Sas 满足条件时按组跳转到下一个

Sas 满足条件时按组跳转到下一个,sas,Sas,我有一个记录婚姻状况变化的文件——身份证、变化类型(婚姻、离婚、丧偶)和变化年份(和月份)。我想计算每个人在任何给定年份的婚姻状况(已婚、离婚、寡妇(呃)、从未结婚)。因为一个人可以经历许多更改,而我的文件大约有2000万行,所以当我找到答案时,我想跳到下一个人,而不是继续浏览那个人的所有其他记录 我想按ID和更改日期降序排序,然后按ID设置。对于每个ID,如果我感兴趣的年份大于(或等于)更改年份,则计算婚姻状况并输出ID和婚姻状况。如果没有,则继续下一个记录,直到满足条件。如果没有符合条件的记

我有一个记录婚姻状况变化的文件——身份证、变化类型(婚姻、离婚、丧偶)和变化年份(和月份)。我想计算每个人在任何给定年份的婚姻状况(已婚、离婚、寡妇(呃)、从未结婚)。因为一个人可以经历许多更改,而我的文件大约有2000万行,所以当我找到答案时,我想跳到下一个人,而不是继续浏览那个人的所有其他记录

我想按ID和更改日期降序排序,然后按ID设置。对于每个ID,如果我感兴趣的年份大于(或等于)更改年份,则计算婚姻状况并输出ID和婚姻状况。如果没有,则继续下一个记录,直到满足条件。如果没有符合条件的记录,则婚姻状况=从未结婚

data a;
length type_change $10;
input ID type_change yr_change mnth_change;
cards;
1 marriage 2006 9
1 divorce 2010 5
10 marriage 2005 2
10 divorce 2012 10
10 marriage 2016 8
23 marriage 2017 6
35 marriage 2002 7
35 widow 2013 12
;
run;
2015年,我希望得到: -身份证婚姻状况 -1离婚 -10离婚 -23从未结过婚 -35丧偶


提前谢谢

然后使用retain语句

提取所有ID:

proc sort data=a out=ids(keep= id) nodupkey ;
by id;
run;
生成所有ID所需的所有年份

data years;
set ids;
    must_be_date=2000;

    do i = 1 to 20;
        must_be_date+1;
        output;
    end;

    drop i;
run;
按条件加入:

proc sql;
    create table res as 
        select * 
            from years left join a on years.must_be_date = a.yr_change and a.id = years.id
    ;
run;

proc sort ;
by id must_be_date;
run;
使用保留:

data res;
retain temp "never been married";
set res;
by id must_be_date;
if first.id then temp="never been married";
if type_change="" then type_change = temp;
else temp=type_change;
run;
检查:

data res_2015;
set res;
where must_be_date=2015;
run;
结果表:

+--------------------+----+--------------+-------------+-----------+-------------+
|        temp        | ID | must_be_date | type_change | yr_change | mnth_change |
+--------------------+----+--------------+-------------+-----------+-------------+
| divorce            |  1 |         2015 | divorce     |         . |           . |
| divorce            | 10 |         2015 | divorce     |         . |           . |
| never been married | 23 |         2015 | never been  |         . |           . |
| widow              | 35 |         2015 | widow       |         . |           . |
+--------------------+----+--------------+-------------+-----------+-------------+
/*仅执行一次此排序并保存已排序*/
proc sort data=have out=sorted;
由id yr_更改;
跑
proc sort data=have(keep=id)out=ids nodupkey;
按身份证;
跑
数据步骤1;
集合排序;

其中,yr_change如果跳过的意思是不阅读它们,则不能“跳过”观察结果。但您可以使用IF语句(或其他条件逻辑)忽略它们

使用“保留”和“分组处理”应该可以得到答案

%let year=2015;

data want ;
  set a ;
  by id yr_change mnth_change ;
  length status $20;
  retain status ;
  if first.id then status='never been married ';
  if yr_change <= &year then status=type_change ;
  if last.id;
  keep id status;
run;
如果您可以访问ID的主列表,则可以转换为使用WHERE语句,这可能会减少处理所有记录所需的I/O。例如,将ID列表与婚姻状况更改记录的子集合并

data want;
  merge id_list a(in=in2 where=(yr_change <= &year));
  by id;
  length status $20;
  retain status ;
  if first.id then status='never been married ';
  if in2 then status=type_change ;
  if last.id;
  keep id status;
run;
需要数据;

合并id_列表a(in=in2,其中=(yr_changeDOW循环将允许您计算组的结果。隐式输出将保存为组计算的结果。由于结果取决于您感兴趣的年份,因此您还需要在任何创建的数据集中跟踪该结果

%let YEAR_CUTOFF = 2015;

data want (keep=id status year_cutoff);

  attrib
    id length = 8
    status length=$20 label="Status at year end &YEAR_CUTOFF"
    year_cutoff length = 8
  ;

  retain year_cutoff &YEAR_CUTOFF;

  status = 'never been married';

  do until (last.ID);                                    /* The DOW loop */
    set have (rename=status=status_of_interest);
    by id;
    if year <= &YEAR_CUTOFF then status = status_of_interest;
  end;

  /* No explicit OUTPUT in the step, so,
   * an implicit OUTPUT occurs here at the bottom of the step
   */
run;
%let YEAR\u CUTOFF=2015;
需要数据(保留=身份证状态年份\截止日期);
阿特里布
id长度=8
状态长度=$20 label=“年终状态和年终截止状态”
年份\截止长度=8
;
保留年度截止和年度截止;
状态='从未结婚';
直到(last.ID);/*道循环*/
设置有(重命名=状态=感兴趣的状态);
按身份证;

如果你有一份所有人的名单,那可能已经结婚了吗?我有一份所有人的名单。在这里,我关注的是那些至少结过一次婚的人,因为从未结过婚的人很容易处理。根据抽样的数据和你的代码,我得到了:1-离婚10-离婚35个寡妇。没有23个I-在你改变的地方删除col中的空格umn名称“类型更改”这给了我ID=1的“从未结婚”而不是离婚。我只是编辑以从<更改为>。很抱歉,需要做更多的编辑,但这会给我一个巨大的表来保存和处理。我有1300万人,可以追溯到1900年。我相信他指的是跳过,实际上可以使用where而不是if来避免IO对于效率更高的PDV,建议的解决方案处理了所有2000万条记录,我相信用户希望避免这些记录。注意,如果源是外部数据库,则WHERE只能提供显著的性能改进,在外部数据库中过滤观察结果将减少从数据库到SAS的数据传输。但这将有丢失的风险识别ID=23这样的主题的能力。因此需要提供ID列表的另一个来源。在这一点上,我会与你有所不同。也许对于这样一个狭窄/相对较小的数据集,这是真的。我们已经在磁盘中的数据集上做了基准测试,大约有1000亿条记录,每个记录都有200-300个属性,其中在使用if时,使用which over if是非常重要的。在某些情况下,它可能很重要。访问可用于减少从磁盘读取的I/O块数的索引。磁盘I/O相对于内存/CPU速度的相对速度。使用if状态时,观察范围极广将需要更多内存到内存的传输t、 谢谢大家。我不想处理所有2000万条记录。在“set have(rename=status=status\u of interest)”行中状态是指类型改变吗?如果是,那么ID 1,10是已婚而不是离婚。是的,
status
type\u change
。我可能将其编码为阅读@Tom答案的人工制品。你的
yr\u change
在我的代码中只是
year
,这就是你看到的原因,运行copy/paste ha人类的危险因理解而减轻。
data want;
  merge id_list a(in=in2 where=(yr_change <= &year));
  by id;
  length status $20;
  retain status ;
  if first.id then status='never been married ';
  if in2 then status=type_change ;
  if last.id;
  keep id status;
run;
%let YEAR_CUTOFF = 2015;

data want (keep=id status year_cutoff);

  attrib
    id length = 8
    status length=$20 label="Status at year end &YEAR_CUTOFF"
    year_cutoff length = 8
  ;

  retain year_cutoff &YEAR_CUTOFF;

  status = 'never been married';

  do until (last.ID);                                    /* The DOW loop */
    set have (rename=status=status_of_interest);
    by id;
    if year <= &YEAR_CUTOFF then status = status_of_interest;
  end;

  /* No explicit OUTPUT in the step, so,
   * an implicit OUTPUT occurs here at the bottom of the step
   */
run;