SAS - модифицировать один набор данных, используя условия из другого - PullRequest
0 голосов
/ 26 января 2019

Моя текущая проблема заключается в том, что у меня есть данные в одной таблице, в то время как другая таблица содержит диапазоны значений для классификации каждого столбца в категорию.

Пример: для "Сити" - значение от 0 до 6 изменяется на 1, 7-16 на 2 и 17+ на 3.

В конечном итоге мне придется использовать этот код в таблице из более чем 100 столбцов с общим количеством 500 диапазонов / категорий значений.

У меня есть подходящий код для создания категорий и для выбора столбца за столбцом, но основной код (чтение условия и применение их) ускользает от меня.

В следующем примере кода test1 содержит необработанные данные, test2 диапазоны значений для всех столбцов, условия test3 для выбранного столбца.

proc sql noprint;

create table work.test1
(Id char(4),
        city num,
        country num);

insert into work.test1
    values('1639',5,42260)
    values('1065',10,38090)
    values('1400',15,29769);

create table work.test2
(condition char(7),
        g_l char(6),
        g_p char(6));

insert into work.test2
values('city',"low","6")
values('city',"7","16")
values('city',"17","high")
values('country',"low","1000")
values('country',"1001","high");

    %let zmien = "city";

    data work.test3 (where=(condition = &zmien));
    set work.test2;
    run;

    proc sql noprint; 
    select count(warunek) into :ile_war 
    from work.test3; 
    quit;

    %let kat = 0; /* place where current category is stored */
    %let v_l = 0; /* place where lower border of the category is stored */
    %let v_h = 0; /* place where higher border of the category is stored */
    %macro kat(ile_war);

Моя идея состояла в том, чтобы использовать цикл do для макросов, чтобы просмотреть все категории для каждого столбца. Вся идея работает, если я не использую макрос (что исключает использование цикла, насколько мне известно), и вместо вызова symput используйте простые уравнения (x = y) в if.

%macro kat(ile);
%do a=1 %to &ile;
            data work.test4;
            set work.tesT3 point=a;

                    %if g_l = "low" %then %do;
                            call symput('kat',&a);
                            call symput('war_l',0);                     
                    %end;

                    %if g_l ~= "low" %then %do;
                            call symput('kat',&a);
                            call symput('war_l',g_l);
                    %end;

                    %if g_p = "high" %then %do;
                            call symput('war_h',9999999);
                    %end;

                    %if g_p ~= "high" %then %do;
                            call symput('war_h',g_p);
                    %end;
                            output;

            stop;

            data work.test1;
            modify work.test1(WHERE=(&zmien BETWEEN &war_l AND &war_h));
            &zmien=&kat;
            replace;
            run;

%end;
%mend;

Буду очень признателен за любую помощь с макросом или предложение сделать это каким-либо другим способом.

EDIT: Поэтому, пытаясь использовать рекомендуемый формат процедуры, я сталкиваюсь с проблемой - она ​​работает, когда я жестко кодирую диапазоны и переменную / столбец, которые нужно изменить, но я не знаю, как заставить это работать в случае:

A) имя столбца как содержимое макропеременной (возникла ошибка, что формат либо не найден, либо неприменим)

B) диапазоны, находящиеся в наборе данных

Как я могу прочитать значения для столбца переменной, вставить его в формат, использовать его для классификации данных, а затем перезаписать для использования в другом столбце?

Ответы [ 2 ]

0 голосов
/ 28 января 2019

Похоже, вы хотите использовать данные формата в TEST2 для преобразования значений в TEST1 в коды.Итак, для CITY у вас есть три уровня, поэтому вы хотите сгенерировать значения 1,2,3.Таким образом, вы можете сделать это с форматами, но если вы хотите, чтобы результаты были числами, а не строками, вам нужно использовать вызов функции INPUT () для преобразования отформатированного значения обратно в число.

Сначала давайтеСоздайте пример данных, используя обычный код SAS, поскольку редактировать тестовые данные гораздо проще, чем редактировать операторы SQL INSERT.

data test1;
  input id $ city country ;
cards;
1639 5 42260
1065 10 38090
1400 15 29769
;
data test2;
  input condition $ g_l $ g_p $ ;
cards;
city low 6
city 7 16
city 17 high
country low 1000
country 1001 high
;

Мы можем преобразовать набор данных TEST2 в формат.Мы можем использовать шаг данных для создания данных, необходимых для определения форматов с помощью PROC FORMAT.Давайте предположим, что он уже отсортирован по условию по категориям, которые вы хотите создать, чтобы мы могли генерировать номера категорий.Также я предполагаю, что CONDITION является допустимым именем формата (начинается с буквы или подчеркивания и НЕ заканчивается цифрой).

data formats ;
  length fmtname $32 start end 8 hlo $3 label $32 ;
  keep fmtname -- label;
  set test2;
  by condition notsorted;
  if first.condition then row=1;
  else row + 1;
  fmtname = condition ;
  start=input(g_l,??32.);
  end=input(g_p,??32.);
  if g_l='low' then hlo=cats(hlo,'L');
  if g_p='high' then hlo=cats(hlo,'H');
  label = left(put(row,32.));
run;

proc format cntlin=formats ;
run;

Чтобы использовать эти форматы для преобразования значений в номера категорий, которые мы должны сгенерироватьнекоторый код.Когда список переменных достаточно мал, вы можете поместить код в одну макропеременную (максимальная длина 64 Кбайт).

Например, если мы хотим сгенерировать новые переменные с суффиксом _GRP для любой переменной во входном наборе данных, TEST1, имя которой находится в списке условий в таблице метаданных, TEST2.Мы могли бы использовать такой код для генерации макропеременной.

proc contents data=test1 out=contents noprint;
run;

proc sql noprint ;
  select distinct cats(name,'_grp=input(put(',name,',',name,'.),32.)')
    into :recode separated by ';'
  from contents
  where upcase(name) in (select upcase(condition) from test2)
  ;
quit ;

Для вашего примера макропеременная RECODE выглядит следующим образом:

city_grp=input(put(city,city.),32.);
country_grp=input(put(country,country.),32.)

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

data want ;
  set test1 ;
  &recode;
run;

Результаты:

                                              country_
Obs     id     city    country    city_grp       grp

 1     1639      5      42260         1           2
 2     1065     10      38090         2           2
 3     1400     15      29769         2           2

Если у вас много переменных, которые вы кодируете, вместо генерации макропеременнойвместо этого вы можете просто написать код в файл.

proc sql noprint ;
  create table names as
  select distinct name
  from contents
  where upcase(name) in (select upcase(condition) from test2)
  ;
quit ;

filename code temp;
data _null_;
  set names ;
  file code ;
  put name +(-1) '_grp=input(put(' name ',' name +(-1) '.),32.);' ;
run;
data want ;
  set test1 ;
  %include code / source2;
run;

Возможно, вы также захотите сгенерировать еще одну серию форматов, которые вы могли бы использовать для декодирования категорий обратно в описания.Поэтому для вашей новой переменной CITY_GRP вы можете сгенерировать формат CITY_GRP., который будет переводить 1 в low - 6 и т. Д.

data format2 ;
  length fmtname $32 start 8 label $50 ;
  keep fmtname -- label;
  set test2;
  by condition notsorted;
  if first.condition then row=1;
  else row + 1;
  fmtname = catx('_',condition,'grp') ;
  start=row ;
  label = catx(' - ',g_l,g_p);
run;

proc format cntlin=format2; run;

proc print data=want;
 format city_grp city_grp. country_grp country_grp.;
run;

Результат:

Obs     id     city    country    city_grp    country_grp

 1     1639      5      42260     low - 6     1001 - high
 2     1065     10      38090     7 - 16      1001 - high
 3     1400     15      29769     7 - 16      1001 - high
0 голосов
/ 28 января 2019

Вот как это будет работать, если вы используете форматы. При желании вы можете автоматизировать фактическую реализацию форматов, но вот подход, который я бы порекомендовал. Вы можете создать IF / THEN, если хотите, но мне кажется, что это намного больше работы и более привередливые.

*create formats from the data set, test2;
data createFormats;
set test2;
by condition notsorted;
fmtname = catx('_', condition, 'fmt' );
start = g_l;
end = g_p;
label = catx(" to ", g_l, g_p);
run;

proc format cntlin=createFormats;
run;

title 'Original Data';
proc print data=test1;
run;

*recode into formats;

data new;
set test1;

*this part can be automated via a macro assuming you use consistent naming structure as here;

city_group = put(city, city_fmt.);
country_group = put(country, country_fmt.);

run;

title 'formats applied';
proc print data=new;
run;

*apply formats for display, will be honoured by most procs;
proc datasets lib=work nodetails nolist; 
modify test1;
*this could also be automated via a macro;
format city city_fmt. country country_fmt.;
run;quit;

title 'Recoded into new variables';
proc print data=test1;
run;
...