Предположим, что имена переменных abc_*
изменены на flag_*
.
Два подхода к данным:
Использование массивов для организации segment
и flag
Переменные и сравнить flag(<i>)
с flag(<i+1>)
в al oop, чтобы обнаружить каждый случай перехода флагов от месяца к месяцу от 0 до 1. При обработке сигналов это нарастающий фронт и предлагает второй путь.
Используйте много операторов ( сгенерированных макросов ) с тестовыми выражениями битовой маски, чтобы найти передний фронт в каждом месте, где может возникнуть 01, когда флаги месяца организованы как набор битов.
В обоих случаях я бы использовал HASH
для накопления различных комбинаций:
account
(сколько бы их ни было), segment
(сколько их существует) и position
( месяц 1 до 83 ).
и последующая обработка в получить число account
за segment
& position
Примечание: наихудший сценарий 41 нарастающих фронтов в каждой строке, вместе взятых при подходе, который не проверяет повторы, выходная таблица для постобработки приблизится к 1/2 размера выходной таблицы TRANSPOSE
. И это может быть довольно большим.
Время выполнения кода, вероятно, будет довольно большим из-за 83 попарных «проверок» на строку и необходимого количества операций ввода-вывода, сохраняющих данные из случаев нарастающего фронта. .
Примеры:
* Имитировать некоторые данные, значения сегментов нумеруются, например, c код;
%* These parameters create 128K rows;
%let N_ACCOUNTS = 1500;
%let N_DATES = 208;
%let N_SEGMENT_TYPES = 1000;
%let N_MONTHS = 84;
data have(keep=account_id date segment_: flag_: seek);
call streaminit(&SEED);
do account_id = 1 to 600;
date = '08JAN2015'd - 7;
do dix = 1 to &N_DATES; %* once a week, so &N_DATES weeks;
date + 7;
%* _flags is appropriate for up to &N_MONTHS = 128 ;
_flags = put(rand('uniform',2**32), binary32.)
|| put(rand('uniform',2**32), binary32.)
|| put(rand('uniform',2**32), binary32.)
|| put(rand('uniform',2**32), binary32.)
;
%* NOTE: edges are in this random data and will need to be counted;
array segment segment_1-segment_&N_MONTHS;
array flag_(&N_MONTHS);
do over segment;
%* guarded simulation of a 1 of &N_SEGMENT_TYPES segment value;
do seek = 1 to 1e5 until (1 <= segment <= &N_SEGMENT_TYPES);
segment = ceil(rand('WEIBULL', 1.15, &N_SEGMENT_TYPES / 5));
end;
flag_[_i_] = substr(_flags,_i_,1) = '1';
end;
OUTPUT;
end;
end;
format flag_: 1. date yymmdd10.;
run;
* Путь 1 ;
* Используйте массивы для определения положения нарастающего фронта в каждой позиции и HA SH для хранения уникальных комбинаций;
data _null_;
length segment account_id position 8;
call missing (account_id, segment, position);
declare hash combination(ordered:'A');
combination.defineKey('account_id', 'segment', 'position');
combination.defineDone();
do until (last_row);
set have end=last_row;
array segments segment_:;
array flags flag_:;
do position = 1 to dim(flags) - 1;
if flags(position) = 0 and flags(position+1) = 1 then do;
segment = segments(position);
%PUT always replace;
combination.replace();
end;
end;
end;
combination.output(dataset:'combinations_array_way');
stop;
run;
* Постобработка для получения количества комбинаций и транспонирования ширины;
* Данные класса EACH_POSITION
предполагает отсутствие пропусков account_id
в исходных данных;
* data for a dummy account will ensure all 'positions' (months) are present in the desired order;
* the positions 1..N become column name suffixes in transposed data;
data each_position;
retain account_id segment .;
do position = 1 to &N_MONTHS-1;
output;
end;
run;
proc sql;
create table summary_array_way as
select segment, position, count(*) as count
from
( select * from combinations_array_way union all corresponding
select * from each_position
)
group by segment, position;
quit;
proc transpose data=summary_array_way out=want_array_way(drop=_name_ where=(not missing(segment))) prefix=count_;
by segment;
id position;
var count;
format count 6.;
run;
* Способ 2 (новый) ;
* Использовать макрос, сгенерированный, если операторы используют бит маскирует тесты для определения положения нарастающего фронта в каждой позиции и HA SH для хранения уникальных комбинаций;
data _null_;
length segment account_id position 8;
call missing (account_id, segment, position);
%* track existance of a combination;
%* code will add a hash key only at first occurrence of combination,
%* thus ensuring unique account_ids over segment and position;
declare hash combination(ordered:'A');
combination.defineKey('account_id', 'segment', 'position');
combination.defineDone();
if 0 then set have; * prep PDV for arrays, and N_MONTHS verification;
array segments segment_:;
array flags flag_:;
_n_ = dim(flags);
if &N_MONTHS ne dim(flags) then do;
put "ERROR: Macro symbol N_MONTHS=&N_MONTHS does not agree with the number of flag variables (" _n_ +(-1) ")";
abort cancel;
end;
do until (last_row);
set have end=last_row;
%macro test_01(var,places,offset);
%* code gen for if statements that use bit mask expressions;
%local index mask;
%do index = 1 %to &places;
%let mask = %sysfunc(repeat(.,&index-1))01%sysfunc(repeat(.,&places));
%let mask = %substr(&mask,2,&places+1);
%* emit statement for mask test and hash update;
if &var = "&mask."B then
rc = combination.replace(
key: account_id, key: segment_%eval(&offset+&index), key: %eval(&index+&offset),
data:account_id, data:segment_%eval(&offset+&index), data: %eval(&index+&offset)
);
%end;
%mend;
options mprint nosymbolgen nomlogic;
%macro test_flag_chunks();
%* chunk concatenation of flag variables for bit mask evaluation;
%local count L R B;
%let L = 1;
%let R = %sysfunc(MIN(64,&N_MONTHS));
%do %while (&L < &N_MONTHS);
%let B = %eval (&R - &L + 1); %* number of 0/1 flag variables to process as bits;
%* concatenate up to 64 0/1 flag values to be a bit representation of a 8 byte character value;
chunk = input ( cats ( of flag_&L - flag_&R ), $binary64. );
%* code generate B if statements that utilize bit mask expressions
% for testing for 01 at each position;
%test_01 (chunk, %eval(&B-1), %eval(&L-1))
%let L = %eval(&L + 63);
%let R = %eval(&R + 63);
%if &R > &N_MONTHS %then %let R = &N_MONTHS;
%end;
%mend;
%test_flag_chunks
*;
%* Example of the code gen for 84 month use case;
%* -------------------------------------------------;
%* chunk = input(cats(of flag1-flag64),$binary64.);
%* %test_01(chunk,63,0)
%* chunk = input(cats(of flag64-flag84),$binary64.);
%* %test_01(chunk,20,63)
%* -------------------------------------------------;
end;
combination.output(dataset:'combinations');
stop;
run;
/ * Частичный журнал кода gen
MPRINT(TEST_FLAG_CHUNKS): chunk = input ( cats ( of flag_64 - flag_84 ), $binary64. );
MPRINT(TEST_01): if chunk = "01..................."B then rc = combination.replace( key: account_id, key: segment_64, key: 64, data:account_id, data:segment_64, data: 64 );
MPRINT(TEST_01): if chunk = ".01.................."B then rc = combination.replace( key: account_id, key: segment_65, key: 65, data:account_id, data:segment_65, data: 65 );
MPRINT(TEST_01): if chunk = "..01................."B then rc = combination.replace( key: account_id, key: segment_66, key: 66, data:account_id, data:segment_66, data: 66 );
MPRINT(TEST_01): if chunk = "...01................"B then rc = combination.replace( key: account_id, key: segment_67, key: 67, data:account_id, data:segment_67, data: 67 );
MPRINT(TEST_01): if chunk = "....01..............."B then rc = combination.replace( key: account_id, key: segment_68, key: 68, data:account_id, data:segment_68, data: 68 );
MPRINT(TEST_01): if chunk = ".....01.............."B then rc = combination.replace( key: account_id, key: segment_69, key: 69, data:account_id, data:segment_69, data: 69 );
MPRINT(TEST_01): if chunk = "......01............."B then rc = combination.replace( key: account_id, key: segment_70, key: 70, data:account_id, data:segment_70, data: 70 );
MPRINT(TEST_01): if chunk = ".......01............"B then rc = combination.replace( key: account_id, key: segment_71, key: 71, data:account_id, data:segment_71, data: 71 );
MPRINT(TEST_01): if chunk = "........01..........."B then rc = combination.replace( key: account_id, key: segment_72, key: 72, data:account_id, data:segment_72, data: 72 );
MPRINT(TEST_01): if chunk = ".........01.........."B then rc = combination.replace( key: account_id, key: segment_73, key: 73, data:account_id, data:segment_73, data: 73 );
MPRINT(TEST_01): if chunk = "..........01........."B then rc = combination.replace( key: account_id, key: segment_74, key: 74, data:account_id, data:segment_74, data: 74 );
MPRINT(TEST_01): if chunk = "...........01........"B then rc = combination.replace( key: account_id, key: segment_75, key: 75, data:account_id, data:segment_75, data: 75 );
MPRINT(TEST_01): if chunk = "............01......."B then rc = combination.replace( key: account_id, key: segment_76, key: 76, data:account_id, data:segment_76, data: 76 );
MPRINT(TEST_01): if chunk = ".............01......"B then rc = combination.replace( key: account_id, key: segment_77, key: 77, data:account_id, data:segment_77, data: 77 );
MPRINT(TEST_01): if chunk = "..............01....."B then rc = combination.replace( key: account_id, key: segment_78, key: 78, data:account_id, data:segment_78, data: 78 );
MPRINT(TEST_01): if chunk = "...............01...."B then rc = combination.replace( key: account_id, key: segment_79, key: 79, data:account_id, data:segment_79, data: 79 );
MPRINT(TEST_01): if chunk = "................01..."B then rc = combination.replace( key: account_id, key: segment_80, key: 80, data:account_id, data:segment_80, data: 80 );
MPRINT(TEST_01): if chunk = ".................01.."B then rc = combination.replace( key: account_id, key: segment_81, key: 81, data:account_id, data:segment_81, data: 81 );
MPRINT(TEST_01): if chunk = "..................01."B then rc = combination.replace( key: account_id, key: segment_82, key: 82, data:account_id, data:segment_82, data: 82 );
MPRINT(TEST_01): if chunk = "...................01"B then rc = combination.replace( key: account_id, key: segment_83, key: 83, data:account_id, data:segment_83, data: 83 );
* / ;
* Такая же постобработка;
proc sql;
create table summary_array_way as
select segment, position, count(*) as count
from
( select * from combinations_array_way union all corresponding
select * from each_position
)
group by segment, position;
quit;
proc transpose data=summary_array_way out=want_array_way(drop=_name_ where=(not missing(segment))) prefix=count_;
by segment;
id position;
var count;
format count 6.;
run;