Извлечение определенных строк из данных с использованием хеш-объекта в SAS - PullRequest
1 голос
/ 11 февраля 2012

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

Table A

Rec  Var1 Var2 ... VarX
1    ...
2
3

Во второй таблице указывается, каким строкам из Table A должна быть назначена переменная кодирования:

Table B

Code  BegRec    EndRec
AA      1200      4370
AX      7241      9488
BY     12119     14763

Таким образом, первая строка Table B означает, что любым данным в Table A, которые имеют rec между 1200 и 4370, должен быть присвоен код AA.

Я знаю, как это сделать с помощью proc sql, но я хочу посмотреть, как это сделать с хеш-объектом .

В SQL это просто:

proc sql;
 select b.code, a.*
 from tableA a, tableB b
 where b.begrec<=a.rec<=b.endrec;
quit;

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

Ответы [ 4 ]

4 голосов
/ 11 февраля 2012

Решение хеш-объекта (код ввода данных заимствован у @Rob_Penridge).

    data big;
      do rec = 1 to 20000;
       output;
      end;
    run;

    data lookup;      
      input Code $ BegRec EndRec;
      datalines;
      AA      1200      4370
      AX      7241      9488
      BY     12119     14763
      ;
    run;


    data created;
      format code $4.;
      format begrec endrec best8.;
      if _n_=1 then do;
        declare hash h(dataset:'lookup');
        h.definekey('Code');
        h.definedata('code','begrec','endrec');
        h.definedone();
        call missing(code,begrec,endrec);
        declare hiter iter('h');
      end;

    set big;
    iter.first();
      do until (rc^=0);
       if begrec <= rec <= endrec then do;
       code_dup=code;
      end;
      rc=iter.next();
     end;
    keep rec code_dup;
    run;
2 голосов
/ 14 февраля 2012

Вот мое решение, используя proc format.Это также делается в оперативной памяти, очень похоже на хеш-таблицу, но для работы требуется меньше структурного кода.

(Код ввода данных также заимствован из @Rob_Penridge.)

data big;
  do rec = 1 to 20000;
   output;
  end;
run;

data lookup;      
  input Code $ BegRec EndRec;
  datalines;
  ZZ         0        20
  JJ        40        60
  AA      1200      4370
  AX      7241      9488
  BY     12119     14763
  ;
run;

data lookup_f;
    set lookup;    
    rename
        BegRec  = start
        EndRec  = end
        Code    = label;

    retain fmtname 'CodeRecFormat';
run;

proc format library = work cntlin=lookup_f; run;


data big_formatted;
    format rec CodeRecFormat.;
    format rec2 8.;
    length code $5.;

    set big;    

    code = putn(rec, "CodeRecFormat.");
    rec2 = rec;
run;
2 голосов
/ 13 февраля 2012

У меня возникнет соблазн использовать метод прямого доступа POINT = здесь, он будет читать только требуемые номера строк, а не весь набор данных.Вот код, который использует тот же код создания данных, что и в ответе Роба.

    data want;
    set lookup;
    do i=begrec to endrec;
    set big point=i;
    output;
    end;
    drop begrec endrec;
    run;

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

    data big;
    set lookup (rename=(code=code1));
    do i=begrec to endrec;
    modify big point=i;
    code=code1;
    replace;
    end;
    run;
2 голосов
/ 11 февраля 2012

Я не уверен, что хеш-таблица будет даже самым эффективным подходом здесь.Я бы, вероятно, решил эту проблему, используя оператор SELECT, так как условная логика будет быстрой, и все равно требуется только 1 анализ данных:

select;
  when ( 1200 <= _n_ <=4370) code = 'AA';
  ...
  otherwise;
end;

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

1) Создайте данные

data big;
  do i = 1 to 20000;
    output;
  end;
run;

data lookup;      
  input Code $ BegRec EndRec;
  datalines;
AA      1200      4370
AX      7241      9488
BY     12119     14763
;
run;

2) Сохраните содержимое таблицы меньшего размера в макроспеременные.Вы также можете сделать это, используя call symput или другой предпочтительный метод.Этот метод предполагает, что у вас не слишком много строк в таблице поиска.

%table_parse(iDs=lookup, iField=code  , iPrefix=code);
%table_parse(iDs=lookup, iField=begrec, iPrefix=begrec);
%table_parse(iDs=lookup, iField=endrec, iPrefix=endrec);

3) Динамически создайте оператор SELECT.

%macro ds;
  %local cnt;

  data final;
    set big;

    select;
      %do cnt=1 %to &code;
        when (&&begrec&cnt <= _n_ <= &&endrec&cnt) code = "&&code&cnt";
      %end;
      otherwise;
    end;

  run;
%mend;
%ds;

Вот макрос служебной программы:

/*****************************************************************************
**  MACRO.TABLE_PARSE.SAS
**
**  AS PER %LIST_PARSE BUT IT TAKES INPUT FROM A FIELD IN A TABLE.
**  STORE EACH OBSERVATION'S FIELD'S VALUE INTO IT'S OWN MACRO VARIABLE.
**  THE TOTAL NUMBER OF WORDS IN THE STRING IS ALSO SAVED IN A MACRO VARIABLE.
**
**  THIS WAS CREATED BECAUSE %LIST_PARSE WOULD FALL OVER WITH VERY LONG INPUT
**  STRINGS.  THIS WILL NOT.
**
**  EACH VALUE IS STORED TO ITS OWN MACRO VARIABLE.  THE NAMES
**  ARE IN THE FORMAT <PREFIX>1 .. <PREFIX>N.
**
**  PARAMETERS:
**  iDS        : (LIB.DATASET) THE NAME OF THE DATASET TO USE.
**  iFIELD     : THE NAME OF THE FIELD WITHIN THE DATASET.
**  iPREFIX    : THE PREFIX TO USE FOR STORING EACH WORD OF THE ISTRING TO 
**               ITS OWN MACRO VARIABLE (AND THE TOTAL NUMBER OF WORDS). 
**  iDSOPTIONS : OPTIONAL. ANY DATSET OPTIONS YOU MAY WANT TO PASS IN
**               SUCH AS A WHERE FILTER OR KEEP STATEMENT.
**
******************************************************************************
**  HISTORY:
**  1.0  MODIFIED: 01-FEB-2007  BY: ROBERT PENRIDGE
**  - CREATED.
**  1.1  MODIFIED: 27-AUG-2010  BY: ROBERT PENRIDGE
**  - MODIFIED TO ALLOW UNMATCHED QUOTES ETC IN VALUES BEING RETURNED BY 
**    CHARACTER FIELDS.
**  1.2  MODIFIED: 30-AUG-2010  BY: ROBERT PENRIDGE
**  - MODIFIED TO ALLOW BLANK CHARACTER VALUES AND ALSO REMOVED TRAILING
**    SPACES INTRODUCED BY CHANGE 1.1.
**  1.3  MODIFIED: 31-AUG-2010  BY: ROBERT PENRIDGE
**  - MODIFIED TO ALLOW PARENTHESES IN CHARACTER VALUES.
**  1.4  MODIFIED: 31-AUG-2010  BY: ROBERT PENRIDGE
**  - ADDED SOME DEBUG VALUES TO DETERMINE WHY IT SOMETIMES LOCKS TABLES.
*****************************************************************************/
%macro table_parse(iDs=, iField=, iDsOptions=, iPrefix=);
  %local dsid pos rc cnt cell_value type;

  %let cnt=0;
  /*
  ** OPEN THE TABLE (AND MAKE SURE IT EXISTS)
  */
  %let dsid=%sysfunc(open(&iDs(&iDsOptions),i));
  %if &dsid eq 0 %then %do;
    %put WARNING: MACRO.TABLE_PARSE.SAS: %sysfunc(sysmsg());      
  %end;

  /*
  ** GET THE POSITION OF THE FIELD (AND MAKE SURE IT EXISTS)
  */
  %let pos=%sysfunc(varnum(&dsid,&iField));
  %if &pos eq 0 %then %do;
    %put WARNING: MACRO.TABLE_PARSE.SAS: %sysfunc(sysmsg());      
  %end;
  %else %do;
    /*
    ** DETERMINE THE TYPE OF THE FIELD
    */
    %let type = %upcase(%sysfunc(vartype(&dsid,&pos)));
  %end;

  /*
  ** READ THROUGH EACH OBSERVATION IN THE TABLE
  */
  %let rc=%sysfunc(fetch(&dsid));
  %do %while (&rc eq 0);
    %let cnt = %eval(&cnt + 1);
    %if "&type" = "C" %then %do;
      %let cell_value = %qsysfunc(getvarc(&dsid,&pos));
      %if "%trim(&cell_value)" ne "" %then %do;
        %let cell_value = %qsysfunc(cats(%nrstr(&cell_value)));
      %end;
    %end;
    %else %do;
      %let cell_value = %sysfunc(getvarn(&dsid,&pos));
    %end;

    %global &iPrefix.&cnt ;
    %let &iPrefix.&cnt = &cell_value ;

    %let rc=%sysfunc(fetch(&dsid));
  %end;


  /*
  ** CHECK FOR ABNORMAL TERMINATION OF LOOP
  */
  %if &rc ne -1 %then %do;
    %put WARNING: MACRO.TABLE_PARSE.SAS: %sysfunc(sysmsg());      
  %end;


  /*
  ** ENSURE THE TABLE IS CLOSED SUCCESSFULLY
  */
  %let rc=%sysfunc(close(&dsid));
  %if &rc %then %do;
    %put WARNING: MACRO.TABLE_PARSE.SAS: %sysfunc(sysmsg());      
  %end;

  %global &iPrefix;
  %let &iPrefix = &cnt ;
%mend;

Другие примеры вызова этого макроса:

%table_parse(iDs=sashelp.class, iField=sex, iPrefix=myTable, iDsOptions=%str(where=(sex='F')));
%put &mytable &myTable1 &myTable2 &myTable3; *etc...;
...