通过SAS中的通用后缀对列求和

时间:2019-05-10 07:38:33

标签: function sas sum

我有一系列标题为Income_2015Interest 2015Dividends 2015Income_2018Interest 2018Dividends 2018等的列,并希望创建每年的总列数。

是否有一种方法可以在SAS中执行此操作而不必单独列出总和中的所有列

我目前使用以下内容按类型获取总计:

data keep;
set from;

income_total = sum(of Income_:);
run;

除非使用以下方法,否则找不到按年总计的方法:

year_2015_total = sum(Income_2015, Interest_2015, Dividends_2015)

... year_2018_total = sum(Income_2018, Interest_2018, Dividends_2018)

我很高兴可以完成上述操作,但这只是我需要包括的几列示例。实际的列表要长得多

3 个答案:

答案 0 :(得分:0)

没有一种通过后缀定义变量列表的简单/自动方法。也就是说,没有任何直接等效于sum (of :2018)的东西。

也就是说,您可以选择。一种是使用数组。它们使您可以按数组名称引用变量列表。

另一种是使用宏语言。这样,您就可以构建一个宏变量,该宏变量包含具有特定后缀的所有变量名的列表。

但是我认为最好的解决方案是重组数据。

如果您的数据现在看起来像:

Person Income_2015 Interest_2015 Income_2016 Interest_2016
1      100,000     1,000         200,000     2,000
2      500,000     5,000         600,000     6,000

将其移至:

Person Year        Income      Interest
1      2015        100,000     1,000
1      2016        200,000     2,000
2      2015        500,000     5,000
2      2016        600,000     6,000

这种垂直结构使处理数据变得更加容易,并且您可以轻松地跨年(列)或收入来源(行)进行汇总。关键是年份实际上是一个数据值。因此,垂直(“规范化”)结构通过将Year作为值存储在变量中而不是将Year作为变量名称的一部分来存储,而使您受益。

答案 1 :(得分:0)

您可以尝试使用宏变量,例如:

proc sql;
    select name into:list_2015 separated by ',' from dictionary.columns where libname='YOURLIB' and memname='YOURDATA' and name contains '2015';
quit;

data want;
  set have;
  sum_2015=sum(of &list_2015);
run;

答案 2 :(得分:0)

不幸的是,您的数据设计已将数据元素存储为元数据的一部分(变量名的一部分),并且您获得的数据集越来越大。

分类形式适用于汇总汇总,并使用诸如TABULATEREPORT之类的报告过程来生成数据的“广泛”视图。

id topic     year  amount
a  income    2015  
a  interest  2015  
a  dividends 2015
a  losses    2015
b  income    2015  
b  interest  2015  
b  dividends 2015
                    … no losses row for id=b corresponds to 'missing' value in wide form
c  income    2015  
c  interest  2015  
                    … c had no dividends, no losses, no fubars

保持结构不变(有时候,就像跨60年的一千万个帐户的大型机数据一样),您需要对数据集进行预处理,以识别包含年份和值等年份的列后续步骤代码中的信息,其中包含代码生成(即宏),该代码生成涵盖了列名中的年份。例如:

%macro make_data(data=);
  %local year category index varname;

  data &data;
    do accountid = 1 to 20;
      %do index = 65 %to 90;
      %do year=1995 %to 2019;
        %let varname = %sysfunc(byte(&index))_&year;
        amount+1;
        &varname = amount;
      %end;
      %end;
      output;
    end;
    drop amount;
  run;
%mend;

%macro generate_year_total_code(data=);
  proc contents noprint data=&data out=havemeta(keep=name);
  run;

  data havemeta2;
    set havemeta;
    if prxmatch ("/[^0-9]\d{4}/", trim(name));
    year = input(substr(name,length(name)-3), 4.);
  run;

  proc sort data=havemeta2;
    by year;
  run;

  data _null_;
    length varlist $32000;

    do until (last.year);
      set havemeta2;
      by year;

      varlist = catx(',', varlist, name);
    end;

    call symputx('year_count', _n_);
    call symput ( cats('total_statement_', _n_)
                , cats('total_',year,'=sum(',varlist,')'));
  run;
%mend;

%macro totals(data=);

  %generate_year_total_code(data=&data);

  %local index; 

  data &data;
    set &data;
    %do index = 1 %to &year_count;
      &&total_statement_&index;
    %end;
  run;

%mend;

%make_data(data=have);

options mprint;
%totals(data=have);

另一种方法是将数据转换为分类形式,以便提取年份值以进行适当汇总或用于分类报告中。例如:

proc transpose data=have out=haveTall;
  by accountid;
run;

data haveTall;
    set haveTall;
    if prxmatch ("/_\d{4}$/", trim(_name_));  * data value in col1 is from a year suffixed variable name;
    year = input(substr(_name_,length(_name_)-3), 4.);
    category = substr(_name_,1, length(_name_)-5);
    amount = col1;
    drop _name_ col1;
run;

proc tabulate data=haveTall;
  class accountid category year;
  var amount;
  table 
    accountid * category
    , 
    year=''*amount=''*sum=''
    /
    nocellmerge
    ;
run;

第三种更复杂但简洁的方式。使用哈希对象动态计算总计,输出总计并合并回原始数据。与方法#1相比,这需要更多的CPU,因为PDV变量名称是在每一行中求值的。

data _null_;
  if 0 then set have(keep=accountid);

  length year amount 8;
  declare hash totals(ordered:'a');
  totals.defineKey('accountid', 'year');
  totals.defineData('accountid', 'year', 'total');
  totals.defineDone();
  call missing (year, amount, accountid);

  do until (end);
    set have end=end;
    array numbers _numeric_;

    do over numbers;
      length name $32;
      name = vname(numbers);
      if prxmatch ("/_\d{4}$/", trim(name)) then do;
        year = input(substr(name,length(name)-3), 4.);
        if totals.find() = 0
          then total + numbers;
          else total = numbers;
        totals.replace();
      end;
    end;
  end;

  totals.output(dataset:'totals');

  stop;
run;

proc transpose data=totals prefix=total_ out=totals_across_year(drop=_name_);
  by accountid;
  var total;
  id year;
run;