Удаление строк между двумя значениями в SAS - PullRequest
3 голосов
/ 26 февраля 2020

Для следующих данных я пытаюсь отфильтровать строки каждого идентификатора группы на основе этих условий:

  • После каждой строки с type='B' and value='Y' выполните следующее
    • Удалить строки до следующей строки имеют type='F' and value='Y'.
  • Если B = 'Y отсутствует, сохраните их все (например, id = 002)

Можем ли мы создать переменную flag, как показано в моем наборе данных want? так что я могу фильтровать флаг = 'Y'?

Имею

  ID     Type     Date         Value
  001     F       1/2/2018      Y
  001     B       1/3/2018
  001     B       1/4/2018      Y
  001     B       1/5/2018
  001     B       1/6/2018
  001     F       1/6/2018      Y
  001     B       1/6/2018      
  001     B       1/7/2018
  001     B       1/8/2018      Y
  001     B       1/8/2018
  001     B       1/9/2018
  002     F       1/2/2018      Y
  002     B       1/3/2018
  002     B       1/4/2018

Хочу

  ID     Type     Date         Value   Flag
  001     F       1/2/2018      Y       Y
  001     B       1/3/2018              Y
  001     B       1/4/2018      Y       Y 
  001     B       1/5/2018
  001     B       1/6/2018
  001     F       1/6/2018      Y       Y
  001     B       1/6/2018              Y
  001     B       1/7/2018              Y
  001     B       1/8/2018      Y       Y 
  001     B       1/8/2018
  001     B       1/9/2018
  002     F       1/2/2018      Y       Y
  002     B       1/3/2018              Y
  002     B       1/4/2018              Y

Я пытался сделать следующее

data F;
set have;
where Type='F';run;

data B;
 set have;
 where Type='B';run;

 proc sql;
  create table all as select
  a.* from B as b
  inner join F as f
  on a.id=b.id
  and b.date >= a.date;
quit;

Сюда входят все строки из моего набора данных. Любая помощь очень ценится.

Ответы [ 3 ]

1 голос
/ 26 февраля 2020

У меня есть решение, но оно не самое элегантное (и может не охватывать угловые случаи). Если у кого-то есть лучшее решение, поделитесь.

Во-первых, чтобы создать набор данных на тот случай, если кто-то еще захочет испытать его:

Data work.have;
  input @01 ID 3.
        @05 Type $1.
        @07 Date date7.
        @18 Value $1.;
  format ID 3.
         Type $1.
         Date date11.
         Value $1.; 
  datalines;
001 F '02Jan18'n Y
001 B '03Jan18'n  
001 B '04Jan18'n Y
001 B '05Jan18'n  
001 B '06Jan18'n 
001 F '06Jan18'n Y
001 B '06Jan18'n 
001 B '07Jan18'n 
001 B '08Jan18'n Y
001 B '08Jan18'n 
001 B '09Jan18'n 
002 F '02Jan18'n Y
002 B '03Jan18'n 
002 B '04Jan18'n 
;
run;

Решение: Я основываюсь на вашем отредактированном предложении о создании переменной флага.

Data Flag;
  set work.have;

  if Type = 'B' and Value = 'Y' then
    flag + 1;
  if Type = 'F' then
    flag = 0;

  if Value ne 'Y' and flag = 1 then delete;
run;

Значение переменной флага по умолчанию равно 0.

Первое условие IF-Then идентифицирует строки Type B = 'Y' и помечает их как 1, а также сохраняет этот флаг для последующих строк.

Второе условие IF-Then идентифицирует строку type = 'F' и сбрасывает флаг в 0

. Условие Last If-Then отбрасывает все строки с флагом = 1, кроме первого вхождения, которое Тип B = 'Y' строк.

Надеюсь, это относится к вашей проблеме.

1 голос
/ 26 февраля 2020

Критерии для вычисления состояния строки как части смежной подгруппы (назовите это «серией строк») внутри группы ID относительно просты, но может возникнуть скомпрометированное состояние или указать его, если некоторые происходят забавные случаи данных:

  • два или более BY перед FY (дополнительное окончание цикла)
  • два или более FY перед BY ('запуск запуска' в пределах цикла )
  • первая строка в группе не FY («запуск запуска» не первая в группе)
data want(drop=run_:);
  SET have;
  BY id;

  run_first = (type='F' and value='Y');
  run_final = (type='B' and value='Y');

  * set flag state at criteria for start of contiguous sub-group criteria;
  run_flag + run_first;

  if first.id and NOT run_flag then
    put 'WARNING: first row in group ' id= ' is not F Y, this may be incorrect';

  if run_flag > 1 and run_first then 
    put 'WARNING: an additional F Y before a B Y at row ' _n_;

  if run_flag then
    OUTPUT;

  if run_flag = 0 and run_final then 
    put 'WARNING: an additional B Y before a F Y at row ' _n_;

  * reset flag at criteria for contiguous sub-group;
  if last.id or run_final then 
    run_flag = 0;
run;
1 голос
/ 26 февраля 2020

Так же, как и Ричард, я не совсем понимаю, каковы критерии фильтрации.

Я вижу одну проблему с вашим объединением. вы использовали. * в своем операторе выбора, но "b" и "f" в качестве псевдонимов набора данных. это не будет работать, так как никакой набор данных не был назначен псевдониму "a".

Правильный путь будет следующим:

proc sql;
  create table all as 
  select b.* from B as b
  inner join F as f
  on b.id=f.id
  and b.date >= f.date;
quit;

Однако даже тогда я не верю, что внутреннее соединение - это правильный способ решения вашей проблемы. Пожалуйста, сообщите нам ваше условие фильтрации?

...