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完全连接< /代码>