SAS: сумма по подмножеству столбца таблицы (несколько раз) - PullRequest
0 голосов
/ 29 июня 2018

(Я новичок в SAS, и я борюсь с тем, насколько это сложно делать по столбцам, что было бы довольно легко на «нормальном» языке. Пожалуйста, потерпите меня, если это чрезвычайно просто.)

У меня есть таблица со значениями type , a1-a10 и b1-b10 , и я хотел бы найти (для каждого N ) сумма bN для тех строк, где aN положительна. Я могу сделать это по одной переменной за раз, например с чем-то вроде этого:

proc sql;
create table work.test1 as
    select type, b1
    from work.table
    where (a1 >0);
run;

и затем суммировать все эти таблицы, а затем объединить их, но это будет много кода и немного ошибок. Есть ли хороший и компактный способ сделать это?

Редактировать: Я хотел бы получить выходную таблицу со значениями type , sum1-sum10 , где sumN - сумма, описанная выше.

Пример данных:

type | a1 | a2 | ... | b1 | b2 | ...
------------------------------------
 cat   10   14   ...   1     2   ...
 cat   -5    3   ...   1     1   ...
 dog   35   -1   ...   9     3   ...
 dog    9    2   ...  0.5    1   ...

Желаемый вывод:

type | sum1 | sum2 | ...
------------------------
 cat    1      3     ...
 dog   9.5     1     ...

Таким образом, для каждого типа и N суммы те bN , где aN в той же строке положительны.

Ответы [ 4 ]

0 голосов
/ 29 июня 2018

Вот подход proc summary. Это не так прямо, как подход с использованием массива, но его гораздо проще обобщить для другой статистики, которая может вас заинтересовать.

data have; 
input type $ a1   a2   b1  b2  ; 
datalines;
cat    10   14   1    2
cat    -5    3   1    1
dog    35   -1   9    3
dog     9    2   0.5  1
;
run;

/*Create a view of the dataset with suitable weight columns*/
data t_have / view = t_have;
  set have;
  array a[*] a1-a2;
  do i = 1 to dim(a);
    a[i] = a[i] > 0;
  end;
run;

/*Use proc summary to sum across rows*/
proc summary nway data = t_have;
  class type;
  var b1 /weight=a1; /*You could macro-ise this bit to avoid excessive repetition*/
  var b2 /weight=a2;
  output out= want(drop=_:) sum= mean= /autoname;
run;
0 голосов
/ 29 июня 2018

Вы можете использовать массивы для выполнения этой задачи за один шаг с одним выходом. Массив хранит значения по столбцам в памяти, которые затем можно циклически выполнять для выполнения расчетов.

В этом коде я создал 3 массива, один для A1-A2, один для B1-B2 и один для новых переменных SUM1-Sum2. Очевидно, что в ваших реальных данных вы изменили бы диапазон на A10, B10, SUM10.

Я добавил множество комментариев, чтобы описать, что делает код, но я также рекомендую прочитать массивы, чтобы лучше понять.

/* create input data */
data have;
input type $ a1 a2 b1 b2;
datalines;
cat . 14 1 2
cat -5 3 1 1
dog 35 -1 9 3
dog 9 2 0.5 1
;
run;

/* sort data by type (needed for next step) */
proc sort data=have;
by type;
run;

data want;
set have;
by type; /* data neds to be sorted by this */
array var_a{2} a1-a2; /* store the values of a in an array */
array var_b{2} b1-b2; /* store the values of b in an array */
array sumvar{2} sum1-sum2; /* set up an array of sum variables (will also create physical variables) */
if first.type then do; /* set sum variables to zero when type changes */
    do i = 1 to dim(sumvar);
        sumvar{i} = 0;
    end;
end;
do j=1 to dim(var_a); /* loop through each var_a value and add var_b to sum_N if var_a>0 */
    if var_a{j}>0 then sumvar{j}+var_b{j}; /* syntax var1 + var2 retains value across rows */
end;
keep type sum: ; /* only keep required variables */
if last.type then output; /* only output last record for each type, with the total sum */
run;
0 голосов
/ 29 июня 2018

Вы можете сделать это в одном SQL select с одним предложением case для каждой переменной.

Данные

data have; input 
type $ a1   a2   b1  b2  ; datalines;
cat    10   14   1    2
cat    -5    3   1    1
dog    35   -1   9    3
dog     9    2   0.5  1
run;

Пример SQL

proc sql;
  create table want_way1
  as select 
    type
  , sum (case when a1 > 0 then b1 else 0 end) as sum1
  , sum (case when a2 > 0 then b2 else 0 end) as sum2
  from have
  group by type
  ;

В SQL нет массивов, поэтому будет написан макрос для генерации либо всего SQL, либо только необходимых предложений для каждой из пар переменных N a и b. Макрос может проверить метаданные данных, если вы хотите, чтобы макрос обнаружил N сам.

ДАННЫЕ Пример шага

Использование цикла DOW для групповой обработки и массивов для обработки предметов. Довольно компактно, если вы удалите комментарии.

data want_way2 (keep=type sum:);
  do until (last.type);
    set have;
    by type;

    * array statement is non-executable, but associates PDV variables with the array reference;
    * array statement will create new variables in PDV if needed;

    array a a1-a2;      /* connects existing variables with array */
    array b b1-b2;      /* connects existing variables with array */
    array s sum1-sum2;  /* creates new variables and connects them with array */

    * repurpose _n_ as simply an automatic variable that does not need to be dropped;
    do _n_ = 1 to dim(a);
      s(_n_) = sum ( s(_n_) , ifn ( a(_n_) > 0, b(_n_), 0 ) );
    end;
  end;
run;
0 голосов
/ 29 июня 2018

Если я понимаю, что вы хотите, то я думаю, что помещение вашего кода в макрос поможет. Например, в приведенном ниже примере я использую цикл %do для генерации 10 различных наборов данных, по одному для каждого N. Все, что я сделал, это обернул ваш код в макрос, так что я надеюсь, что ваш код уже делает то, что вы хотите. И я заменил ваш run на quit, иначе proc sql не остановится.

Edit:

options symbolgen mprint mlogic;

%macro Y(N=);

  %macro compute;
  %do i = 1 %to &N.;
    proc sql;
      create table work.test&i. as
        select type, sum(b&i.) as sum&i.
        from work.table
        where (a&i. >0)
        group by type
        order by type;
      quit;
   %end;
  %mend;

   %compute;

      data want;
      %do i = 1 %to &N.;
        merge test&i.;
      %end;
      run;
 %mend;

%Y(N=10);
...