【问题标题】:Simple Do-Loop: Checking to see if the row data before is the same简单的 Do-Loop:检查之前的行数据是否相同
【发布时间】:2014-04-04 14:37:19
【问题描述】:

我习惯于在 MatLab 中编码,使用 Do Loops 时 SAS 对我来说还是个新手。我的目标是检查当前行上方/之前的行,以查看当前行号和上一个行号是否匹配。如果第一行上方的矢量数据与当前行匹配,我希望能够检查这一点。

Num(列向量)= 123,123,134,156,290,348,596,569,569

/*---- Do-Loop ----*/
data full;
set work.Test;
 count=_N_;

do i=1 to count;
    Current = Num(i);
    Prev = Num(i-1);
end;

run;

对我来说,“Current”应该引用向量“Num”的第 i 个单元格,而“Prev”应该引用“Current”单元格之前的第 i 个单元格。有人可以帮帮我吗?

【问题讨论】:

  • 检索前一个观察值(行)的函数是lag()。所以你会使用current=num; prev=lag(num);。在 SAS 表中引用“单元格”会使事情变得混乱,数据按变量和观察值排列(分别类似于列和行)。
  • 那么检索下一个观察值(行)怎么样,该函数会被调用什么?
  • 那就是lead()
  • lead() 在基本 SAS 中不存在,lag() 不会精确访问前一行的数据。它可以像在某些情况下那样使用,但在这种情况下,最好不要使用它。特别是,它在条件语句中不会按照您期望的方式工作,因此如果您出于某种原因使用lag,则应小心无条件地分配 prev。
  • 公平点@Joe,我一定把它与 SQL 的非 SAS 变体中存在的 lead() 混淆了。我也会回应对lag() 的保留意见,我会尽可能避免它。

标签: loops sas do-loops


【解决方案1】:

SAS 与 Matlab 的显着不同在于,Matlab 有向量和矩阵,而基本 SAS 没有。 Base SAS 比矩阵语言更接近 SQL;您操作行内的列。如果您想将 SAS 视为一种矩阵语言,请查看您是否拥有 SAS-IML 许可(PROC SETINIT; RUN; 应该向您显示您拥有的许可,或询问您的 SAS 管理员)。该语言与 Matlab 非常相似。

至于使用基础 SAS 来解决这个问题,你做什么取决于你的目标。如果只是将每一行与上一行 [或下一行,因为这两个目标相同] 进行比较,您可以使用 PROC COMPARE 轻松完成此操作。

例如:

data class_one/view=class_one;
set sashelp.class(firstobs=2);
run;

proc compare base=sashelp.class compare=class_one out=class_compare;
run;

PROC COMPARE 有很多选项可以完全满足您的需求,请参阅documentation 了解更多详细信息。

您还可以非常轻松地对下一行进行匹配合并。

data want;
merge sashelp.class(in=a) sashelp.class(in=b firstobs=2 rename=(...) keep=...);
... do stuff ...
run;

不过,您需要重命名来自第二个数据集的变量,否则它们会覆盖第一个数据集的变量,并使用 keep 选项仅保留您正在使用的变量 - 和 @987654329 @ 使用原始名称,而不是重命名的名称。然后在...do stuff... 中输入您用来比较该行和下一行的任何代码。如果“上一行”很重要(即,您希望行 [2..n] 作为最终输出,而不是 [1..n-1]),则将 firstobs=2 移动到第一个数据集。 (这告诉它跳过第一次观察,因此一个数据集从 1 开始,一个从 2 开始,然后都迭代超过该值。)

如果您只是检查行号,这会更容易一些。你可以这样做:

data want;
set have;
by num notsorted; *notsorted is needed unless you do expect it to be in numeric order;
if first.num then first_obs_for_rownum=1;
if last.num then last_obs_for_rownum=1;
run;

显然你可以用那些then 子句做其他事情,我只是想弄清楚他们做了什么。 if first.num and last.num 将表明一行是唯一的行号(至少,按照数据集上的顺序 - 如果它未排序,则相同的 num 可能出现在其他地方,但不会出现在该行之前/之后)。 first.num 是一个 1/0 变量,指示上一行对于 num 是否具有不同 (1) 或相同 (0) 值; last.num 是一个 1/0 变量,它指示下一行对于 num 是具有不同 (1) 还是相同 (0) 值。这些可以用于任何变量(所以first.idfirst.name 等对这些变量有效),只要它们以某种方式出现在by 语句中;如果您有多个变量 by 语句,则每次重置该变量左侧的任何变量时都会重置它们。

【讨论】:

  • 一开始我并没有注意到 num 是唯一感兴趣的变量,所以最后一部分(已编辑)可能是最有用的答案,尽管它很容易适应向前合并。
  • 您还可以在a.num = b.num + 1 上将数据集与自身进行proc sql 连接。
  • @Alex 不是。首先,SQL 不尊重行顺序,因此查看行顺序的确切请求会阻止您使用 SQL,除非您有一个与 num 分开的行号变量。其次,这个特定的建议与请求背道而驰(如果 a.num=b.num,加入(想象的?)行号变量)。
  • 我在那里搞砸了num。我的意思是你可以提前存储_n_ 并加入a._n_ = b._n_ + 1(而不是num)并比较num
  • 嗯,你可以,但它不会比常规数据步骤合并更快或更容易编码,因为您还需要一个数据步骤或至少一个数据步骤视图。
【解决方案2】:

我附上了一个代码,它检查前一行的值并检查当前值并在有变化时更新一列。

如果这有帮助,请告诉我。

Data Me;     
Input Pid Visit Dose;    
Cards;    
1 1 2    
1 2 3    
1 3 4    
1 4 4    
;
Run;

Data Me;
Set Me;
Length Dose_Change_Why $20.;
Dose_Change_Why = " ";
Run;

%Macro Chg;

Proc Sql;
Select Count(Dose) into:Cnt From Me;
Quit;

Data _null_;
Cnt = "&Cnt.";
Call Symput('Cnts', Compress(Trim(Cnt)));
Run;

Proc Sql;
Select Dose into:Dos1-:Dos&Cnts. From Me;
Quit;

%Do i = 1 %To &Cnt.-1;

Data _null_;
i = &i.;
j = &i. + 1;
Call symput('j', Compress(Trim(j)));
Run;

%If "&&Dos&i." ne "&&Dos&j." %Then %Do;
%Put &&Dos&i. &&Dos&j.;
Proc Sql;
Update Me set Dose_Change_Why = "Reason for change" Where Visit = &j.;
Quit;
%End;

%End;

%Mend;

%Chg;

Proc Print;
Run;

【讨论】:

    【解决方案3】:

    同样可以通过滞后功能实现。

    Data Me; 
    Input Pid Visit Dose;
    Cards;
    1 1 2
    1 2 3
    1 3 4
    1 4 4
    ;
    Run;
    
    Data Me(Drop=Dose1); 
    Set Me;
    Dose1 = Lag(Dose);
    If _n_ gt 1 and Dose ne Dose1 Then Why = "Reason";
    Run;
    

    【讨论】:

      猜你喜欢
      • 2018-03-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-05
      • 1970-01-01
      • 2014-07-15
      • 1970-01-01
      相关资源
      最近更新 更多