Ваш дизайн данных, к сожалению, сохранил элемент данных как часть метаданных (часть имени (имен) переменных), и вы получаете набор данных, который становится широким.
Категориальная форма подходит для броскаагрегирование и использование процедур отчетности, таких как TABULATE
или REPORT
, для генерации «широкого» представления данных.
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, поскольку имена переменных 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;