SAS使用数组实现多观测数据转置(禁用proc transpose)
SAS 数组实现长表转宽表(禁用PROC TRANSPOSE)
需求说明
- 实现多观测长表到单ID单记录宽表的转置,禁止使用
proc transpose过程,必须通过数组逻辑完成 - 原始数据集
have结构:每条记录对应单次行权行为,包含id(主体ID)、exercise_date(行权日期)、total_shares_exercised(行权股数)三个字段 - 目标宽表结构:
- 单条记录对应唯一ID
- 横向展开字段:ID、Date1/Date2/...(按行权顺序存储各次行权日期)、Shares_exercised_date1/Shares_exercised_date2/...(存储对应次的行权股数)
原代码问题排查
原代码运行后仅能返回最后一条日期取值,核心问题如下:
- 存在错误的子集筛选语句
if last.ID;,该语句会直接过滤掉每个ID分组下除最后一条外的所有观测,前序赋值结果全部被丢弃 - 仅定义了日期存储数组,未定义行权股数的对应存储数组,无法输出股数字段
- 未对数组元素加
retain声明,SAS默认每次读入新观测会重置未保留变量的值,无法累加存储前序记录 - 引用变量名错误,原始日期字段名为
exercise_date,代码中错误写为不存在的date变量 - 未在新ID开始时重置计数器和数组值,容易出现不同ID的数据交叉污染
修正后实现代码
data want; set have; by id; /* 定义两个数组,分别存储行权日期、对应行权股数,预设最大支持40次行权,可按需调整长度 */ array date_arr[40] Date1-Date40; array shares_arr[40] Shares_exercised_date1-Shares_exercised_date40; /* 保留计数器、数组所有元素的值,跨观测读取时不重置 */ retain counter date_arr: shares_arr:; /* 每个ID的第一条记录触发初始化:重置计数器、清空数组历史值 */ if first.id then do; counter = 1; call missing(of date_arr[*], of shares_arr[*]); end; /* 按当前计数器序号给对应数组位置赋值 */ date_arr[counter] = exercise_date; shares_arr[counter] = total_shares_exercised; /* 计数器自增,为下一条记录的存储做准备 */ counter + 1; /* 仅在ID组最后一条记录时输出,此时该ID所有行权记录已全部存入数组 */ if last.id then output; /* 给所有日期字段统一应用日期输出格式 */ format Date: date9.; run;
逻辑说明
- 代码依赖数据集按
id排序,示例提供的原始have数据集已满足排序要求,若实际数据未排序需提前用proc sort排序 - 数组长度设置为40是兼容绝大多数场景的预设值,可根据业务中单ID最大行权次数灵活调整
call missing语句用于清空数组残留值,避免上一个ID的存量数据混入当前ID的结果- 输出动作仅在每个ID的最后一条观测触发,保证输出结果为单ID单条记录的宽表格式
内容的提问来源于stack exchange,提问作者Stacked_sas
相关产品推荐
相关产品推荐

