从当前观察中读取下一个 k 观察

Reading next k observation from current observation

Here's a very similar question

我的问题和上面的有点不同link。

背景

我有一个包含每小时数据的数据集。所以每个对象每天有 24 条记录。现在我想创建 K 个新列,代表每个对象的下一个 1,2,...K 小时记录。如果不存在,则用缺失值替换它们。

K是动态的,由用户定义。

必须保留原来的顺序。无论是在数据步骤中保证还是最后使用排序。

我正在寻找实现此目标的有效方法。

例子

原始数据:

Object Hour Value 
A      1    2.3
A      2    2.3
A      3    4.0
A      4    1.3

给定 K = 2,所需的输出是

Object Hour Value Value1 Value2
A      1    2.3   2.3    4.0
A      2    2.3   4.0    1.3
A      3    4.0   1.3    .
A      4    1.3   .      .

可能的解决方案

  1. 逆序排序->获取前k条记录->向后排序

当没有。观察量很大,这不应该是一个理想的方式。

  1. proc expand。我不熟悉它,因为它从未在我的电脑上获得许可。

  2. 在数据步骤中使用 point

  3. retain 数据步骤中的语句。我不确定这是如何工作的。

您可以调换小时数,然后在每个对象中自由访问提前的小时数。只是为了设置 K 的值并生成一些虚拟数据:

* Assign K ;
%let K=3 ;
%let Kn=value&k;

* Generate test objects each containing 24 hourly records ;
data time ;
  do object=1 to 10 ;
      do hour=1 to 24 ;
        value=round(ranuni(1)*10,0.1) ;
        output ;
      end ;
  end ;
run ;

编辑:我更新了以下步骤,因为意识到不需要转置。一步完成所有操作可在 CPU 时间

中提高约 20%

使用 24 小时值数组并循环 do i=1 to &k 每小时:

* Populate K variables ;
data output(keep=object hour value value1-&kn ) ;
  set time ;
  by object ;
  retain k1-k24 . ;
  array k(2,24) k1-k24 value1-value24 ;

  k(1,hour)=value ;

  if last.object then do hour=1 to 24 ;
    value=k(1,hour) ;
    do i=1 to &k ;
      if hour+i <=24 then k(2,i)=k(1,hour+i) ;
      else k(2,i)=.;
    end ;
    output ;
  end ;
run ;

假设这是作为宏变量提供的,这很容易通过边到边合并提前完成。对于比总记录数大得多的 K,肯定比转置快,并且可能比循环 POINT 快。

基本上,您将原始数据集合并到自身,并使用 FIRSTOBS 将起点向下推一个用于每个连续的合并迭代。如果您有需要保护的 BY 组,这需要一些额外的工作,但这通常不太难管理。

这是一个使用 SASHELP.CLASS 的例子:

%let K=5;

%macro makemergesets(k=, datain=, varin=, keepin=);
  %do _i = 2 %to &k;
    &datain (firstobs=&_i rename=&varin.=&varin._&_i. keep=&keepin. &varin.)
  %end;
%mend makemregesets;

data class_all;
  merge sashelp.class
    %makemergesets(k=&k,datain=sashelp.class, varin=age,keepin=)
  ;
run;