【问题标题】:SAS Data Step | Between 2 DatesSAS 数据步 |两个日期之间
【发布时间】:2016-02-19 19:51:59
【问题描述】:

可能是一个简单的问题。我有一个简单的数据集,其中包含预定的付款日期。

DATA INFORM2;
 INFORMAT previous_pmt_date scheduled_pmt_date MMDDYY10.;
 INPUT previous_pmt_date scheduled_pmt_date;
 FORMAT previous_pmt_date scheduled_pmt_date MMDDYYS10.;
DATALINES;
11/16/2015 12/16/2015
12/17/2015 01/16/2016
01/17/2016 02/16/2016

; 

我要做的是创建一个二进制最新行指示器。例如,如果我想知道截至 2016 年 1 月 31 日的最新行,我希望将第 2 行标记为最新行。我之前一直在做的是找出 2016 年 1 月 31 日在 previous_pmt_date 和 schedule_pmt_date 之间的位置,但这不符合我的目的。我想在数据步骤中执行此操作,而不是 SQL 子查询。有什么想法吗?

想要:

previous_pmt_date scheduled_pmt_date latest_row_ind
11/16/2015        12/16/2015         0
12/17/2015        01/16/2016         1
01/17/2016        02/16/2016         0

【问题讨论】:

  • 数据步骤偏好有什么特别的原因吗?我问是因为 SQL 会更容易/更短......
  • 我已经有一个更大的数据步骤,我想将其集成到其中,但如果您有一些 SQL 建议,我很乐意使用这些建议。
  • 好吧,我想有一些 datastep 解决方案可能符合要求……我们可以假设数据按 schedule_pmt_date 升序排序吗?并且给定帐户的日期范围永远不会重叠(您没有显示帐户,但我假设有一个)。
  • 是的,没错。按升序帐户和 schedule_pmt_date 排序。给定帐户没有重叠范围。
  • 是否必须在单个数据步中完成。或者可以使用另一个数据步吗?我是否正确理解日期间隔 [previous_pmt_date; schedule_pmt_date] 不相交。对于给定的示例是的,但是对于所有数据?

标签: sas


【解决方案1】:

这是一个解决方案,它在单个现有数据步中完成所有操作,无需任何额外的排序。首先,我将稍微修改您的数据以包含帐户,因为解决方案确实应该考虑到这一点:

DATA INFORM2;
 INFORMAT previous_pmt_date scheduled_pmt_date MMDDYY10.;
 INPUT account previous_pmt_date scheduled_pmt_date;
 FORMAT previous_pmt_date scheduled_pmt_date MMDDYYS10.;
DATALINES;
1 11/16/2015 12/16/2015
1 12/17/2015 01/16/2016
1 01/17/2016 02/16/2016
2 11/16/2015 12/16/2015
2 12/17/2015 01/16/2016
2 01/17/2016 02/16/2016

; 
run;

指定截止日期:

%let cutoff_date = %sysfunc(mdy(1,31,2016));

此解决方案使用this question 中的方法将下一行数据中的变量保存到当前行中。如果需要,您可以在末尾删除 vars(出于测试目的,我已将其注释掉)。

data want;
  set inform2 end=eof;
  by account scheduled_pmt_date; 

  recno = _n_ + 1;

  if not eof then do;
    set inform2 (keep=account previous_pmt_date scheduled_pmt_date
                 rename=(account            = next_account 
                         previous_pmt_date  = next_previous_pmt_date
                         scheduled_pmt_date = next_scheduled_pmt_date)
                ) point=recno;
  end;
  else do;
    call missing(next_account, next_previous_pmt_date, next_scheduled_pmt_date);
  end;

  select;
    when ( next_account eq account and next_scheduled_pmt_date gt &cutoff_date ) flag='a';
    when ( next_account ne account ) flag='b';
    otherwise flag = 'z';
  end;

  *drop next:;

run;

这种方法的工作原理是使用数据集中的当前观察值(通过_n_ 获得)并将其加 1 以获得下一个观察值。然后,我们使用第二个带有point= 选项的set 语句来加载下一个观察结果并同时重命名变量,这样它们就不会覆盖当前变量。

然后我们使用一些逻辑来标记必要的记录。我不是 100% 满足您的目的所需的逻辑,因此我提供了一些示例逻辑并使用不同的标志来显示正在触发的逻辑。

一些笔记...

by 语句不是绝对必要的,但我将其包括在内是为了 (a) 确保数据正确排序,以及 (b) 帮助未来的读者理解数据步的意图,因为某些逻辑需要这个排序顺序。

call missing 语句只是用来清理日志。 SAS 不喜欢你有没有被赋值的变量,这将发生在最后一次观察中,所以这就是我们包含它的原因。将其注释掉,看看会发生什么。

end=eof 语法基本上创建了一个名为eof 的临时变量,当我们到达该 set 语句的最后一个观察值时,其值为 1。我们只是用它来确定我们是否在最后一行。

最后但非常重要的是,确保在加载第二个数据集时只保留所需的变量,否则您将覆盖原始数据中的现有变量。

【讨论】:

  • 我还建议您使用测试数据和案例语句,以确保涵盖所有边缘案例。
  • 我不认为你的方法是完全有效的。例如,如果我们向每个帐户添加另一个观察,您的方法是将帐户的第一次出现标记为“z”,然后将接下来的 2 个观察标记为“a”,最后将最后一个观察标记为“b”。在这种情况下,我想要的正确标志出现在每个帐户的第 2 行。
  • @davids12 是的,我认为在案例陈述中会有额外的复杂性或错误,但我会留给你解决这些问题的逻辑(我只能猜测如何处理它们)。不过,解决方案的基本结构已经到位——您只需更新case 语句逻辑即可满足您的需求。
  • @davids12 在旁注中,如果根据您的原始问题要求,我的案例逻辑中存在明显错误,请随时编辑/修复它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-19
  • 2012-10-31
相关资源
最近更新 更多