我需要SAS的大师的建议:) 假设我有两个大数据集。第一个是庞大的数据集(约50-100Gb!),其中包含电话号码。第二个包含前缀(20-40万个观察值)。 我需要为每个电话号码的第一个表添加最合适的前缀。
例如,如果我有一个电话号码+71230000
和前缀
+7
+71230
+7123
最合适的前缀是+71230
。
我的想法。首先,对前缀表进行排序。然后在数据步骤中,处理电话号码表
data OutputTable;
set PhoneNumbersTable end=_last;
if _N_ = 1 then do;
dsid = open('PrefixTable');
end;
/* for each observation in PhoneNumbersTable:
1. Take the first digit of phone number (`+7`).
Look it up in PrefixTable. Store a number of observation of
this prefix (`n_obs`).
2. Take the first TWO digits of the phone number (`+71`).
Look it up in PrefixTable, starting with `n_obs + 1` observation.
Stop when we will find this prefix
(then store a number of observation of this prefix) or
when the first digit will change (then previous one was the
most appropriate prefix).
etc....
*/
if _last then do;
rc = close(dsid);
end;
run;
我希望我的想法足够清楚,但如果不是,我很抱歉。
那你有什么建议? 谢谢你的帮助。
P.S。当然,第一个表中的电话号码不是唯一的(可能会重复),遗憾的是,我的算法不使用它。
答案 0 :(得分:2)
有两种方法可以做到这一点,你可以使用格式或哈希表。
使用格式的示例:
/* Build a simple format of all prefixes, and determine max prefix length */ data prefix_fmt ; set prefixtable end=eof ; retain fmtname 'PREFIX' type 'C' maxlen . ; maxlen = max(maxlen,length(prefix)) ; /* Store maximum prefix length */ start = prefix ; label = 'Y' ; output ; if eof then do ; hlo = 'O' ; label = 'N' ; output ; call symputx('MAXPL',maxlen) ; end ; drop maxlen ; run ; proc format cntlin=prefix_fmt ; run ; /* For each phone number, start with full number and reduce by 1 digit until prefix match found */ /* For efficiency, initially reduce phone number to length of max prefix */ data match_prefix ; set phonenumberstable ; length prefix $&MAXPL.. ; prefix = '' ; pnum = substr(phonenumber,1,&MAXPL) ; do until (not missing(prefix) or length(pnum) = 1) ; if put(pnum,$PREFIX.) = 'Y' then prefix = pnum ; pnum = substr(pnum,1,length(pnum)-1) ; /* Drop last digit */ end ; drop pnum ; run ;
答案 1 :(得分:2)
这是另一种解决方案,只要您可以在一个主要(可能是好的)限制下工作,那么非常,速度方面很快:电话号码不能从一个0,并且必须是数字或可转换为数字(即,#34; +"不需要查找)。
我正在做的是构建一个1 / null标志数组,每个可能的前缀一个1 / null标志。除此之外不能使用前导0:因为' 9512'和' 09512'是相同的数字。这可以解决 - 添加' 1'在开始时(所以如果你有可能的6位前缀,那么一切都是1000000 +前缀)例如可以工作 - 但它需要调整下面(并可能有性能影响,虽然我认为它不会那么糟糕)。如果" +"也需要,可能需要转换为数字;在这里你可以用" +"获得2000000添加到开头,或类似的东西。
好消息是,每行最多只需要6个查询(或左右) - 比任何其他搜索选项快得多(因为临时数组是连续的内存块,它是' s只需"去检查预先计算的6个内存地址")。哈希和格式将是一个相当大的块,因为他们必须重新查找每一个。
一个主要的性能建议:注意你的前缀可能无法匹配的方式。检查6然后5然后4然后......可能会更快,或检查1然后2然后3然后......可能会更快。这一切都取决于实际的前缀本身和实际的电话号码。如果您的大多数前缀都是" + 11"这样的事情,你几乎肯定想从左边开始,如果那个和#34; 94"很快就会发现不匹配。
用这个,解决方案。
data prefix_match;
if _n_=1 then do;
array prefixes[1000000] _temporary_;
do _i = 1 to nobs_prefix;
set prefixes point=_i nobs=nobs_prefix;
prefixes[prefix]=1;
end;
call missing(prefix);
end;
set phone_numbers;
do _j = 6 to 1 by -1;
prefix = input(substr(phone_no,1,_j),6.);
if prefix ne 0 and prefixes[prefix]=1 then leave;
prefix=.;
end;
drop _:;
run;
对于一个拥有40k前缀和100m电话号码(并且没有其他变量)的测试装置,这在我的(好)笔记本电脑上运行了1分多钟,而不是6,并且使用格式解决方案进行更改4并更改使用哈希解决方案(将其修改为输出所有行,因为其他两个解决方案都可以)。这对我来说似乎是正确的。
答案 2 :(得分:0)
这是一个哈希表示例。
生成一些虚拟数据。
data phone_numbers(keep=phone)
prefixes(keep=prefix);
;
length phone $10 prefix $4;
do i=1 to 10000000;
phone = cats(int(ranuni(0) * 9999999999 + 1));
len = int(ranuni(0) * 4 + 1);
prefix = substr(phone,1,len);
if input(phone,best.) ge 1000000000 then do;
output;
end;
end;
run;
假设最长的前缀是4个字符,请尝试找到最长的匹配,然后继续,直到尝试了最短的前缀。如果找到匹配项,则输出记录并继续进行下一次观察。
data ht;
attrib prefix length=$4;
set phone_numbers;
if _n_ eq 1 then do;
declare hash ht(dataset:"prefixes");
ht.defineKey('prefix');
ht.defineDone();
end;
do len=4 to 1 by -1;
prefix = substr(phone,1,len);
if ht.find() eq 0 then do;
output;
leave;
end;
end;
drop len;
run;
如果找不到匹配项来输出记录并将前缀字段留空,可能需要添加逻辑吗?不确定你想如何处理这种情况。