Простой Do-Loop: проверка того, совпадают ли данные в предыдущей строке

Я привык программировать в MatLab, SAS для меня все еще в новинку при использовании Do Loops. Моя цель - проверить строку выше/перед текущей строкой, чтобы увидеть, совпадают ли номера текущей и предыдущей строки. Если векторные данные над первой строкой совпадают с текущей строкой, я хочу иметь возможность проверить это.

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;

Для меня «Текущий» должен ссылаться на i-ю ячейку вектора «Число», а «Предыдущий» должен ссылаться на i-ю ячейку перед ячейкой «Текущая». Кто-нибудь может мне помочь?


person Sarah Reinke    schedule 04.04.2014    source источник
comment
Функция для получения значения предыдущего наблюдения (строки) — lag(). Так что вы бы использовали current=num; prev=lag(num);. Ссылка на «ячейки» в таблице SAS сбивает с толку, данные упорядочиваются в переменных и наблюдениях (аналогично столбцам и строкам соответственно).   -  person mjsqu    schedule 04.04.2014
comment
Как насчет получения следующего наблюдения (строки), как будет называться эта функция?   -  person Sarah Reinke    schedule 04.04.2014
comment
Это будет lead()   -  person mjsqu    schedule 04.04.2014
comment
lead() не существует в базовом SAS, а lag() не имеет точного доступа к данным предыдущей строки. Его можно использовать так, как если бы он это делал в некоторых случаях, но в этом случае, вероятно, лучше его не использовать. В частности, он НЕ работает так, как вы ожидаете, внутри условных операторов, поэтому, если вы по какой-то причине используете lag, вам следует быть осторожным, чтобы присвоить prev безоговорочно.   -  person Joe    schedule 04.04.2014
comment
Справедливое замечание @Joe, я, должно быть, перепутал его с lead(), который существует в вариантах SQL, отличных от SAS. Я бы также повторил оговорки по поводу lag(), по возможности стараюсь их избегать.   -  person mjsqu    schedule 04.04.2014
comment
Я полностью исключил его из цикла выполнения и переименовал в переменную. Prev_Policy = lag(PolicyNbr) - после сортировки политик. Есть ли другие варианты, похожие на lead() в базовом SAS?   -  person Sarah Reinke    schedule 04.04.2014


Ответы (3)


SAS существенно отличается от Matlab тем, что в Matlab есть векторы и матрицы, а в базовом SAS их нет. Базовый 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, чтобы получить именно то, что вы хотите, см. документацию для получения более подробной информации.

Вы также можете очень легко выполнить слияние со следующей строкой.

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

Однако вам нужно переименовать переменные, поступающие из второго набора данных, поскольку в противном случае они перезапишут переменные первого набора данных, и используйте параметр keep, чтобы сохранить только те, с которыми вы работаете, а keep использует ИСХОДНОЕ имя, а не переименованное имя. Затем в ...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 будет означать, что строка уникальна по номеру строки (по крайней мере, в порядке набора данных - если он не отсортирован, то же число может появиться в другом месте, но не непосредственно перед/после этой строки). first.num — это переменная 1/0, указывающая, имела ли предыдущая строка другое (1) или такое же (0) значение для num; last.num — это переменная 1/0, которая указывает, имеет ли следующая строка другое (1) или такое же (0) значение для num. Их можно использовать для любой переменной (так что first.id first.name и т. д. работают для этих переменных), если они каким-то образом появляются в операторе by; и если у вас есть оператор by с несколькими переменными, они сбрасываются каждый раз, когда сбрасывается любая переменная слева от этой переменной.

person Joe    schedule 04.04.2014
comment
Сначала я не заметил, что num была единственной интересующей меня переменной, поэтому последняя часть (отредактировано), вероятно, является наиболее полезным ответом, хотя было бы очень легко адаптировать и слияние вперед. - person Joe; 04.04.2014
comment
Вы также можете выполнить proc sql соединение набора данных с самим собой на a.num = b.num + 1. - person Alex A.; 04.04.2014
comment
@ Алекс Не совсем так. Во-первых, SQL не учитывает порядок строк, поэтому точный запрос на просмотр порядка строк не позволяет вам использовать SQL, если у вас нет переменной номера строки, отдельной от num. Во-вторых, это конкретное предложение идет вразрез с запросом (если a.num=b.num, соединенное с (воображаемой?) переменной номера строки). - person Joe; 04.04.2014
comment
Я пошутил над num там. Я имел в виду, что вы можете сохранить _n_ заранее и присоединиться к a._n_ = b._n_ + 1 (не num) и сравнить num. - person Alex A.; 04.04.2014
comment
Да, вы могли бы, но кодировать это было бы не быстрее и не проще, чем обычное слияние шага данных вперед, поскольку вам понадобится шаг данных или, по крайней мере, представление шага данных в дополнение. - person Joe; 04.04.2014
comment
Правда. +1 к вашему ответу. - person Alex A.; 04.04.2014

Я прилагаю код, который проверяет значение предыдущей строки и проверяет текущее значение и обновляет столбец, если есть изменения.

Позвольте мне знать, если это помогает.

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;
person Vinith Srinivas    schedule 04.05.2017

То же самое может быть достигнуто и с помощью функции запаздывания.

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;
person Vinith Srinivas    schedule 04.05.2017