如何在大型数据集中对多个变量进行排名?

时间:2016-10-27 06:53:22

标签: sorting sas rank proc ranking-functions

我有大约5000万条记录的数据集,大约有30个变量(列)。 我需要为每个变量排序数据集。

Proc rank不起作用,因为它需要大量内存用于这个大型数据集。

要手动给出排名,我必须在相应的变量列上对数据集进行排序,然后使用公式给出排名。但问题是我们必须在30个变量上对数据集进行30次排序,这将花费很长时间并且不可行。

在这种情况下,我们可以使用哪些替代品?

3 个答案:

答案 0 :(得分:0)

你处在一个没有多少选择的困境中。如果您每次都要排序并保留所有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;

答案 1 :(得分:0)

如果您只需要在所有50米行中排名为十分位数/百分位数等而不是从1到50米的完整排名,那么您应该能够通过更少的内存量获得正确答案的非常好的近似值proc summary,使用qmethod=P2并指定合适的qmarkers设置。

这种方法使用P平方算法: http://www.cs.wustl.edu/~jain/papers/ftp/psqr.pdf

答案 2 :(得分:0)

我不确定,这是否是一个好主意:但您可能想要使用Hash对象。该对象被加载到您的RAM中。假设你有30 Mio的数值观测值,你将需要大约(2 * 8bytes)* 50 mio = 800MB的RAM - 如果我没有记错的话。

代码看起来像这样(使用Foxers Macro循环遍历变量,使用一个小帮助宏来获取数据集中的变量列表和带有两个变量的小测试数据集):

%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;