【问题标题】:Split large SAS dataset into smaller datasets将大型 SAS 数据集拆分为较小的数据集
【发布时间】:2014-02-21 18:46:12
【问题描述】:

在将大型 SAS 数据集拆分为较小的数据集时,我需要一些帮助。

每个月我都会有一个包含几百万条记录的数据集。这个数字会因月而异。我需要将此数据集拆分为多个较小的数据集,每个数据集包含 250,000 条记录。例如,如果我在原始数据集中有 1,050,000 条记录,那么我需要最终结果是 4 个包含 250,000 条记录的数据集和 1 个包含 50,000 条记录的数据集。

从我一直在看的情况来看,这似乎需要使用宏。不幸的是,我对 SAS 相当陌生(不熟悉使用宏)并且没有太多时间来完成这项工作。任何帮助将不胜感激。

【问题讨论】:

    标签: macros split sas


    【解决方案1】:

    这是一个基本的方法。这需要手动调整间隔,但很容易理解。

    * split data;
    data output1;
    set df;
    if 1 <= _N_ < 5 then output;
    run;
    
    
    data output2;
    set df;
    if 5 <= _N_ < 10 then output;
    run;
    
    
    data output3;
    set df;
    if 10 <= _N_ < 15 then output;
    run;
    
    
    data output4;
    set df;
    if 15 <= _N_ < 22 then output;
    run;
    

    【讨论】:

      【解决方案2】:

      在乔的回答的基础上,也许你可以尝试这样的事情:

      %MACRO SPLIT(DATASET);
      
      %LET DATASET_ID = %SYSFUNC(OPEN(&DATASET.));
      %LET NOBS = %SYSFUNC(ATTRN(&DATASET__ID., NLOBS));
      %LET NB_DATASETS = %SYSEVALF(&NOBS. / 250000, CEIL);
      
      DATA 
        %DO I=1 %TO &NB_DATASETS.;
          WANT&I. 
        %END;;
      
        SET WANT;
      
        %DO I=1 %TO &NB_DATASETS.;
      
          %IF &I. > 1 %THEN %DO; ELSE %END; IF _N_ LE 2.5E5 * &I. THEN OUTPUT WANT&I.;
      
        %END;
      RUN;
      %MEND SPLIT;    
      

      【讨论】:

      • 这与我最终所做的非常相似。谢谢。
      • 由于我的编辑因过多更改答案而未获批准,因此我想在此指出需要进行一些小的改进/更正:1)还要关闭数据集以防止它稍后锁定。 2)设置旺旺;应该是 SET &DATASET; 3)(很高兴)每个数据集的 obs 数量和这些新数据集的名称都可以是附加的宏参数。
      【解决方案3】:

      如果您在内存中有空间来存储 一个 较小的数据集,那么一个更有效的选择是哈希解决方案。这是一个基本上使用您在问题中描述的示例:

      data in_data;
        do recid = 1 to 1.000001e7;
          datavar = 1;
          output;
        end;
      run;
      
      
      data _null_;
        if 0 then set in_data;
        declare hash h_out();
        h_out.defineKey('_n_');
        h_out.defineData('recid','datavar');
        h_out.defineDone();
      
        do filenum = 1 by 1 until (eof);
          do _n_ = 1 to 250000 until (eof);
            set in_data end=eof;
            h_out.add();
          end;
          h_out.output(dataset:cats('file_',filenum));
          h_out.clear();
        end;
        stop;
      run;
      

      我们用适当的参数定义一个哈希对象,并简单地告诉它每 250k 条记录输出一次,并清除它。我们也可以在这里做一个散列哈希,特别是如果它不仅仅是“每 250k 条记录”,而是其他一些标准推动了事情,但是你必须适应 所有 的记录在内存中,不仅仅是 250k。

      另请注意,我们可以在不明确指定变量的情况下执行此操作,但它需要在数据集上有一个有用的 ID:

      data _null_;
        if 0 then set in_data;
        declare hash h_out(dataset:'in_data(obs=0)');
        h_out.defineKey('recid');
        h_out.defineData(all:'y');
        h_out.defineDone();
      
        do filenum = 1 by 1 until (eof);
          do _n_ = 1 to 250000 until (eof);
            set in_data end=eof;
            h_out.add();
          end;
          h_out.output(dataset:cats('file_',filenum));
          h_out.clear();
        end;
        stop;
      run;
      

      由于在构造函数上使用了dataset 选项(all:'y' 功能所必需的),我们不能再将_n_ 用作哈希ID,因此我们必须有一个记录ID。希望有这样一个变量,或者可以添加一个视图。

      【讨论】:

        【解决方案4】:

        Google 上的第一个结果来自 SAS User Group International (SUGI) 这些人是你的朋友。

        文章在这里: http://www2.sas.com/proceedings/sugi27/p083-27.pdf

        代码是:

        %macro split(ndsn=2); 
        data %do i = 1 %to &ndsn.; dsn&i. %end; ; 
         retain x; 
         set orig nobs=nobs; 
         if _n_ eq 1 
         then do; 
         if mod(nobs,&ndsn.) eq 0 
         then x=int(nobs/&ndsn.); 
         else x=int(nobs/&ndsn.)+1; 
         end; 
         if _n_ le x then output dsn1; 
         %do i = 2 %to &ndsn.; 
         else if _n_ le (&i.*x) 
         then output dsn&i.; 
         %end; 
         run; 
        %mend split; 
        
        %split(ndsn=10);
        

        您需要做的就是替换“%split(ndsn=10);”中的 10 位数字与您需要的号码。 在第 4 行“set orig nobs=nobs;”中,只需将 orig 替换为您的数据集名称即可。

        你好!

        【讨论】:

          【解决方案5】:

          您可以在没有宏的情况下使用 CALL EXECUTE() 来执行此操作。它将 SAS 代码创建为文本字符串,然后在您的“手动编写”代码完成后执行它。

          data _null_;
              if 0 then set have nobs=n;
              do i=1 to ceil(n/250000);
                  call execute (cats("data want",i)||";");
                  call execute ("set have(firstobs="||(i-1)*250000+1||" obs="||i*250000||");");
                  call execute ("run;");
              end;
          run;
          

          【讨论】:

            【解决方案6】:

            如果您不介意询问可能不存在的数据集,并且对事物有合理的限制,那么您完全可以不使用宏。

            data want1 want2 want3 want4 want5 want6 want7 want8 want9;
            if _n_ le 2.5e5 then output want1;
            else if _n_ le 5e5 then output want2;
            else if _n_ le 7.5e5 then output want3;
            ... etc....
            run;
            

            宏会使编程更高效,阅读更清晰,但不会改变它在现实中的实际运行方式。

            【讨论】:

            • 没错,我可以这样做。不幸的是,我不知道要考虑多少记录...可能是 100 万或 1000 万或其他完全不同的东西。
            猜你喜欢
            • 1970-01-01
            • 2021-03-28
            • 2019-03-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-03-07
            • 2020-01-31
            相关资源
            最近更新 更多