Сопоставление по нескольким переменным; Макрос SAS выполняет цикл при использовании _n_ в качестве переменной

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

Вот ручной код в качестве примера того, что я хочу автоматизировать (этот код работает нормально, в «родительском» наборе данных ta220092 в этом случае есть четыре наблюдения, а в другом - родительские наборы данных (он может быть больше или меньше):

data ta2200921 ta2200922 ta2200923 ta2200924;
set ta220092;
if _n_ = 1 then output ta2200921;
if _n_ = 2 then output ta2200922;
if _n_ = 3 then output ta2200923;
if _n_ = 4 then output ta2200924;
run;

Пытаясь автоматизировать это. Я подумал, что должен использовать автоматическую переменную «n» для добавления к имени набора данных и для оператора% to, поскольку количество наблюдений в каждом «родительском» наборе данных варьируется, но я не уверен, как это сделать сделай это. Я создал следующий код, в котором есть проблема, с которой, я надеюсь, кто-то может помочь:

%macro treatmentsplit(j);
%do i = 1 %to &j.;
&j. = _n_;
data tatest220092&i.;
set ta220092 (where = (_n_ = &i.));
run;
%end;
%mend treatmentsplit;
%treatmentsplit;

Спасибо.

Помимо редактирования приведенного выше для некоторой ясности, мне нужно отредактировать свой вопрос, чтобы выяснить, почему я не считаю, что это повторяющийся вопрос, как отметил Джо. Предлагаемый им повторяющийся вопрос: Какой самый быстрый как разделить набор данных sas для пакетной обработки?

Я не считаю этот вопрос повторяющимся по двум причинам. Во-первых, основная причина желания разделиться иная. Для моей проблемы это не проблема попытки разбить большой набор данных для разумной пакетной обработки. В следующем абзаце я рассмотрю свою основную причину, по которой я хочу разделиться. Вторая причина, по которой я не считаю это дубликатом, - это код, разрешающий вопрос «Какой самый быстрый способ разбить набор данных SAS для пакетной обработки» не работает в моей ситуации. В двух предоставленных кодовых ответах указывается количество наборов данных, на которые должен быть разделен родительский набор данных. Я не знаю заранее количество разделений для каждого набора данных, который я хочу разделить, поскольку количество наблюдений различается в каждом наборе данных. Я попытался изменить второй ответ (RWill) для моей ситуации, и пока безуспешно. Вот моя лучшая попытка изменить второй ответ на мою ситуацию до сих пор (пробовал варианты):

%macro nobs(dsn);
%local nobs dsid rc;
%let nobs=0;
%let dsid = %sysfunc(open(&dsn));
%if &dsid %then %do;
  %let nobs = %sysfunc(attrn(&dsid,NOBS));
%end;
%else %put Open for dataset &dsn failed - %sysfunc(sysmsg());
%let rc   = %sysfunc(close(&dsid));
%mend nobs;

%macro batch_process(dsn_in,dsn_out_prefix);
%let dsn_obs = %nobs(&dsn_in);
%let obs_per_dsn = 1;
data
 %do i = 1 %to &dsn_obs;
    &dsn_out_prefix.&i
 %end; ;
 set &dsn_in;
 drop _count;
 retain _count 0;
 _count = _count + 1;
 %do i = 1 %to &dsn_obs;
    if (1 + ((&i - 1) * 1) <= _count <= (&i * 1) then do;
       output &dsn_out_prefix.&i;
    end; 
 %end;
  run;
%mend batch_process;
%batch_process( dsn_in=tmp1.ta220092 , dsn_out_prefix = ta220092);

Ошибка из журнала, похоже, указывает на то, что существует проблема с переменной DSN_OBS в цикле do (5-я строка во втором макросе):

СИМВОЛГЕН: Макропеременная DSN_OBS разрешается в ОШИБКА: функция% EVAL не имеет выражения для оценки или оператор% IF не имеет условия. ОШИБКА: значение% TO цикла% DO I недопустимо.

Чтобы устранить мою основную причину, по которой я хочу разделить мой набор данных на одно наблюдение на набор данных, я изменил макрос, который почти работает так, как мне нужно, с одной проблемой. Исходный макрос, который я модифицировал, предназначен для сопоставления оценок склонности http://www.biostat.umn.edu/~will/6470stuff/Class25-12/PSmatching.sas. Я модифицирую его, чтобы лучше адресовать свой набор данных (изменяя имена переменных), и я также добавил метод, который я называю «CC» для вычисляемого измерителя, потому что я хочу захватить все элементы управления, которые находятся в пределах 10 или 20% от совпадающей переменной моего лечения group (будет вторая соответствующая переменная, выбранная ближайшим соседом, но у меня нет проблем с кодом для этого шага по строке). Проблема в том, что в наборе данных обработки (например, ta220092 выше) есть два наблюдения, у которых есть совпадающие переменные, которые имеют перекрывающиеся вычисленные «зоны» каверномера: у одного есть активы, которые равны 62, а у другого - 64. Макрос имеет замену вариант; если я выберу «да», то лечение будет сопоставлено с одним и тем же элементом управления 100 раз (не то, что я хочу, я хочу, чтобы все элементы управления были в пределах вычисленного измерителя). Если я выберу «нет» для варианта замены, то макрос будет работать почти так, как я хочу, но контрольные наблюдения, которые потенциально соответствуют двум наблюдениям обработки, которые имеют перекрывающиеся вычисленные измерители, будут разделены между двумя наблюдениями обработки, вместо того, чтобы иметь возможность быть в пределах нормы каждого лечения. Таким образом, макрос не позволяет замену на уровне набора данных, тогда как я хочу, чтобы он не допускал замены на уровне наблюдения. Другими словами, я хочу, чтобы между наблюдениями была замена, но я не уверен, как изменить макрос. Я подумал, что будет проще (но предоставил гораздо менее элегантное решение) разделить каждое наблюдение за лечением на отдельный набор данных (у меня меньше 600 процедур). Вот макрос, который у меня работает, но не выполняет то, что я хочу. (Поскольку я новичок в Stack Overflow, вы можете указать мне, является ли это изменение TMI, если я должен был открыть другой вопрос или просто дать всю эту информацию в исходном вопросе - я очень признателен за вашу помощь и буду хотел бы быть как можно меньше обузой).

    %macro Matching(datatreatment=, datacontrol=, method=, numberofcontrols=, caliper=, ccpercent=,
     replacement=, out=);

    /* Create copies of the treated units if N > 1 */;
     data _Treatment0(drop= i);
      set &datatreatment;
      do i= 1 to &numberofcontrols;
      RandomNumber= ranuni(12345);
    output;
    end;
    run;
    /* Randomly sort both datasets */
    proc sort data= _Treatment0 out= _Treatment(drop= RandomNumber);
    by RandomNumber;
    run;
    data _Control0;
    set &datacontrol;
    RandomNumber= ranuni(45678);
    run;
    proc sort data= _Control0 out= _Control(drop= RandomNumber);
    by RandomNumber;
    run;

     data Matched (keep = cikSelectedControl atControl roacontrol roatreat fyear  industry MatchedToTreatcik atTreat);
      length atC 8;
      length cikC 8;
      /* Load Control dataset into the hash object */
      if _N_= 1 then do;
    declare hash h(dataset: "_Control", ordered: 'no');
    declare hiter iter('h');
    h.defineKey('cikC');
    h.defineData('roac','atC','cikC');
    h.defineDone();
    call missing(cikC, atC, roac);
    end;
    /* Open the treatment */
    set _Treatment;
    %if %upcase(&method) ~= RADIUS %then %do;
    retain BestDistance 99;
    %end;
    /* Iterate over the hash */
    rc= iter.first();
    if (rc=0) then BestDistance= 99;
    do while (rc = 0);
    /* Caliper */
    %if %upcase(&method) = CALIPER %then %do;
    if (atT - &caliper) <= atC <= (atT + &caliper) then do;
    ScoreDistance = abs(atT - atC);
    if ScoreDistance < BestDistance then do;
    BestDistance = ScoreDistance;
    cikSelectedControl = cikC;
    atControl =  atC;
    MatchedToTreatcik = cikT;
    atTreat = atT;
    end;
    end;
    %end;
    /* Calculated caliper */
    %if %upcase(&method) = CC %then %do;
    ccdist = &ccpercent*atT;
    if (atT - ccdist) <= atC <= (atT + ccdist) then do;
    ScoreDistance = abs(atT - atC);
    if ScoreDistance < BestDistance then do;
    BestDistance = ScoreDistance;
    cikSelectedControl = cikC;
    atControl =  atC;

    MatchedToTreatcik = cikT;
    atTreat = atT;
    ROAControl = roaC;
    ROATreat=roat;
    end;
    end;
    %end;
    /* NN */
    %if %upcase(&method) = NN %then %do;
    ScoreDistance = abs(atT - atC);
    if ScoreDistance < BestDistance then do;
    BestDistance = ScoreDistance;
    cikSelectedControl = cikC;
    atControl =  atC;
    MatchedToTreatcik = cikT;
    atTreat = atT;
    end;
    %end;

    %if %upcase(&method) = NN or %upcase(&method) = CALIPER or %upcase(&method) = CC      %then %do;
    rc = iter.next();
    /* Output the best control and remove it */
    if (rc ~= 0) and BestDistance ~=99 then do;
    output;
    %if %upcase(&replacement) = NO %then %do;
    rc1 = h.remove(key: cikSelectedControl);
    %end;
    end;
    %end;
    /* Radius */
    %if %upcase(&method) = RADIUS %then %do;
    if (atT - &caliper) <= atC <= (atT + &caliper) then do;
    cikSelectedControl = cikC;
    atControl =  atC;
    MatchedToTreatcik = cikT;
    atTreat = atT;
    output;
    end;
    rc = iter.next();
    %end;
    end;
    run;
    /*to download datasets from wrds to investigate*/
    proc download data=matched; run;
    proc download data=_Control; run;
    /* Delete temporary tables. Quote for debugging */
    proc datasets NOLIST; /*Nolist option should prevent printing of dataset list*/
    delete _:(gennum=all);

    run;
     data &out;
       set Matched;
     run;
    proc datasets NOLIST; /*Nolist option should prevent printing of dataset list*/
    delete Matched;
    %mend Matching;


    %Matching(datatreatment= Ta220092, datacontrol= ca220092, method= cc,
     numberofcontrols= 100, caliper=1, ccpercent=.2, replacement= no, out= matchtest4);

Еще одно замечание: я буду запускать этот матч через PC SAS в системе WRDS, которая работает быстрее и не приведет к зависанию моего компьютера во время обработки.


person mrlcpa    schedule 12.07.2014    source источник
comment
Я хотел бы разделить свои наблюдения в родительском наборе данных на их собственные уникальные дочерние наборы данных. Почему? Что ты собираешься с ними делать дальше? Разве вы не можете просто прочитать родительский набор данных и использовать фильтры, чтобы получить нужные вам строки?   -  person sasfrog    schedule 12.07.2014
comment
Не делай этого. Как говорит @sasfrog, это обычно не очень хорошая идея. Если вы пытаетесь получить несколько случайных выборок своих данных, используйте proc surveyselect, он может сделать множество выборок (Google Don't be loopy SAS). Разделение наборов данных усложняет обслуживание и делает ваши библиотеки и код беспорядком.   -  person Joe    schedule 13.07.2014
comment
Спасибо, Джо и @Sasfrog за ваши комментарии. Я понимаю, что то, что я пытаюсь сделать, не изящно. Я отредактировал свое сообщение, чтобы попытаться дать лучшее обоснование, объяснить, почему вопрос о пакетной обработке не решает мою проблему, и указать основную проблему, которую я пытаюсь решить. Может быть, вы сможете взвесить дополнительную информацию?   -  person mrlcpa    schedule 14.07.2014
comment
Вау, tl; доктор. Что вы пытаетесь сделать в одном-двух предложениях?   -  person sasfrog    schedule 14.07.2014
comment
Я пытаюсь сопоставить наблюдения по переменной1 в пределах определенного%, а затем по ближайшему соседу переменной2, и у меня есть макрос, который почти работает, предоставляя мне пул элементов управления для первого шага - в пределах% от переменной1, но это не так. позволяют заменять контрольные наблюдения в контрольном пуле для разных наблюдений. Два способа исправить: 1) поместить каждое наблюдение лечения в отдельный набор данных (быстрый вопрос, неэлегантное решение, это мой исходный вопрос) или 2) исправить макрос (элегантный способ решения, но более сложный).   -  person mrlcpa    schedule 15.07.2014


Ответы (1)


Я улучшил свое понимание макроса и изменил его, чтобы он работал. Оказывается, вычисленная штангенциркуль в основном соответствовала ближайшему соседу с ограничением радиуса. Поэтому, когда я изменил макрос, чтобы включить вычисленный радиус, тогда макрос смог найти то, что мне нужно (см. Вопрос выше). Ниже представлен модифицированный макрос:

/************************************************ 
matching.sas   adapted from

Paper 185-2007  SAS Global Forum 2007
Local and Global Optimal Propensity Score Matching
Marcelo Coca-Perraillon
Health Care Policy Department, Harvard Medical School, Boston, MA

-------------------------------
Treatment and Control observations must be in separate datasets such that 
Control data includes: cikC =  subject_cik, atC = total assets
Treatment data includes: cikT, atT = total assets
cik must be numeric 

method = NN (nearest neighbor), caliper, or radius, or CC or RC --  CC/RC added by 
MRL calcpercent= percentage to be applied to ccvariable or rcvariable to create 
calculated caliper or calculated radius

caliper value = max for matching

replacement = yes/no  whether controls can be matched to more than one case

out = output data set name

example call:

 %Matching(datatreatment= T, datacontrol= C, method= RC,
  numberofcontrols= 1, caliper=, calcpercent=.20, replacement= no, out= matches);

************************************************/
rsubmit;
%macro Matching(datatreatment=, datacontrol=, method=, numberofcontrols=, caliper=,
 calcpercent=, replacement=, out=);

    /* Create copies of the treated units if N > 1 */;
     data _Treatment0(drop= i);
      set &datatreatment;
      do i= 1 to &numberofcontrols;
      RandomNumber= ranuni(12345);
    output;
    end;
    run;
    /* Randomly sort both datasets */
    proc sort data= _Treatment0 out= _Treatment(drop= RandomNumber);
    by RandomNumber;
    run;
    data _Control0;
    set &datacontrol;
    RandomNumber= ranuni(45678);
    run;
    proc sort data= _Control0 out= _Control(drop= RandomNumber);
    by RandomNumber;
    run;

     data Matched (keep = cikSelectedControl atControl roacontrol roatreat fyear industry MatchedToTreatcik atTreat);
      length atC 8;
      length cikC 8;
      /* Load Control dataset into the hash object */
      if _N_= 1 then do;
    declare hash h(dataset: "_Control", ordered: 'no');
    declare hiter iter('h');
    h.defineKey('cikC');
    h.defineData('roac','atC','cikC');
    h.defineDone();
    call missing(cikC, atC, roac);
    end;
    /* Open the treatment */
    set _Treatment;
    %if %upcase(&method) ~= RADIUS or %upcase(&method) ~= CR %then %do;
    retain BestDistance 99;
    %end;
    /* Iterate over the hash */
    rc= iter.first();
    if (rc=0) then BestDistance= 99;
    do while (rc = 0);
    /* Caliper */
    %if %upcase(&method) = CALIPER %then %do;
    if (atT - &caliper) <= atC <= (atT + &caliper) then do;
    ScoreDistance = abs(atT - atC);
    if ScoreDistance < BestDistance then do;
    BestDistance = ScoreDistance;
    cikSelectedControl = cikC;
    atControl =  atC;
    MatchedToTreatcik = cikT;
    atTreat = atT;
    end;
    end;
    %end;
    /* Calculated caliper */
    %if %upcase(&method) = CC %then %do;
    ccdist = &calcpercent*atT;
    if (atT - ccdist) <= atC <= (atT + ccdist) then do;
    ScoreDistance = abs(atT - atC);
    if ScoreDistance < BestDistance then do;
    BestDistance = ScoreDistance;
    cikSelectedControl = cikC;
    atControl =  atC;

    MatchedToTreatcik = cikT;
    atTreat = atT;
    ROAControl = roaC;
    ROATreat=roat;
    end;
    end;
    %end;
    /* NN */
    %if %upcase(&method) = NN %then %do;
    ScoreDistance = abs(atT - atC);
    if ScoreDistance < BestDistance then do;
    BestDistance = ScoreDistance;
    cikSelectedControl = cikC;
    atControl =  atC;
    MatchedToTreatcik = cikT;
    atTreat = atT;
    end;
    %end;

    %if %upcase(&method) = NN or %upcase(&method) = CALIPER or %upcase(&method) = CC %then %do;
    rc = iter.next();
    /* Output the best control and remove it */
    if (rc ~= 0) and BestDistance ~=99 then do;
    output;
    %if %upcase(&replacement) = NO %then %do;
    rc1 = h.remove(key: cikSelectedControl);
    %end;
    end;
    %end;
    /* Radius */
    %if %upcase(&method) = RADIUS %then %do;
    if (atT - &caliper) <= atC <= (atT + &caliper) then do;
    cikSelectedControl = cikC;
    atControl =  atC;
    MatchedToTreatcik = cikT;
    atTreat = atT;
    ROAControl = roaC;
    ROATreat=roat;
    output;
    end;
    rc = iter.next();
    %end;
    /* Calculated Radius */
    %if %upcase(&method) = CR %then %do;
    rcdist = &calcpercent*atT;
    if (atT - rcdist) <= atC <= (atT + rcdist) then do;
    cikSelectedControl = cikC;
    atControl =  atC;
    MatchedToTreatcik = cikT;
    atTreat = atT;
    ROAControl = roaC;
    ROATreat=roat;
    output;
    end;
    rc = iter.next();
    %end;
    end;
    run;
    /*for when testing and  using wrds
    proc download data=matched; run;
    proc download data=_Control; run;*/
    /* Delete temporary tables. Quote for debugging */
    proc datasets NOLIST; /*Nolist option should prevent printing of dataset list*/
    delete _:(gennum=all);

    run;
     data &out;
       set Matched;
     run;
    proc datasets NOLIST; /*Nolist option should prevent printing of dataset list*/
    delete Matched;
    %mend Matching;
person mrlcpa    schedule 15.07.2014