SAS中具有不同索引和缺失值的数组处理
array processing with different indices and missing values in SAS
have
是一个包含 4 个变量的 sas 数据集:一个 id
和存储有关受访者与他们所在团队的 3 个不同成员共享的所有活动的信息的变量。有 4 种不同的 activity 类型,由每个玩家(p1 到 p3)的 :_activities
变量中填充的数字标识。以下是前 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
考虑受访者 A:他们与团队中的玩家 1 共享所有 4 项活动,并与团队中的玩家 2 共享活动 1 和 3。我需要为每个玩家位置和每个 activity 创建标志。例如,对于 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 . . …
我现在通过在一个 length 语句中初始化所有变量名,然后编写一大堆 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, .))) 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;
我的 "array subscript is out of range" 因为播放器和 activity 数组的长度不同,但嵌套在不同的 do 循环中会导致我编写的代码行数比墙纸解决方案多得多。
您如何更系统地做到这一点,and/or 用更少的代码行?
不确定为什么要处理 4 个标志活动,而只有 3 个标志。
一些想法:
- 将列名重构为编号后缀会减少一些 wallpaper 效果。
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 ;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
return 4为二次元上限
findw(trim(activities[i]),cats(j),',')
在 csv 字符串中查找 j 的位置
trim
需要删除不属于 findw
单词分隔符列表 的尾随空格
cats
将j数转换为字符表示
findw
returnj 在 csv 字符串中的位置。
- 如果 activity 数据值不可靠,可能还
compress
出空间和其他垃圾。
- 首先
> 0
将位置评估为 0
j 不存在且 1
存在
- second
> 0
是另一个逻辑评估,确保 j present 标志保持 0
或 1
。否则标志将是频率计数(想象 activity 数据 1,1,2,3
)
flags(i,j)
涵盖可用于标记的 3 x 4 插槽。
考虑转换成分层视图并在那里执行逻辑。这里真正的难题是每个列表中都可能缺少位置。因此,一个简单的 do 循环会很困难。更快的方法是多步骤:
- 创建所有可能的球员和位置的模板
- 创建所有球员和位置的实际列表
- 将模板与实际列表合并并标记所有匹配项
它不像单个数据步骤那样优雅,但使用起来有点容易。
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 .;
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;
按照 Stu 的示例,DS2
数据步骤可以使用散列查找执行他的 'merge'。哈希查找取决于创建将 CSV 项目列表映射到标志的数据集。
* Create data for hash;
data share_flags(where=(not missing(key)));
length key f1-f4 8;
array k[4] _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
结果
have
是一个包含 4 个变量的 sas 数据集:一个 id
和存储有关受访者与他们所在团队的 3 个不同成员共享的所有活动的信息的变量。有 4 种不同的 activity 类型,由每个玩家(p1 到 p3)的 :_activities
变量中填充的数字标识。以下是前 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
考虑受访者 A:他们与团队中的玩家 1 共享所有 4 项活动,并与团队中的玩家 2 共享活动 1 和 3。我需要为每个玩家位置和每个 activity 创建标志。例如,对于 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 . . …
我现在通过在一个 length 语句中初始化所有变量名,然后编写一大堆 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, .))) 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;
我的 "array subscript is out of range" 因为播放器和 activity 数组的长度不同,但嵌套在不同的 do 循环中会导致我编写的代码行数比墙纸解决方案多得多。
您如何更系统地做到这一点,and/or 用更少的代码行?
不确定为什么要处理 4 个标志活动,而只有 3 个标志。
一些想法:
- 将列名重构为编号后缀会减少一些 wallpaper 效果。
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 ;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
return 4为二次元上限findw(trim(activities[i]),cats(j),',')
在 csv 字符串中查找 j 的位置trim
需要删除不属于findw
单词分隔符列表 的尾随空格
cats
将j数转换为字符表示findw
returnj 在 csv 字符串中的位置。- 如果 activity 数据值不可靠,可能还
compress
出空间和其他垃圾。
- 如果 activity 数据值不可靠,可能还
- 首先
> 0
将位置评估为0
j 不存在且1
存在 - second
> 0
是另一个逻辑评估,确保 j present 标志保持0
或1
。否则标志将是频率计数(想象 activity 数据1,1,2,3
)
flags(i,j)
涵盖可用于标记的 3 x 4 插槽。
考虑转换成分层视图并在那里执行逻辑。这里真正的难题是每个列表中都可能缺少位置。因此,一个简单的 do 循环会很困难。更快的方法是多步骤:
- 创建所有可能的球员和位置的模板
- 创建所有球员和位置的实际列表
- 将模板与实际列表合并并标记所有匹配项
它不像单个数据步骤那样优雅,但使用起来有点容易。
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 .;
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;
按照 Stu 的示例,DS2
数据步骤可以使用散列查找执行他的 'merge'。哈希查找取决于创建将 CSV 项目列表映射到标志的数据集。
* Create data for hash;
data share_flags(where=(not missing(key)));
length key f1-f4 8;
array k[4] _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
结果