基于子串提取创建变量实现SAS数据集转置需求问询
解决SAS数据集按规则转置提取字段的问题
现有SAS数据集需按特定规则转置:原数据集包含ID及75个NT开头的变量,其中待处理的notes类变量后缀为奇数(如nt3、nt5等)。目标是转置后按ID拆分出time、date、code、notes字段,每个ID对应多条记录,以下是详细信息及解决方案。
原数据集代码
data have; input ID $1. NT2 $38. NT3 $4. NT4 $38. NT5 $4.; cards; 1 NOTES 12:13:44 03-16-2018 CODE: ABC AML NOTES 09:13:11 03-12-2018 CODE: OPI TEST 2 NOTES 04:25:09 01-04-2018 CODE: FDS IMD NOTES 03:25:10 01-09-2018 CODE: FGH TEST 3 NOTES 12:22:49 11-12-2018 CODE: DGH TEST NOTES 08:02:49 11-11-2018 CODE: LKO AML 4 NOTES 22:02:21 01-14-2018 CODE: MKL TEST NOTES 07:02:21 01-10-2018 CODE: LOP IMD 5 NOTES 09:01:36 01-23-2018 CODE: HJK TEST NOTES 09:01:56 01-23-2018 CODE: UIY TEST ; run;
期望输出
| ID | time | date | code | notes |
|---|---|---|---|---|
| 1 | 12:13:44 | 03-16-2018 | ABC | AML |
| 1 | 09:13:11 | 03-12-2018 | OPI | TEST |
| 2 | 04:25:09 | 01-04-2018 | FDS | IMD |
| 2 | 03:25:10 | 01-09-2018 | FGH | TEST |
| 3 | 12:22:49 | 11-12-2018 | DGH | TEST |
| 3 | 08:02:49 | 11-11-2018 | LKO | AML |
| 4 | 22:02:21 | 01-14-2018 | MKL | TEST |
| 4 | 07:02:21 | 01-10-2018 | LOP | IMD |
| 5 | 09:01:36 | 01-23-2018 | HJK | TEST |
| 5 | 09:01:56 | 01-23-2018 | UIY | TEST |
现有代码问题分析
用户编写的代码无法得到目标输出,核心问题包括:
- 数组
t包含所有NT开头变量,未筛选后缀为奇数的目标变量 - 提取字段时错误引用
nt0,未使用当前循环的数组元素t[_i] - 未在循环内执行
output,导致每个ID仅保留最后一次循环结果 note0的拼接逻辑与需求无关,属于冗余代码
现有错误代码:
data note11; length note0 $50; set note10; array t{*} nt:;; do _i = 1 to dim(t); /*确保notes非空*/ if not missing(t[_i]) then note0 = catx('/',note0,vname(t(_i))); /*提取时间戳、日期、代码*/ if find(upcase(t[_i]),"NOTES") then do; timestamp=substr(left(nt0), index(left(nt0), 'NOTES')+5, 9); date=substr(left(nt0), 15, 10); code = substr(left(nt0), index(left(nt0), 'CODE:')+5); end; end; drop _i; run;
解决方案代码
以下代码可实现目标需求,适配75个NT变量的场景:
data want; set have; /* 定义数组包含所有NT开头变量 */ array nt_all{*} nt:; do _i = 1 to dim(nt_all); /* 提取变量名后缀的数字 */ _suffix = input(scan(vname(nt_all[_i]), 2, 'NT'), best.); /* 仅处理后缀为奇数且非空的变量 */ if mod(_suffix, 2) = 1 and not missing(nt_all[_i]) then do; /* 提取time:NOTES后的第一个字段 */ time = scan(nt_all[_i], 2, ' '); /* 提取date:NOTES后的第二个字段 */ date = scan(nt_all[_i], 3, ' '); /* 提取CODE:后的内容,拆分code和notes */ _code_str = substr(nt_all[_i], index(upcase(nt_all[_i]), 'CODE:') + 5); code = scan(_code_str, 1, ' '); notes = scan(_code_str, 2, ' '); /* 输出当前记录 */ output; end; end; /* 丢弃临时变量和原始NT变量 */ drop _i _suffix _code_str nt:; run;
代码要点说明
- 变量筛选:通过提取变量名后缀的数字,用
mod(_suffix,2)=1判断是否为奇数,无需手动枚举75个变量 - 字段提取:使用
scan和index函数替代固定位置截取,避免因字符串长度变化导致的提取错误 - 记录生成:每次循环符合条件时执行
output,为每个ID生成多条对应记录 - 清理冗余:最后丢弃临时变量和原始NT变量,仅保留目标字段
内容的提问来源于stack exchange,提问作者lydias
相关产品推荐
相关产品推荐

