обработка массивов с разными индексами и пропущенными значениями в SAS - PullRequest
2 голосов
/ 29 января 2020

have - это набор данных sas с 4 переменными: id и переменными, хранящими информацию обо всех действиях, которыми респондент делится с 3 различными членами команды, в которой они находятся. Существует 4 различных типа активности, которые определяются числами, указанными в :_activities переменных для каждого игрока (с p1 по p3). Ниже приведены первые 5 наблюдений:

id  p1_activities   p2_activities   p3_activities
A   1,2,3,4         1,3 
B   1,3             1,2,3           1,2,3
C                   1,2,3           1,2,3
D                   1,2,3   
E   1,2,3                           1

Рассмотрим респондента А: они делятся всеми 4 действиями с игроком 1 в своей команде, а действия 1 и 3 - с игроком 2 в своей команде. Мне нужно создать флаги для каждой позиции игрока и каждого вида деятельности. Например, новая переменная цифр c p1_act2_flag должна равняться 1 для всех респондентов, у которых значение 2 указано в символьной переменной p1_activities. Вот первые 6 переменных, которые мне нужно создать из 12 итогов для показанных данных:

p1_act1_flag p1_act2_flag p1_act3_flag p1_act4_flag p2_act1_flag p2_act2_flag …
1            1            1            1            1            0            …
1            0            1            0            1            1            …
.            .            .            .            1            1            …
.            .            .            .            1            1            …
1            1            1            0            .            .            …

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

data want;
length p1_act1_flg p1_act2_flg p1_act3_flg p1_act4_flg
       p2_act1_flg p2_act2_flg p2_act3_flg p2_act4_flg
       p3_act1_flg p3_act2_flg p3_act3_flg p3_act4_flg
       p4_act1_flg p4_act2_flg p4_act3_flg p4_act4_flg 8.0;
set have;

array plracts  {*} p1_activities p2_activities p3_activities;

array p1actflg {*} p1_act1_flg p1_act2_flg p1_act3_flg p1_act4_flg;
array p2actflg {*} p2_act1_flg p2_act2_flg p2_act3_flg p2_act4_flg;
array p3actflg {*} p3_act1_flg p3_act2_flg p3_act3_flg p3_act4_flg;
array p4actflg {*} p4_act1_flg p4_act2_flg p4_act3_flg p4_act4_flg;

do i=1 to dim(plracts);
do j=1 to dim(p1actflg);

         if find(plracts{i}, cats(put(j, $12.))) then p1actflg{j}=1;
    else if missing(plracts{i})                  then p1actflg{j}=.;
    else                                              p1actflg{j}=0;

end;
end;

*do this again for the other p#actflg arrays;

run;

Мой «индекс массива вне диапазона» из-за разной длины массива игрока и активности, но вложение в разные циклы do в результате я написал гораздо больше строк кода, чем решение для обоев.

Как бы вы сделали это более систематически и / или с гораздо меньшим количеством строк кода?

Ответы [ 3 ]

3 голосов
/ 29 января 2020

Не уверен, почему вы обрабатываете 4 действия для флагов, когда их только 3.

Некоторые идеи:

  • Перевод имен столбцов в нумерованные суффиксы уменьшит некоторые из обои эффект.
    • activities_p1-activities_p3
  • Преобразование имен столбцов флагов в суффиксы чисел
    • flag_p1_1-flag_p1_4
    • flag_p2_1-flag_p2_4
    • flag_p3_1-flag_p3_4
  • Используйте DIM, чтобы не выходить за границы массива.
  • Используйте двумерный массив для флагов
  • Используйте прямую адресацию элементы, которые нужно пометить
  • Добавить проверку ошибок

Не меньше, но, возможно, более надежно?

Этот код проверяет каждый элемент в списке действий, а не ищет присутствие заданных c элементов (1..4):

data want;
  set have;
  array activities
    activities_p1-activities_p3
  ;
  array flags(3,4) 
    flag_p1_1-flag_p1_4
    flag_p2_1-flag_p2_4
    flag_p3_1-flag_p3_4
  ;

  do i = 1 to dim(activites);
    if missing(activities[i]) then continue; %* skip;
    do j = 1 by 1;
      item = scan ( activities[i], j, ',' );
      if missing(item) then leave; %* no more items in csv list;
      item_num = input (item,?1.);
      if missing(item_num) then continue; %* skip, csv item is not a number;
      if item_num > hbound(flags,2) or item_num < lbound(flags,2) then do;
        put 'WARNING:' item_num 'is invalid for flagging';
        continue; %* skip, csv item is missing, 0, negative or exceeds 4;
      end;
      flags (i, item_num) = 1;
    end;
    * backfill zeroes where flag not assigned;
    do j = 1 to hbound(flags,2);
      flags (i, item_num) = sum (0, flags (i, item_num));  %* sum() handles missing values;
    end;
  end;

Здесь та же самая обработка, но только поиск определенных c элементов, которые должны быть помечены:

data have; length id activities_p1-activities_p3 $20;input 
id  activities_p1-activities_p3 ; datalines;
A   1,2,3,4         1,3             .
B   1,3             1,2,3           1,2,3
C   .               1,2,3           1,2,3
D   .               1,2,3           .
E   1,2,3           .               1
;
data want;
  set have;
  array activities
    activities_p1-activities_p3
  ;
  array flags(3,4) 
    flag_p1_1-flag_p1_4
    flag_p2_1-flag_p2_4
    flag_p3_1-flag_p3_4
  ;
  do i = 1 to dim(activities);
    if not missing(activities[i]) then
    do j = 1 to hbound(flags,2);
      flags (i,j) = sum (flags(i,j), findw(trim(activities[i]),cats(j),',') > 0) > 0;
    end;
  end;
run;

Что происходит?

  • переменные флагов сбрасываются на пропущенные в верхней части шага
  • hbound возвращают 4 в качестве верхнего предела второго измерения
  • findw(trim(activities[i]),cats(j),',') найти позицию j в строке csv
    • trim, необходимой для удаления конечных пробелов, которые не являются частью findw списка разделителей слов
    • cats преобразований число от j до представления символов
    • findw возвращает позицию j в строке csv.
      • может также захотеть compress пропусков и другого мусора, если значения данных о деятельности ненадежны.
    • first > 0 оценивает положение в 0 j не подарок и 1 подарок
    • секунда > 0 - это еще одна оценка логики c, которая гарантирует, что флаг присутствия останется 0 или 1. В противном случае флаги будут подсчитывать частоту (представьте данные активности 1,1,2,3)
  • flags(i,j), охватывающие 3 x 4 слота, доступных для пометки.
1 голос
/ 30 января 2020

Следуя примеру Стю, шаг DS2 DATA может выполнить его «слияние» с использованием поиска ha sh. Поиск ha sh зависит от создания набора данных, который сопоставляет списки элементов CSV с флагами.

* Create data for hash;

data share_flags(where=(not missing(key)));
  length key $7 f1-f4 8;
  array k[4] $1 _temporary_;

  do f1 = 0 to 1; k[1] = ifc(f1,'1','');
  do f2 = 0 to 1; k[2] = ifc(f2,'2','');
  do f3 = 0 to 1; k[3] = ifc(f3,'3','');
  do f4 = 0 to 1; k[4] = ifc(f4,'4','');
    key = catx(',', of k[*]);
    output;
  end;end;end;end;
run;

proc ds2;
  data want2 / overwrite=yes;
    declare char(20) id;
    vararray char(7) pact[*] activities_p1-activities_p3;
    vararray double fp1[*] flag_p1_1-flag_p1_4;
    vararray double fp2[*] flag_p2_1-flag_p2_4;
    vararray double fp3[*] flag_p3_1-flag_p3_4;
    declare char(1) sentinel;

    keep id--sentinel;
    drop sentinel;

    declare char(7) key;
    vararray double flags[*] f1-f4;

    declare package hash shares([key],[f1-f4],4,'share_flags'); %* load lookup data;

    method run();
      declare int rc;
      set have;

      rc = shares.find([activities_p1],[flag_p1:]);  %* find() will fill-in the flag variables;
      rc = shares.find([activities_p2],[flag_p2:]);
      rc = shares.find([activities_p3],[flag_p3:]);
    end;

  enddata;
run;
quit;
%let syslast = want2;

share_flags
enter image description here

результат
enter image description here

1 голос
/ 29 января 2020

Рассмотрите возможность преобразования в иерархическое представление и выполнения логики c. Настоящим сторонником здесь является тот факт, что в каждом списке могут отсутствовать позиции. Из-за этого простое выполнение l oop будет затруднено. Более быстрый способ будет многошаговым:

  1. Создание шаблона всех возможных игроков и позиций
  2. Создание фактического списка всех игроков и позиций
  3. Объединение шаблон с фактическим списком и пометить все совпадения

Это не так элегантно, как один шаг данных, как это может быть сделано, но с ним довольно легко работать.

data have;
infile datalines dlm='|';
input id$ p1_activities$ p2_activities$ p3_activities$;
datalines;
A|1,2,3,4|1,3| 
B|1,3|1,2,3|1,2,3| 
C| |1,2,3|1,2,3| 
D| |1,2,3| 
E|1,2,3| |1
;
run;

/* Make a template of all possible players and positions */
data template;
    set have;
    array players p1_activities--p3_activities;
    length varname $15.;

    do player = 1 to dim(players);
        do activity = 1 to 4;

            /* Generate a variable name for later */
            varname = cats('p', player, '_act', activity, '_flg');
            output;
        end;
    end;

    keep ID player activity varname;
run;

/* Create a list of actual players and their positions */
data actual;
    set have;
    array players p1_activities--p3_activities;

    do player = 1 to dim(players);
        do i = 1 to countw(players[player], ',');
            activity = input(scan(players[player], i, ','), 8.);

            /* Do not output missing positions */
            if(NOT missing(activity)) then output;
        end;
    end;

    keep ID player activity;
run;

/* Merge the template with actual values and create a flag when an
   an id, player, and activity matches with the template
*/
data want_long;
    merge template(in=all)
          actual(in=act);
    by id player activity;

    flag_activity = (all=act);
run;

/* Transpose it back to wide */
proc transpose data=want_long
               out=want_wide;
    id varname;
    by id;
    var flag_activity;
run;
...