使用主键按列拆分 SAS 数据集

Split SAS datasets by column with primary key

所以我有一个包含一个主键的数据集:unique_id 和 1200 个变量。此数据集是从宏生成的,因此列数不会固定。我需要将此数据集拆分为 4 个或更多数据集,每个数据集包含 250 个变量,并且每个较小的数据集都应包含主键,以便我稍后可以将它们合并回来。有人可以帮我用 sas 函数或宏来解决这个问题吗? 提前致谢。

按您请求的方式拆分数据集的一种简单方法是对多个输出数据集使用单个数据步骤,其中每个输出数据集都有一个 KEEP= 数据集选项,列出要保留的变量。例如:

data split1(keep=Name Age Height) split2(keep=Name Sex Weight);
  set sashelp.class;
run;

因此您需要获取变量列表,然后将其分组为 250 个或更少的变量组。然后你可以使用这些分组来生成上面的代码。这是一种使用 PROC CONTENTS 获取变量列表并使用 CALL EXECUTE() 生成代码的方法。

我将使用宏变量来保存输入数据集的名称、每个数据集上需要保留的关键变量以及每个数据集中要保留的最大变量数。

因此对于上面的示例,这些宏变量值将是:

%let ds=sashelp.class;
%let key=name;
%let nvars=2;

因此使用 PROC CONTENTS 获取变量名列表:

proc contents data=&ds noprint out=contents; run;

现在 运行 一个数据步骤,将它们分成组并生成一个成员名称以用于新的拆分数据集。确保在计数时不要将 KEY 变量包含在变量列表中。

data groups;
  length group 8 memname  varnum 8 name  ;
  group +1;
  memname=cats('split',group);
  do varnum=1 to &nvars while (not eof);
    set contents(keep=name where=(upcase(name) ne %upcase("&key"))) end=eof;
    output;
  end;
run;

现在您可以使用该数据集来驱动代码的生成:

data _null_;
  set groups end=eof;
  by group;
  if _n_=1 then call execute('data ');
  if first.group then call execute(cats(memname,'(keep=&key'));
  call execute(' '||trim(name));
  if last.group then call execute(') ');
  if eof then call execute(';set &ds;run;');
run;

以下是 SAS 日志的结果:

NOTE: CALL EXECUTE generated line.
1    + data
2    + split1(keep=name
3    +  Age
4    +  Height
5    + )
6    + split2(keep=name
7    +  Sex
8    +  Weight
9    + )
10   + ;set sashelp.class;run;

NOTE: There were 19 observations read from the data set SASHELP.CLASS.
NOTE: The data set WORK.SPLIT1 has 19 observations and 3 variables.
NOTE: The data set WORK.SPLIT2 has 19 observations and 3 variables.

另一种使用宏变量的方法:

/* Number of columns you want in each chunk */

%let vars_per_part = 250;

/* Get all the column names into a dataset */

proc contents data = have out=cols noprint;
run;

%macro split(part);

  /* Split the columns into 250 chunks for each part and put it into a macro variable */

  %let fobs = %eval((&part - 1)* &vars_per_part + 1);
  %let obs  = %eval(&part * &vars_per_part);
  proc sql noprint;
    select name into :cols separated by " " from cols (firstobs =  &fobs obs = &obs) where name ~= "uniq_id";
    quit;

  /* Chunk up the data only keeping those varaibles and the uniq_id */
  data want_part∂
    set have (keep = &cols uniq_id);
  run;

%mend;

/* Run this from 1 to whatever the increment required to cover all the columnns */

%split(1);
%split(2);
%split(3);

这不是一个完整的解决方案,但可以帮助您从另一个角度了解如何解决这个问题。以前的解决方案很大程度上依赖于 proc 内容和数据步骤,但我会使用 proc sqldictionary.columns[=23= 来解决这个问题].我会创建一个宏,根据需要将原始文件分成多个部分,每个部分 250 列。大致步骤:

proc sql; create table as _colstemp as select * from dictionary.columns where library='your library' and memname = 'your table' and name ne 'your primary key'; quit;

计算某处所需的文件数:

proc sql; 
    select ceil(count(*)/249) into :num_of_datasets from _colstemp; 
    select count(*) into :num_of_cols from _colstemp; 
quit;

然后像这样遍历原始数据集:

%do &_i = 1 %to &num_of_datasets

proc sql; 
    select name into :vars separated by ',' 
    from _colstemp(firstobs=%eval((&_i. - 1)*249 + 1) obs = %eval(min(249,&num_of_cols. - &_i. * 249)) ;
quit;

proc sql; 
    create table split_&_i. as
    select YOUR_PRIMARY_KEY, &vars from YOUR_ORIGINAL_TABLE;
quit;
%end;

希望这能给你另一个想法。该解决方案未经测试,可能包含一些伪代码元素,因为它是根据我做事的记忆编写的。此外,这没有宏声明,而且可以做很多参数化。这将使解决方案更通用(例如,参数化每个数据集的变量数量、主键名称和数据集名称。