Loops 在SAS数据步骤的每次迭代中向表中添加多行
我有一个包含时间间隔数据的数据集。它看起来像这样:Loops 在SAS数据步骤的每次迭代中向表中添加多行,loops,sas,Loops,Sas,我有一个包含时间间隔数据的数据集。它看起来像这样: date | person | shift_start | shift_end | activity_start | activity_end | activity 10JAN | Joe | 8:00 | 16:00 | 10:00 | 11:00 | training 10JAN | Joe | 8:00 | 16:00 | 13:00
date | person | shift_start | shift_end | activity_start | activity_end | activity
10JAN | Joe | 8:00 | 16:00 | 10:00 | 11:00 | training
10JAN | Joe | 8:00 | 16:00 | 13:00 | 14:00 | meeting
11JAN | Joe | 8:00 | 16:00 | 8:00 | 11:00 | dragoning
11JAN | Joe | 8:00 | 16:00 | 13:00 | 14:00 | wizardry
我想做的是浏览表格并“填补空白”。对于上面的数据,我想添加以下行
date | person | shift_start | shift_end | activity_start | activity_end | activity
10JAN | Joe | 8:00 | 16:00 | 8:00 | 10:00 | default
10JAN | Joe | 8:00 | 16:00 | 11:00 | 13:00 | default
10JAN | Joe | 8:00 | 16:00 | 14:00 | 16:00 | default
11JAN | Joe | 8:00 | 16:00 | 11:00 | 13:00 | default
11JAN | Joe | 8:00 | 16:00 | 14:00 | 16:00 | default
如您所见,我需要为每个日期和人员添加可能多的行。我不确定是否可以以这种方式在datastep中添加行,因为传入数据的处理将继续进行。此外,即使这一点得到支持,我也不确定如何才能实现我的目标。我是这么想的:
data fill_gaps;
retain prev_date
prev_default_activity_end
prev_default_activity_start;
if(date <> prev_date) then do;
/*different shift than previous row */
if(shift_start = activity_start) then do;
/* the newly created activity start time should be the
activity end time of this row, but the new activity end
time cannot be determined without looking at the next row */
default_activity_start = activity_end;
else do;
/* the new activity start and end time can be determined */
default_activity_start = shift_start;
default_activity_end = activity_start;
end;
else do;
/* same shift as previous row */
default_activity_start = prev_default_activity_start;
default_activity_end = activity_start;
end;
prev_date = date;
prev_default_activity_end = default_activity_end;
prev_default_activity_start = default_activity_start;
run;
数据填补空白;
保留上一个日期
上一个默认活动结束
上一个默认活动开始;
如果(日期上一个日期),则执行;
/*与上一行不同的移位*/
如果(班次启动=活动启动),则执行;
/*新创建的活动开始时间应为
此行的“活动结束时间”,但新的“活动结束时间”
如果不查看下一行,则无法确定时间*/
默认活动开始=活动结束;
否则你会;
/*可以确定新活动的开始和结束时间*/
默认活动开始=班次开始;
默认活动结束=活动开始;
结束;
否则你会;
/*与前一行的班次相同*/
默认活动开始=上一次默认活动开始;
默认活动结束=活动开始;
结束;
上一个日期=日期;
prev\u default\u activity\u end=默认\u activity\u end;
prev\u default\u activity\u start=default\u activity\u start;
跑
然后可能需要更多的数据步骤来提取填充了default\u activity\u start
和default\u activity\u end
的行,并将这些行(带有新列)附加到原始表中
这对我来说似乎有点麻烦,而且我还没有机会测试它(对不起,我知道这听起来很懒!)。难道没有比这更优雅的方法吗
感谢 这里有一个使用
lag
的解决方案,假设您的输入数据集名为test
。此解决方案填补空白并输出原始行,有关更多信息,请参阅注释:
/* Must sort by person,date to use by-group processing */
proc sort data=test;
by person date;
run;
data fill_gaps (drop=_:);
_new_row=0;
set test;
/*Hold the previous activity end time*/
_laen=lag(activity_end);
by person date;
/*Conditions such that a new row should be inserted */
if (shift_start < activity_start and first.date) or
_laen < activity_start
or (last.date and activity_end < shift_end) then do;
/* Output current row */
output;
/*Build interim row and output */
activity_end = activity_start;
if first.date then activity_start = shift_start;
else activity_start = _laen;
activity = 'default';
_new_row=1;
output;
/* If we get to the last date - output the end record */
if last.date and activity_end < shift_end then do;
activity_start = activity_end;
activity_end = shift_end;
output;
end;
end;
else output;
run;
/*必须按人员、日期排序,以便按组处理使用*/
proc排序数据=测试;
按人日期;
跑
数据填补空白(下降=);
_新行=0;
集合测试;
/*保留上一个活动结束时间*/
_laen=滞后(活动结束);
按人日期;
/*应插入新行的条件*/
如果(班次开始<活动开始和第一个日期)或
_laen<活动\启动
或者(最后日期和活动结束<班次结束),然后执行;
/*输出电流行*/
产出;
/*生成临时行和输出*/
活动结束=活动开始;
如果为first.date,则活动开始=班次开始;
其他活动开始时间=\u laen;
活动='默认';
_新行=1;
产出;
/*如果我们到达最后一个日期-输出结束记录*/
如果last.date和activity\u end
输出将无法正确排序,您需要按人员、日期、活动\u开始进行排序
使用了临时变量,所有变量都以下划线作为前缀。要从输出数据集中删除这些内容,请删除数据集选项中关于drop=.
的注释。哈希对象是根据键搜索与否向前或向后操作数据的最方便工具。这里有一个散列选项。对于每个数据步骤迭代,它将当前行上载到哈希上,在分析和填充间隙后,下载该行并输出。如果数据按原样输入,则无需排序
data have;
infile cards dlm='|';
input (date person) (:$8.) (shift_start shift_end activity_start activity_end ) (:time8.) activity :$20.;
format shift_start shift_end activity_start activity_end :time8.;
cards;
10JAN | Joe | 8:00 | 16:00 | 10:00 | 11:00 | training
10JAN | Joe | 8:00 | 16:00 | 13:00 | 14:00 | meeting
11JAN | Joe | 8:00 | 16:00 | 8:00 | 11:00 | dragoning
11JAN | Joe | 8:00 | 16:00 | 13:00 | 14:00 | wizardry
;
data want;
if _n_=1 then
do;
dcl hash h();
h.definekey('person');
h.definedata('date','person', 'shift_start', 'shift_end', 'activity_start', 'activity_end', 'activity');
h.definedone();
end;
set have;
by person date notsorted;
rc=h.add();
lag_end=lag(activity_end);
if first.date and shift_start < activity_start then
do;
activity_end=activity_start;
activity_start=shift_start;
activity='default';
output;
end;
else if lag_end < activity_start then
do;
activity_end=activity_start;
activity_start=lag_end;
activity='default';
output;
end;
rc=h.find();
output;
rc=h.clear();
if last.date and activity_end < shift_end then
do;
activity_start=activity_end;
activity_end=shift_end;
activity='default';
output;
end;
drop rc lag_end;
run;
数据已经存在;
填充卡dlm=“|”;
输入(日期人)(:8。)(班次开始班次结束活动开始活动结束)(:时间8。)活动:$20。;
格式班次\开始班次\结束活动\开始活动\结束:时间8。;
卡;
1月10日|乔| 8:00 | 16:00 | 10:00 | 11:00 |培训
1月10日乔8:00 | 16:00 | 13:00 | 14:00 |会议
11月|乔| 8:00 | 16:00 | 8:00 | 11:00 |龙
11月|乔| 8:00 | 16:00 | 13:00 | 14:00 |巫术
;
数据需求;
如果n=1,则
做
dcl散列h();
h、 definekey(“人”);
h、 定义数据(“日期”、“人员”、“班次开始”、“班次结束”、“活动开始”、“活动结束”、“活动”);
h、 definedone();
结束;
集有;
按人日期排序;
rc=h.add();
滞后结束=滞后(活动结束);
如果first.date和shift\u start<活动\u start,则
做
活动结束=活动开始;
活动开始=班次开始;
activity='default';
产出;
结束;
否则,如果滞后结束<活动开始,则
做
活动结束=活动开始;
活动开始=滞后结束;
activity='default';
产出;
结束;
rc=h.find();
产出;
rc=h.clear();
如果最后日期和活动结束<班次结束,则
做
活动开始=活动结束;
活动结束=班次结束;
activity='default';
产出;
结束;
落下钢筋混凝土滞后端;
跑
可以换档交叉吗?考虑使用<代码> PROC SQL完全连接< /代码>