SAS数据集转置:解决列模式不一致导致的代码死循环问题
SAS数据集转置:死循环排查与解决
问题背景
需处理含75个NT变量的数据集,按ID拆分出time、date、code、notes字段并转置为指定格式,但现有SAS代码中的while循环陷入死循环,无法输出正确结果。
原始数据集代码
data have; input ID $ 1 NT2 NT3 NT4 NT5 NT6 ; cards; 1NOTES 12:13:44 03-16-2018 CODE: ABC AML NOTES 09:13:11 03-12-2018 CODE: OPI TEST 2NOTES 04:25:09 01-04-2018 CODE: FDS IMD NOTES 03:25:10 01-09-2018 CODE: FGH TEST 3NOTES 12:22:49 11-12-2018 CODE: DGH TESTNOTES 08:02:49 11-11-2018 CODE: LKO AML 4NOTES 22:02:21 01-14-2018 CODE: MKL TESTNOTES 07:02:21 01-10-2018 CODE: LOP IMD 5NOTES 09:01:36 01-23-2018 CODE: HJK TESTNOTES 09:01:56 01-23-2018 CODE: UIY TEST 6NOTES 11:01:06 01-20-2018 CODE: LPO IMD TEST NOTES 10:01:30 01-24-2018 CODE: KLO AML ; 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 6 11:01:06 01-20-2018 LPO IMD/TEST 6 10:01:30 01-24-2018 KLO AML
当前问题代码
data want; set have; attrib notes length=$50; array _nt{*} nt:; do i = 1 to dim(_nt) ; if not missing(_nt(i)) and index(left(_nt[i]), ' NOTES') then do; timestamp=input(scan(_nt(i), 3, " "), time8.0); date=input(scan(_nt(i), 4, " "), mmddyy10.); code = substr(_nt[i], index(left(_nt[i]), 'CODE:')+9); end; /*the while loop is used to concatenate notes that immediately follow the other, but it is running indefinitely*/ do while(index(left(_nt[i+1]), ' NOTES')=0); notes = catx('/',notes, _nt(i+1)); end; output; end; drop i nt:; format timestamp time8. date mmddyy10.; run;
死循环原因分析
- 循环变量未更新:while循环中没有递增
i,导致始终检查同一个_nt[i+1]位置,若该位置不是'NOTES'开头,循环会无限执行。 - 逻辑混乱:遍历数组时每个
i都输出一次,未按NOTES块分组处理;新NOTES块处理时未重置notes变量,会导致内容叠加错误。 - 数组越界风险:当
i等于数组维度时,_nt[i+1]会访问不存在的元素,引发未知错误。
修正后的解决方案
换用字符串拼接+块分割的思路,避免数组遍历的循环问题:
data want; set have; length full_str $2000 time $8 date $10 code $10 notes $50; /* 拼接所有NT变量为完整字符串 */ full_str = catx(' ', of nt:); /* 按NOTES分割出每个记录块,循环处理 */ do j = 1 to count(full_str, 'NOTES'); block = scan(full_str, j, 'NOTES'); if strip(block) = '' then continue; /* 解析time、date */ time = scan(block, 1, ' '); date = scan(block, 2, ' '); /* 解析code:提取CODE:后的第一个单词 */ code_pos = index(block, 'CODE:'); code = scan(substr(block, code_pos+5), 1, ' '); /* 解析notes:拼接CODE后多值内容,去除多余斜杠 */ notes_part = substr(block, code_pos+5+length(code)); notes = catx('/', scan(notes_part, 1, ' '), scan(notes_part, 2, ' ')); notes = compress(notes, '/', 's'); /* 输出当前记录 */ output; end; drop full_str block code_pos notes_part j nt:; run;
代码说明
- 字符串拼接:将所有NT变量合并为一个字符串,简化后续处理逻辑。
- 块分割处理:通过
count(full_str, 'NOTES')确定每个ID对应的记录数,用固定次数的do循环替代while循环,彻底避免死循环。 - 字段解析:分别提取time、date、code,将CODE后的多值notes用斜杠拼接,自动处理空值和多余符号。
内容的提问来源于stack exchange,提问作者lydias
相关产品推荐
相关产品推荐

