Как добавить число с начальным нулем в счетчик SAS Macro l oop? - PullRequest
1 голос
/ 18 февраля 2020

Итак, у меня есть ряд наборов данных в указанной c библиотеке. Эти наборы данных имеют имена в формате DATASET_YYYYMM, по одному набору данных на каждый месяц. Я пытаюсь добавить диапазон этих наборов данных на основе пользовательского ввода для диапазона дат. т.е. если start_date - 01NOV2019, а end_date - 31JAN2020, я хочу добавить три набора данных: LIBRARY.DATASET_201911, LIBRARY.DATASET_201912 и LIBRARY.DATASET_202001.

Диапазон, очевидно, переменный, поэтому я не могу просто назвать наборы данных вручную в заданной функции. Поскольку мне нужно провести l oop через годы и месяцы в диапазоне дат, я считаю, что макрос - лучший способ сделать это. Я использую al oop в операторе SET для добавления всех наборов данных. Я скопировал мой пример кода ниже. Это работает в теории. Но на практике, только если мы в цикле в течение ноября и декабря. Поскольку формат имени набора данных имеет два ди git месяца, для января-сентября это будет 01-09. Однако функция month возвращает 1-9, и, конечно, выдается ошибка «Файл DATASET_NAME не существует». Проблема в том, что я не могу найти способ заставить его интерпретировать месяц, начиная с нуля, не нарушая функциональность другой части цикла / макроса.

Я пробовал множество подходов, чтобы отформатировать число как z2, не могу заставить его работать. То есть, включая функции PUTN в строке DO для quote_month следующим образом, он игнорирует начальный ноль при генерации имени набора данных в строке ниже.

%DO quote_month = %SYSFUNC(IFN(&quote_year. = &start_year.,%SYSFUNC(PUTN(&start_month.,z2.)),1,.)) %TO %SYSFUNC(IFN(&quote_year. = &end_year.,%SYSFUNC(PUTN(&end_month.,z2.)),12,.));

Ниже приведен пример кода (без какой-либо попытки переформатировать его в z2 ) - он выдаст ошибку, потому что не может найти «dataset_20201», потому что он на самом деле называется «dataset_202001». Набор данных с именем dataset_combined_example создает желаемый вывод кода, вручную ссылаясь на имена наборов данных, что он не сможет сделать на практике. Кто-нибудь знает как go об этом?

DATA _NULL_;
    FORMAT start_date end_date DATE9.;
    start_date = '01NOV2019'd;
    end_date = '31JAN2020'd;
    CALL symput('start_date',start_date);
    CALL symput('end_date',end_date);
RUN;

DATA dataset_201911;
   input name $;
   datalines;
Nov1
Nov2
;
RUN;
DATA dataset_201912;
   input name $;
   datalines;
Dec1
Dec2
;
RUN;
DATA dataset_202001;
   input name $;
   datalines;
Jan1
Jan2
;
RUN;

DATA dataset_combined_example;
    SET dataset_201911 dataset_201912 dataset_202001;
RUN;


%MACRO get_table(start_date, end_date);
    %LET start_year = %SYSFUNC(year(&start_date.));
    %LET end_year = %SYSFUNC(year(&end_date.));
    %LET start_month = %SYSFUNC(month(&start_date.));
    %LET end_month = %SYSFUNC(month(&end_date.));
    DATA dataset_combined;
        SET
            %DO quote_year = &start_year. %TO &end_year.;
                %DO quote_month = %SYSFUNC(IFN(&quote_year. = &start_year.,&start_month.,1,.)) %TO %SYSFUNC(IFN(&quote_year. = &end_year.,&end_month.,12,.));
                    dataset_&quote_year.&quote_month.
                %END;
            %END;
        ;
    RUN;
%MEND;
%get_table(&start_date.,&end_date.);

Ответы [ 4 ]

2 голосов
/ 18 февраля 2020

Вы можете сделать это, используя putn и z2. формат.

%DO quote_year = &start_year. %TO &end_year.;
     %DO quote_month = %SYSFUNC(IFN(&quote_year. = &start_year.,&start_month.,1,.)) %TO %SYSFUNC(IFN(&quote_year. = &end_year.,&end_month.,12,.));
            dataset_&quote_year.%sysfunc(putn(&quote_month.,z2.))
     %END;
%END;
1 голос
/ 18 февраля 2020

Вы можете использовать формат Z для генерации строк с ведущими нулями.

Но ваша проблема гораздо проще, если вы используете функции даты и форматы SAS для генерации строк ГГГГММ. Просто используйте обычный итеративный% DO l oop для циклического смещения месяца от нуля до количества месяцев между двумя датами.

%macro get_table(start_date, end_date);
%local offset dsname ;
data dataset_combined;
  set
%do offset=0 %to %sysfunc(intck(month,&start_date,&end_date));
  %let dsname=dataset_%sysfunc(intnx(month,&start_date,&offset),yymmn6);
  &dsname.
%end;
  ;
run;
%mend get_table;

Результат:

445   options mprint;
446   %get_table(start_date='01NOV2019'd,end_date='31JAN2020'd);
MPRINT(GET_TABLE):   data dataset_combined;
MPRINT(GET_TABLE):   set dataset_201911 dataset_201912 dataset_202001 ;
MPRINT(GET_TABLE):   run;
1 голос
/ 18 февраля 2020

Вы также можете сделать это, используя таблицы метаданных, не прибегая к макроциклам:

/* A few datasets to combine */
data 
  DATASET_201910
  DATASET_201911
  DATASET_201912
  DATASET_202001
  ;
run;

%let START_DATE = '01dec2019'd;
%let END_DATE = '31jan2020'd; 

proc sql noprint;
  select catx('.', libname, memname) into :DS_LIST separated by ' '
  from dictionary.tables
  where 
    &START_DATE <= 
      case 
        when prxmatch('/DATASET_\d{6}/', memname) 
          then input(scan(memname, -1, '_'), yymmn6.)
        else -99999 
      end 
    <= &END_DATE 
    and libname = 'WORK'
  ;
quit;

data combined_datasets /view=combined_datasets;
  set &DS_LIST; 
run;

case-when в предложении where гарантирует, что любые другие наборы данных присутствуют в том же самом библиотеки, которые не соответствуют ожидаемой схеме именования, игнорируются.

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

0 голосов
/ 18 февраля 2020

В макросе

  • Используйте INTNX, чтобы вычислить границы для всех значений даты oop. В пределах l oop:
    • Вычислить имя набора данных кандидата в соответствии с указанными lib, prefix и желаемым значением даты format. <yyyy><mm> выводится в формате yymmn6.
    • Используйте EXIST для проверки возможных наборов данных на наличие.
      • В качестве альтернативы, не проверяйте, но обязательно установите OPTIONS NODSNFERR до объединения. Этот параметр предотвратит ошибки при указании несуществующего набора данных.
    • Обновите индекс l oop до конца месяца, чтобы следующий шаг переводил индекс в начало следующий месяц.

%macro names_by_month(lib=work, prefix=data_, start_date=today(), end_date=today(), format=yymmn6.);

  %local index name;

  %* loop over first-of-the-month date values;
  %do index = %sysfunc(intnx(month, &start_date, 0)) %to %sysfunc(intnx(month, &end_date, 0));

    %* compute month dependent name;
    %let name = &lib..&prefix.%sysfunc(putn(&index,&format));

    %* emit name if it exists;
    %if %sysfunc(exist(&name)) or %sysfunc(exist(&name,VIEW)) %then %str(&name);

    %* prepare index for loop +1 increment so it goes to start of next month;
    %let index = %sysfunc(intnx(month, &index, 0, E));
  %end;  

%mend;

* example usage:

data combined_imports(label="nov2019 to jan2020");
  set 
    %names_by_month(
      prefix=import_,
      start_date='01NOV2019'd,
      end_date = '31JAN2020'd
    )
  ;
run;
...