proc sql vs data step для поиска значений из справочной таблицы, которая включает исключения

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

state    city     Good     tax
---------------------------------
all      all      all      0.07
all      all      chicken  0.04
all      jackson  all      0.01
arizona  all      meat     0.02
arizona  phoenix  meat     0.04
arizona  tucson   meat     0.03
hawaii   all      all      0.08
nevada   reno     cigar    0.11
nevada   vegas    cigar    0.13

Теперь предположим, что если я ищу налог на (сигару Nevada Reno), в ссылке существует точное совпадение, поэтому ответ равен 0,11. Но если я ищу (цыпленок Невада-Рено), точного совпадения не существует, но (всех всех цыплят) можно использовать в качестве эталона, и результат будет 0,04.

Можете ли вы предложить логику шага PROC SQL или сопоставления-слияния DATA, которая обрабатывает эту ситуацию?


person user2769563    schedule 11.09.2013    source источник
comment
Каков порядок предпочтения? То есть возьмите вашу таблицу выше и добавьте гавайскую курицу. Вы хотите найти цыпленка Гаваи Гонолулу. Я предполагаю, что вы бы использовали курицу на Гавайях, но неясно, должна ли вся курица быть второй.   -  person DomPazz    schedule 12.09.2013


Ответы (3)


Это немного долго. В таких ситуациях я использую хэш-объект. Итеративно "if/then/else" ваш путь через дерево поиска, пытаясь найти значение.

Я предполагаю, что курица из Гонолулу должна быть в «Гавайи полностью курица», а не «все полностью курица».

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

data taxes;
informat state $8.   
         city $12.     
         Good $12.    
         tax best.;
input state $ city $ good $ tax;
datalines;
all      all      all      0.07
all      all      chicken  0.04
all      jackson  all      0.01
arizona  all      meat     0.02
arizona  phoenix  meat     0.04
arizona  tucson   meat     0.03
hawaii   all      all      0.08
hawaii   all      chicken  0.11
nevada   reno     cigar    0.11
nevada   vegas    cigar    0.13
;;;
run;

data to_look_up;
informat lu_state $8.   
         lu_city $12.     
         lu_Good $12.  ;
input lu_state $ lu_city $ lu_good $;
datalines;
nevada reno cigar
nevada reno chicken
hawaii honalulu chicken
texas  dallas steak
;;;
run;

%macro create_hash(name,key,data_vars,dataset);
declare hash &name(dataset:&dataset);
%local i n d;
%let n=%sysfunc(countw(&key));
rc = &name..definekey(
    %do i=1 %to %eval(&n-1);
    "%scan(&key,&i)",
    %end;
    "%scan(&key,&i)"
);
%let n=%sysfunc(countw(&data_vars));
%do i=1 %to &n;
    %let d=%scan(&data_vars,&i);
    rc = &name..definedata("&d");
%end;
rc = &name..definedone();
%mend;

data lookup;
set to_look_up;
    format tax best.
         state $8.   
         city $12.     
         Good $12. ;

    if _N_ = 1 then do;
        %create_hash(scg,state city good, tax,"taxes");
    end;

    state = lu_state;
    city =  lu_city;
    good = lu_good;
    tax = .;

    rc = scg.find();
    if missing(tax) then do;
        /*No exact match - check if state/good combo exists*/   
        city = "all";
        rc = scg.find();
        if missing(tax) then do;
            /*No state/good combo -- check state only taxes*/
            good = "all";
            rc = scg.find();
            if missing(tax) then do;
                /*Check good only*/
                good = lu_good;
                state = "all";
                rc = scg.find();
                if missing(tax) then do;
                    /*Default taxes*/
                    good = "all";
                    rc = scg.find();
                end;
            end;
        end;
    end;
run;
person DomPazz    schedule 11.09.2013

SQL является идеальным инструментом для объединения этих таблиц, поскольку он является наиболее гибким при объединении данных.
Использование тестовых данных DomPazz;

data taxes;
informat state $8.   
         city $12.     
         Good $12.    
         tax best.;
input state $ city $ good $ tax;
datalines;
all      all      all      0.07
all      all      chicken  0.04
all      jackson  all      0.01
arizona  all      meat     0.02
arizona  phoenix  meat     0.04
arizona  tucson   meat     0.03
hawaii   all      all      0.08
hawaii   all      chicken  0.11
nevada   reno     cigar    0.11
nevada   vegas    cigar    0.13
;;;
run;

data to_look_up;
informat lu_state $8.   
         lu_city $12.     
         lu_Good $12.  ;
input lu_state $ lu_city $ lu_good $;
datalines;
nevada reno cigar
nevada reno chicken
hawaii honalulu chicken
texas  dallas steak
;;;
run;

Приведенный ниже запрос соединяет каждую строку в таблице to_look_up со строками в таблице налогов, где; штат соответствует или штат равен «всем» в таблице налогов, город соответствует или город равен «всем» в таблице налогов, а хорошие совпадения или хорошо равны «всем» в таблице налогов.

Это может привести к тому, что более 1 строки в таблице налогов будут соответствовать строке в таблице to_look_up. Хотя мы можем выбрать наилучшее совпадение, установив приоритет совпадений, т. Е. Состояние совпадения перед состоянием равно «все», и то же самое для города и добра.

Предложение Group By здесь важно. Это должна быть уникальная комбинация переменных в таблице to_look_up. При этом мы можем выбрать лучшее совпадение для каждой строки в таблице to_look_up и исключить все остальные совпадения.

proc sql;
create table taxes_applied  as

select  *

/*  Prioritise state, city and good matches.                   */
,   case    when to_look_up.lu_state    eq  taxes.state then 2
            when 'all'                  eq  taxes.state then 1
    end                                 as  match_state

,   case    when to_look_up.lu_city     eq  taxes.city  then 2
            when 'all'                  eq  taxes.city  then 1
    end                                 as  match_city

,   case    when to_look_up.lu_good     eq  taxes.good  then 2
            when 'all'                  eq  taxes.good  then 1
    end                                 as  match_good

from    to_look_up

/*  join taxes table on matching state, city and good or matching 'all' rows.  */
left    join
    taxes
on  (       to_look_up.lu_state eq  taxes.state
        or  'all'               eq  taxes.state
    )
and (       to_look_up.lu_city  eq  taxes.city
        or  'all'               eq  taxes.city
    )   
and (       to_look_up.lu_good  eq  taxes.good
        or  'all'               eq  taxes.good
    )   


/*  Process for each row in to_look_up table.  */ 
group   by  to_look_up.lu_state
        ,   to_look_up.lu_city
        ,   to_look_up.lu_good

/*  Select best match.   */ 
having  match_state eq  max (match_state)
and     match_city  eq  max (match_city)         
and     match_good  eq  max (match_good)

order   by  to_look_up.lu_state
        ,   to_look_up.lu_city
        ,   to_look_up.lu_good
        ,   match_state
        ,   match_city
        ,   match_good      
;

quit;       

Соединения, подобные этому, можно использовать для создания промежуточных итогов в сводных таблицах.

person Chris    schedule 12.09.2014

Если это то, что вам нужно сделать только один раз (я имею в виду не непрерывный процесс), то, вероятно, простым выходом может быть разделение вашего набора данных на несколько наборов данных. В одном наборе данных будут все наблюдения, в которых все «все в состоянии», «наблюдение» и «хорошо». У другого было бы только государство или город или добро только как Все. Другой набор данных может быть комбинацией двух ВСЕХ: штат/город, город/хорошо или штат/хорошо. Я думаю, что в общей сложности 8 наборов данных (включая набор данных без всех в любой из переменных. Затем, когда вы знаете, какие переменные имеют все, вы можете соответствующим образом объединить. Например. Для набора данных со штатом, городом, хорошо, у вас может быть налог 0,07 без какого-либо слияния. Для набора данных со штатом и городом = «Все» вам нужно объединить только хорошо. Единственный другой способ / вариант сделать это imo - создать три новых набора данных с двумя переменными, где var1 = все во всех случаях и var2 = все названия городов (несколько наблюдений)/все названия штатов (несколько наблюдений)/все названия товаров (несколько наблюдений), а затем объединяются с вашим исходным набором данных в var1, чтобы иметь несколько строк в вашем исходном наборе данных вместо ВСЕХ

person zemini    schedule 11.09.2013
comment
Этот ответ выиграет от абзацев и форматирования кода. - person JustinJDavies; 22.08.2014