在SAS中用条件求和检测移动观测窗口数据是否符合规则
SAS分组生成移动窗口规则列解决方案
需求说明
按ID分组,为每个观测生成名为RuleHit的新列:若当前观测的datetime往前8个月的移动窗口内,所有滞后观测(即时间早于当前观测的记录)中event列值为"B"的数量≥2,则RuleHit=1,否则为0。
输入数据集(have)
data have; input id datetime :datetime. event $; format datetime datetime.; datalines; 1 01JAN2022:00:00:00 A 1 15APR2022:11:46:34 B 1 23JUN2022:01:02:10 A 1 22JAN2023:14:32:58 A 1 08JUN2023:16:10:32 B 1 11JUL2023:20:39:51 A 1 28JAN2023:21:41:35 A 1 09APR2023:11:22:17 A 1 16MAY2023:22:14:23 B 1 13NOV2022:08:08:56 B 2 06FEB2022:09:23:43 B 2 12MAY2022:07:55:09 A 2 25JUN2022:08:26:44 B 2 03OCT2022:09:54:03 A 2 05DEC2022:01:51:06 A 2 12JUL2022:10:14:44 B 2 20SEP2022:16:39:51 A 2 01JAN2023:01:18:57 A 2 28MAR2023:12:46:34 B 2 05JUN2023:07:32:10 A 3 28FEB2022:23:08:58 A 3 02APR2022:05:22:17 B 3 21JUN2022:22:01:43 B 3 10AUG2022:18:36:23 A 3 13NOV2022:20:45:19 A 3 24DEC2022:17:37:57 B 3 18MAR2023:19:41:35 A 3 06MAY2023:15:22:17 A 3 09JUL2023:02:14:23 B 3 21OCT2023:14:08:56 B ; run;
预期输出数据集(want)
data want; input id datetime :datetime. event $ RuleHit; format datetime datetime.; datalines; 1 01JAN2022:00:00:00 A 0 1 15APR2022:11:46:34 B 0 1 23JUN2022:01:02:10 A 0 1 13NOV2022:08:08:56 B 1 1 22JAN2023:14:32:58 A 0 1 28JAN2023:21:41:35 A 0 1 09APR2023:11:22:17 A 0 1 16MAY2023:22:14:23 B 1 1 08JUN2023:16:10:32 B 0 1 11JUL2023:20:39:51 A 0 2 06FEB2022:09:23:43 B 0 2 12MAY2022:07:55:09 A 0 2 25JUN2022:08:26:44 B 1 2 12JUL2022:10:14:44 B 0 2 20SEP2022:16:39:51 A 0 2 03OCT2022:09:54:03 A 1 2 05DEC2022:01:51:06 A 0 2 01JAN2023:01:18:57 A 0 2 28MAR2023:12:46:34 B 0 2 05JUN2023:07:32:10 A 0 3 28FEB2022:23:08:58 A 0 3 02APR2022:05:22:17 B 0 3 21JUN2022:22:01:43 B 1 3 10AUG2022:18:36:23 A 1 3 13NOV2022:20:45:19 A 1 3 24DEC2022:17:37:57 B 1 3 18MAR2023:19:41:35 A 0 3 06MAY2023:15:22:17 A 0 3 09JUL2023:02:14:23 B 1 3 21OCT2023:14:08:56 B 1 ; run;
解决方案
方法一:DATA步滑动窗口(高效适合大数据集)
此方法通过排序后维护临时数组队列,动态管理滑动窗口内的B计数,效率较高,适合大规模数据集:
/* 1. 先按ID和datetime排序,确保时间序列有序 */ data have_sorted; set have; by id datetime; run; /* 2. 计算移动窗口内的滞后B数量,生成RuleHit */ data want; set have_sorted; by id; /* 计算窗口起始时间:当前时间往前推8个月,'s'参数表示起始点为月初 */ window_start = intnx('month', datetime, -8, 's'); /* 用临时数组存储观测时间和B标记,数组大小可按需调整 */ array dt[1000] _temporary_; array b_flag[1000] _temporary_; retain dt b_flag cnt_b ptr; /* 每个ID分组初始化 */ if first.id then do; call missing(of dt[*], of b_flag[*]); cnt_b = 0; ptr = 0; end; /* 将当前观测加入队列 */ ptr + 1; dt[ptr] = datetime; b_flag[ptr] = (event = 'B'); if b_flag[ptr] then cnt_b + 1; /* 移除窗口外的旧观测,更新B计数 */ do while(dt[1] < window_start); if b_flag[1] then cnt_b - 1; /* 队列元素左移 */ do i = 1 to ptr - 1; dt[i] = dt[i+1]; b_flag[i] = b_flag[i+1]; end; ptr - 1; if ptr = 0 then cnt_b = 0; end; /* 排除当前观测,判断是否满足条件 */ RuleHit = (cnt_b - b_flag[ptr] >= 2); /* 删除临时变量 */ drop window_start ptr i cnt_b dt: b_flag:; run;
方法二:PROC SQL自连接(简洁适合小数据集)
此方法通过自连接匹配窗口内的滞后观测,统计B的数量,代码简洁直观,适合小规模数据集:
proc sql; create table want as select a.id, a.datetime, a.event, /* 统计窗口内滞后观测中B的数量,判断是否≥2 */ case when sum(case when b.event = 'B' then 1 else 0 end) >= 2 then 1 else 0 end as RuleHit from have as a left join have as b on a.id = b.id /* 匹配当前观测往前8个月到当前观测之前的记录 */ and b.datetime >= intnx('month', a.datetime, -8, 's') and b.datetime < a.datetime group by a.id, a.datetime, a.event /* 按ID和时间排序,与预期输出一致 */ order by a.id, a.datetime; quit;
关键说明
- 必须先对数据集按
ID和datetime排序,确保时间窗口的计算基于有序的时间序列。 - 两种方法均严格排除当前观测,只统计滞后观测中的B数量,符合需求定义。
内容的提问来源于stack exchange,提问作者hmhensen
相关产品推荐
相关产品推荐

