【问题标题】:array processing with different indices and missing values in SASSAS中具有不同索引和缺失值的数组处理
【发布时间】:2020-05-15 06:33:53
【问题描述】:

have 是一个 sas 数据集,包含 4 个变量:id 和变量,用于存储受访者与他们所在团队的 3 个不同成员共享的所有活动的信息。有 4 种不同的活动类型,由填充在每个玩家(p1 到 p3)的 :_activities 变量中的数字标识。以下是前 5 个 obs:

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

考虑受访者 A:他们与团队中的玩家 1 共享所有 4 项活动,并与团队中的玩家 2 共享活动 1 和 3。我需要为每个玩家位置和每个活动创建标志。例如,对于所有在p1_activities 字符变量中出现2 值的受访者,一个新的数字变量p1_act2_flag 应该等于1。以下是我需要为显示的数据创建的 12 个变量中的前 6 个变量:

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            .            .            …

我现在通过在一个长度语句中初始化所有变量名来做到这一点,然后编写大量的 if-then 语句。我想使用更少的代码行,但我的数组逻辑不正确。以下是我尝试为玩家 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-loop 中会导致我编写的代码行数比壁纸解决方案多。

您将如何更系统地和/或使用更少的代码行来做到这一点?

【问题讨论】:

    标签: arrays sas indices do-loops


    【解决方案1】:

    当只有 3 个标志时,不确定为什么要处理 4 个活动。

    一些想法:

    • 将列名重构​​为带编号的后缀会减少一些 壁纸 效果。
      • activities_p1-activities_p3
    • 将标志列名称重构为数字后缀
      • flag_p1_1-flag_p1_4
      • flag_p2_1-flag_p2_4
      • flag_p3_1-flag_p3_4
    • 使用DIM 保持在数组范围内。
    • 对标志使用二维数组
    • 对要标记的项目使用直接寻址
    • 添加错误检查

    不是更少,但也许更强大?

    此代码检查活动列表中的每个项目,而不是寻找特定项目的存在 (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;
    

    这里是相同的处理,但只搜索要标记的特定项目:

    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 删除空格和其他垃圾。
      • 首先&gt; 0 将位置评估为0 j 不存在和1 存在
      • 第二个&gt; 0 是另一个逻辑评估,它确保j 存在标志保持01。否则,标志将是频率计数(想象一下活动数据1,1,2,3
    • flags(i,j) 涵盖了可用于标记的 3 x 4 插槽。

    【讨论】:

    • 我会看看这个。需要明确的是,有 四个活动。 have 的原始变量中的数字代表独特的活动。当我说“考虑受访者 A:他们与团队中的玩家 1 共享所有 4 项活动”时 - 这意味着“1,2,3,4”是四种类型的活动。
    • 这是一个很好的解决方案。你不经常看到人们做二维数组。干得好!
    【解决方案2】:

    考虑转换为分层视图并在那里执行逻辑。这里真正的问题是每个列表中都可能缺少位置。正因为如此,一个简单的 do 循环会很困难。更快的方法是多步骤:

    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;
    

    【讨论】:

      【解决方案3】:

      按照 Stu 的示例,DS2 DATA 步骤可以使用哈希查找执行他的“合并”。哈希查找依赖于创建将 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

      结果

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-08-21
        • 2022-11-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多