将SAS数据集拆分为并行排列?

时间:2017-11-16 23:13:06

标签: sas sas-ds2

SO question on split a large dataset into smaller one但随着proc ds2的出现,必须有一种方法可以使用线程吗?

我编写了以下数据步骤,将数据集拆分为&chunks.块。我尝试在proc ds中编写相同内容,但它只是失败了。我对proc ds2很陌生,所以对于对数据步骤有很好理解的人来说,一个简单的解释是理想的。

数据步骤代码

%macro output_chunks(in, out, by, chunks);
data %do i = 1 %to &chunks.;
    &out.&i.(compress=char drop = i)
    %end;
;
    set &in.;
    by &by.;
    retain i 0;
    if first.&by. then do;
        i = i + 1;      
        if i = &chunks.+1 then i = 1;
    end;

    %do i = 1 %to &chunks.;
        if i = &i. then do;
            output  &out.&i.;
        end;
    %end;
run;
%mend;

proc ds2 code

proc ds2; 
  thread split/overwrite=yes; 
    method run(); 
      set in_data; 
      thisThread=_threadid_; 
      /* can make below into macro but I can't seem to get it to work */
      if thisThread = 1 then do;
        output ds1;
      end;
      else if thisThread = 2 then do;
        output ds2;
      end;
    end; 
    method term();
      put '**Thread' _threadid_ 'processed'  count 'rows:';
    end;
  endthread; 
  run; 
quit; 

2 个答案:

答案 0 :(得分:2)

所以,从某种意义上说,你是对的,DS / 2在这里可能会有所帮助。但是,我怀疑它有点复杂。

DS / 2将很乐意为数据步骤提供支持,但更具挑战性的是写入几个不同的数据集。这是因为在不使用宏语言的情况下构建输出数据集名称的方法并不是很好的方法,据我所知,它不会很好地与线程一起使用(尽管我是&#39 ; m这里没有专家)。

以下是使用线程的示例:

PROC DS2;

     thread in_thread/overwrite=yes;
     dcl bigint count;
     drop count;
        method init();
           count=0;
        end;
         method run();
             set in_data;
             count+1;
             output;             
         end;
         method term();      
           put 'Thread' _threadid_ ' processed' count 'observations.';
         end;
     endthread;
     run;

     data out_data/overwrite=yes;
         dcl thread in_thread t_in; /* instance of the thread */
         method run();
           set from t_in threads=4;
           output;
         end;
     enddata;
     run;
quit;

但是这只是写出一个数据集,如果你将threads=4更改为1,它实际上不会再花费更长时间。两者都是速度方面的,但实际上比常规数据步骤慢(大约是我的速度的1.8倍)。在访问SAS数据集时,DS / 2使用比基本数据步骤更快,更慢的方法访问数据;当您通过SQL或类似工具在RDBMS中工作时,DS / 2的速度增益真正发挥作用。

然而,没有好的方法可以并行驱动输出。这里的上述版本变成了4个数据集。请注意,实际选择输出的位置在主要的非线程数据步骤中......

PROC DS2;

     thread in_thread/overwrite=yes;
     dcl bigint count;
     dcl bigint thisThread;
     drop count;
        method init();
           count=0;
        end;
         method run();
             set in_data;
             count+1;
             thisThread = _threadid_;
             output;

         end;
         method term();      
           put 'Thread' _threadid_ ' processed' count 'observations.';
         end;
     endthread;
     run;

     data a b c d/overwrite=yes;
         dcl thread in_thread t_in; /* instance of the thread */
         method run();
           set from t_in threads=4;
           select(thisThread);
             when (1) output a;
             when (2) output b;
             when (3) output c;
             when (4) output d;
             otherwise;
           end;
         end;
     enddata;
     run;
quit;

所以它实际上比非线程版本慢得多。糟糕!

真的,你的问题是磁盘i / o是主要问题,而不是CPU。你的CPU在这里几乎没有用。 DS / 2可能能够在某些边缘情况下提供帮助,在这种情况下,您拥有一个允许大量同时写入的非常快的SAN,但最终需要X时间来读取这些百万条记录和相同的X时间来写入一百万条记录,基于你的i / o约束,并且赔率是并行化的,不会有帮助。

哈希表会增加更多我怀疑的东西,当然可以在DS / 2中使用;在OP中为数据步骤版本链接的另一个问题上看到我的新答案。 DS / 2可能不会更快地制作解决方案,更可能更慢;但如果你愿意,你可以在DS / 2中实现大致相同的东西,然后子线程就可以自己输出而不涉及主线程。

如果您在Teradata或其他地方执行此操作,DS / 2会有所帮助,您可以使用SAS的数据库内加速器来执行此代码数据库端。 会让事情变得更有效率。然后你可以使用类似于我上面代码的东西,或者更好的哈希解决方案。

答案 1 :(得分:1)

使用HoH方法拆分数据集的用户定义DS2包的示例,最大的缺点是由于DS2中变量列表的实用性非常有限而无法通过键命名数据集而没有大量的吝啬结果我选择了一个更简单的命名对话:

data cars;
set sashelp.cars;
run;

proc ds2;

package hashSplit / overwrite=yes;

declare package hash  h  ();
declare package hash  hs ();
declare package hiter hi;

/**
  * create a child multidata hash object
  */
private method mHashSub(varlist k, varlist d) returns package hash;
  hs = _new_ [this] hash();
  hs.keys(k);
  hs.data(d);
  hs.multidata('Y');
  hs.defineDone();
  return hs;
end;

/**
  * constructor, create the parent and child hash objects
  */
method hashSplit(varlist k);
  h = _new_ [this] hash();
  h.keys(k);
  h.definedata('hs');
  h.defineDone();
end;

/**
  * adds key values to parent hash, if necessary
  * adds key values and data values to child hash
  * consilidates the FIND, ADD and nested ADD methods
  */
method add(varlist k, varlist d);
  declare double rc;

  rc = h.find();
  if rc ^= 0 then do;
    hs = mHashSub(k, d);
      h.add();
  end;
  hs.add();
end;

/**
  * outputs the child hashes to data sets with a fixed naming convention
  *
  * SAS needs to add more support for using variable lists with functions/methods besides hash
  */
method output();
  declare double rc;
  declare int i;

  hi = _new_ hiter('h');

  rc = hi.first();
  do i = 1 to h.num_items by 1 while (rc = 0);
    hs.output(catx('_', 'hashSplit', i));
      rc = hi.next();
  end;
end;

endpackage;
run;
quit;

/**
  * example of using the hashSplit package
  */
proc ds2;
data _null_;
varlist k [origin];
varlist d [_all_];
declare package hashSplit split(k);

method run();
  set cars;
  split.add(k, d);
end;

method term();
  split.output();
end;
enddata;
run;
quit;