2016-11-13 2 views
0

Я ответил на вопрос SAS несколько минут назад и понял, что существует обобщение, которое может быть более полезным, чем этот (here). Я уже не видел этого вопроса в StackOverflow.Сохранение или удаление группы наблюдений на основе характеристики BY-group

Общий вопрос: как вы можете обрабатывать и хранить всю BY-группу на основе какой-либо характеристики BY-группы, которую вы, возможно, не знаете, пока не просмотрите все наблюдения в группе?

Используя входные данные, аналогичную из предыдущего вопроса:

* For some reason, we are tasked with keeping only observations that 
* are in groups of ID_1 and ID_2 that contain at least one obs with 
* a VALUE of 0.; 
* In the following data, the following ID and ID_2 groups should be 
* kept: 
* A 2 (2 obs) 
* B 1 (3 obs) 
* B 3 (2 obs) 
* B 4 (1 obs) 
* The resulting dataset will have 8 observations.; 
data x; 
    input id $ id_2 value; 
datalines; 
A 1 1 
A 1 1 
A 1 1 
A 2 0 
A 2 1 
B 1 0 
B 1 1 
B 1 3 
B 2 1 
B 3 0 
B 3 0 
B 4 0 
C 2 4 
; 
run; 

ответ

0

Дважды решение петли DoW:

data have; 
    input id $ id_2 value; 
datalines; 
A 1 1 
A 1 1 
A 1 1 
A 2 0 
A 2 1 
B 1 0 
B 1 1 
B 1 3 
B 2 1 
B 3 0 
B 3 0 
B 4 0 
C 2 4 
; 
run; 

data want; 
do _n_ = 1 by 1 until(last.id_2); 
    set have; 
    by id id_2; 
    flag = sum(flag,value=0); 
end; 
do _n_ = 1 to _n_; 
    set have; 
    if flag then output; 
end; 
drop flag; 
run; 

Я проверил это против point подхода с использованием 55m ~ рядов и не нашли заметной разницы в производительности. Dataset используется:

data have; 
do ID = 1 to 10000000; 
    do id_2 = 1 to ceil(ranuni(1)*10); 
     do value = floor(ranuni(2) * 5); 
      output; 
     end; 
    end; 
end; 
run; 
+0

Спасибо и благодаря @Reeza введения меня к петле DoW. – vknowles

+0

К сожалению, пропустили ограничение на редактирование. Во всяком случае, @ user667489, ваш код работает, и я ценю элегантность, но я думаю, что это не самый эффективный. Поскольку спецификации говорят, что мы либо сохраняем, либо отклоняем всю группу, нет необходимости перебирать ** ** набор данных дважды. Вот почему я использовал опцию ** POINT = **, поэтому он только обращается ко второму ** SET **, когда это необходимо. У меня также есть вопрос: Является ли ** SUM (флаг, значение = 0) ** более эффективным, чем оператор ** IF **, и простое присвоение символа от 1 до ** **? – vknowles

+0

Я подозреваю, что вы обнаружите, что дополнительное полное последовательное чтение стоит того же или меньше против случайного доступа через 'point'. 'sum' здесь удобна, так как по умолчанию значения по умолчанию отсутствуют в начале каждой группы. – user667489

0

Мой ответ не может быть наиболее эффективным, особенно для больших наборов данных, и я заинтересован в том, чтобы другие возможные ответы. Вот оно:

* For some reason, we are tasked with keeping only observations that 
* are in groups of ID_1 and ID_2 that contain at least one obs with 
* a VALUE of 0.; 
* In the following data, the following ID and ID_2 groups should be 
* kept: 
* A 2 (2 obs) 
* B 1 (3 obs) 
* B 3 (2 obs) 
* B 4 (1 obs) 
* The resulting dataset will have 8 observations.; 
data x; 
    input id $ id_2 value; 
datalines; 
A 1 1 
A 1 1 
A 1 1 
A 2 0 
A 2 1 
B 1 0 
B 1 1 
B 1 3 
B 2 1 
B 3 0 
B 3 0 
B 4 0 
C 2 4 
; 
run; 

* I realize the data are already sorted, but I think it is better 
* not to assume they are.; 
proc sort data=x; 
    by id id_2; 
run; 

data obstokeep; 
    keep id id_2 value; 
    retain startptr haszero; 

    * This SET statement reads through the dataset in sequence and 
    * uses the CUROBS option to obtain the observation number. In 
    * most situations, this will be the same as the _N_ automatic 
    * variable, but CUROBS is probably safer.; 
    set x curobs=myptr; 
    by id id_2; 

    * When this is the first observation in a BY-group, save the 
    * current observation number (pointer). 
    * Also initialize a flag variable that will become 1 if any 
    * obs contains a VALUE of 0; 
    * The variables are in a RETAIN statement, so they keep their 
    * values as the SET statement above is executed for each obs 
    * in the BY-group.; 
    if first.id_2 
    then do; 
     startptr=myptr; 
     haszero=0; 
    end; 

    * This statement is executed for each observation. We check 
    * whether VALUE is 0 and, if so, record that fact.; 
    if value = 0 
    then haszero=1; 

    * At the end of the BY-group, we check to see if there were 
    * any observations with VALUE = 0. If so, we go back using 
    * another SET statement, re-read them via direct access, and 
    * write them to the output dataset. 
    * (Note that if VALUE order is not relevant, you can gain a bit 
    * more efficiency by writing the current obs first, then going 
    * back to get the rest.); 
    if last.id_2 and haszero 
    then do; 
     * When LAST and FIRST at the same time, there is only one 
     * obs, so no need to backtrack, just output and go on.; 
     if first.id_2 
     then output obstokeep; 
     else do; 
      * Here we assume that the observations are sequential 
      * (which they will be for a sequential SET statement), 
      * so we re-read these observations using another SET 
      * statement with the POINT option for direct access 
      * starting with the first obs of the by-group (the 
      * saved pointer) and ending with the current one (the 
      * current pointer).; 
      do i=startptr to myptr; 
       set x point=i; 
       output obstokeep; 
      end; 
     end; 
    end; 

run; 
+0

Вы должны смотреть в циркуляционном http://support.sas.com/resources/papers/proceedings09/038-2009.pdf DOW – Reeza

0
proc sql; 
    select a.*,b.value from (select id,id_2 from have where value=0)a left join have b 
    on a.id=b.id and a.id_2=b.id_2; 
quit; 
+0

Благодарим вас за это решение SQL. Однако у него есть небольшая проблема. Он возвращает слишком много строк, потому что иногда группа BY имеет более одной строки с ** значением = 0 **. В этом случае он возвратил две дополнительные строки для ** id = "B", id_2 = 3 **. Я настраивал его так, чтобы он работал: 'code'proc sql; выберите a.id, a.id_2, b.value \t из (выберите идентификатор, ID_2, граф (*) \t \t \t из имеют \t \t \t, где значение = 0 \t \t \t группы по идентификатору, ID_2) a \t \t левое соединение \t есть b на a.id = b.id и a.id_2 = b.ID_2; quit; 'code' – vknowles

+0

Я проверил эффективность, выполнив это и код @ user667489 на 27,5 миллионов строк, а PROC SQL занял секунды 51.49 (21.01 CPU) против 13.37 (4.87 CPU) секунд для двойного DoW-подхода. PROC SQL сортировал данные, что могло бы объяснить большую разницу. – vknowles