我有一个包含大约 5000 万条记录的数据集,其中包含大约 30 个变量(列)。我需要对每个变量的数据集进行排名。
Proc rank 不起作用,因为它需要大量内存来存储这个大型数据集。
要手动给出排名,我必须对相应变量列上的数据集进行排序,然后使用公式给出排名。但问题是我们必须对 30 个变量的数据集进行 30 次排序,这将花费非常长的时间并且不可行。
在这种情况下我们可以使用哪些替代品?
我有一个包含大约 5000 万条记录的数据集,其中包含大约 30 个变量(列)。我需要对每个变量的数据集进行排名。
Proc rank 不起作用,因为它需要大量内存来存储这个大型数据集。
要手动给出排名,我必须对相应变量列上的数据集进行排序,然后使用公式给出排名。但问题是我们必须对 30 个变量的数据集进行 30 次排序,这将花费非常长的时间并且不可行。
在这种情况下我们可以使用哪些替代品?
我不确定这是否是一个好主意:但您可能想要使用 Hash 对象。该对象被加载到您的 RAM 中。假设您有 30 Mio 的数值观测,您将需要大约 (2*8bytes)*50 mio = 800MB 的 RAM——如果我没记错的话。
代码可能如下所示(使用 Foxers 宏循环变量,使用小辅助宏从数据集中获取变量列表,以及使用两个变量的小型测试数据集):
%Macro GetVars(Dset) ;
%Local VarList ;
/* open dataset */
%Let FID = %SysFunc(Open(&Dset)) ;
/* If accessable, process contents of dataset */
%If &FID %Then %Do ;
%Do I=1 %To %SysFunc(ATTRN(&FID,NVARS)) ;
%Let VarList= &VarList %SysFunc(VarName(&FID,&I));
%End ;
/* close dataset when complete */
%Let FID = %SysFunc(Close(&FID)) ;
%End ;
&VarList
%Mend ;
data dsn;
input var1 var2;
datalines;
1 48
1 8
2 5
2 965
3 105
4 105
3 85
;
run;
%MACRO LOOP_OVER(VARS);
%DO I=1 %TO %SYSFUNC(COUNTW(&VARS.));
%LET var = %SCAN(&VARS,&I.);
data out&i.(keep=rank&i.);
if 0 then set dsn;
if _N_ =1 then
do;
dcl hash hh(ordered:'A');
dcl hiter hi('hh');
hh.definekey("&var.");
hh.definedata("&var.","rank&i.");
hh.definedone();
end;
/*Get unique combination variable and point in dataset*/
do while(not last);
set dsn end=last;
hh.ref();
end;
/*Assign ranks within hash object*/
rc=hi.first();
k = 1;
do while(rc=0);
rank&i.=k;
hh.replace();
k+1;
rc=hi.next();
end;
/*Output rank to new dataset in original order of observations*/
do while(not theend);
set dsn end=theend;
hh.find();
output;
end;
/*If data can be sorted according to the rank (with no duplicates) use:
hh.output("out&i.");
&outi. will then have variables &var. and rank&i.
However, the merging below may not be sensible anymore
as correspondence between variables is not preserved.
There will also be no duplicates in the dataset.
*/
run;
%END;
%MEND LOOP_OVER;
%LOOP_OVER(%GetVars(dsn));
/*Merge all rank datasets to one large*/
data all;
merge out:;
run;
你处于一个艰难的境地,没有太多选择。如果您每次都对所有 30 个变量进行排序和保留,这将显着增加您的处理时间。如果我是你,我只会保留你想要排名的变量和一个序列号来应用你的公式,然后在最后将它们合并在一起。这将要求您遍历数据集中的每个变量,然后将它们全部合并在一起。请参阅下面的示例,如果它有助于减少您的处理时间:
** PUT ALL VARIABLES INTO LIST **;
PROC SQL NOPRINT;
SELECT DISTINCT(NAME)
INTO :VARS SEPARATED BY " "
FROM DICTIONARY.COLUMNS
WHERE LIBNAME = 'SASHELP' AND MEMNAME = 'FISH';
QUIT;
%PUT &VARS.;
** CREATE SEQUENCE NUMBER IN FULL DATA **;
DATA FISH; SET SASHELP.FISH;
SEQ=_N_;
RUN;
** LOOP OVER EACH VARIABLE TO ONLY PROCESS THAT VARIABLE AND SEQUENCE -- REDUCES PROCESSING TIME **;
%MACRO LOOP_OVER(VARS);
%DO I=1 %TO %SYSFUNC(COUNTW(&VARS.));
%LET VAR = %SCAN(&VARS,&I.);
DATA FISH_&I.; SET FISH (KEEP=SEQ &VAR.);
RUN;
/* INSERT YOUR FORMULA CODE HERE ON FISH_&I. DATA (MINE IS AN EXAMPLE) */
PROC SORT DATA = FISH_&I.;
BY &VAR.;
RUN;
DATA FISH1_&I.; SET FISH_&I.;
BY &VAR.;
RANK_&VAR = _N_;
RUN;
/* RESORT FINAL DATA BY SEQUENCE NUMBER VARIABLE */
PROC SORT DATA = FISH1_&I.;
BY SEQ;
RUN;
%END;
%MEND;
%LOOP_OVER(&VARS.);
** MERGE ALL SUBSETS BACK TOGETHER BY THE ORIGINAL SEQUENCE NUMBER **;
DATA FINAL;
MERGE FISH1_:;
BY SEQ;
DROP SEQ;
RUN;
如果您只需要对十分位数/百分位数等进行排名,而不是在所有 50m 行中从 1 到 50m 进行完整排名,那么您应该能够通过使用更少的内存来获得正确答案的非常好的近似值proc summary
,使用qmethod=P2
和指定合适的qmarkers
设置。
这种方法使用 P-squared 算法: http ://www.cs.wustl.edu/~jain/papers/ftp/psqr.pdf