Ранжирование различных комбинаций в SQL - PullRequest
0 голосов
/ 31 мая 2019

Я работаю с данными аптек и пытаюсь классифицировать использование трех конкретных препаратов (A, B, C) среди большой группы пациентов.Короче говоря, я хочу выяснить 12 лучших комбинаций этих препаратов, которые люди используют.Так, например, пациент 1 может принимать лекарства A + B,
пациент 2 принимает A + C, пациент 3 принимает B + C, пациент 4 принимает A + B и так далее.Я немного покопался, и есть 25 возможных комбинаций для ранга.Я хочу, чтобы мой вывод выглядел примерно так:

enter image description here

Таблицы, с которыми я работаю, выглядят так: enter image description here

В настоящее время я разбиваю наркотики на различные комбинированные группы, выполняя что-то вроде этого:

select distinct concat(substance_name, dosage, unit) as Drug_Dose_Combo,
count(distinct user_id) as Patients 
from pharmacy_data a join drug_reference_table b 
on a.drug_code=b.drug_code 
group by 1 
order by 2 desc

Однако, это кажется очень неэффективным, поэтому я ищу лучший способстроит это.Мне не обязательно использовать здесь rank (), я просто хочу, чтобы вывод был похож на то, что я обрисовал выше.

Ответы [ 2 ]

0 голосов
/ 01 июня 2019

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

Первый шаг в таком анализе - это взять данные аптеки и для каждого user_id определить наборы из 1, 2 и 3 drug_dose комбинаций, в которых они участвуют, поскольку вы, возможно, захотите сделать тот же анализ на substance_name, drug_name и / или drug_code Я собираюсь бросить в него кухонную раковину и сделать все четыре. Не зная, какая у вас база данных, я собираюсь использовать SQL Server 2017 для этого примера, хотя используемые концепции применимы к таким базам данных, как Oracle, MySQL, PostgreSQL и другим, хотя синтаксис может отличаться.

Чтобы создать drug_code и другие комбинации, я сначала присоединю таблицу pharmacy_data к таблице drug_reference, а затем использую рекурсивный запрос к составным данным:

with usage_info as (
  select pd.user_id
       , dr.drug_code
       , dr.drug_name
       , dr.substance_name
       , concat(dr.substance_name,dr.dosage,dr.unit) drug_dose
    from pharmacy_data pd
    join drug_reference dr
      on dr.drug_code = pd.drug_code
), recur(user_id, combo_id, dc_combo, dc_combo_size, dn_combo, sn_combo, dd_combo, last_dc) as (
  -- Anchor part
  select user_id
       , cast(cast(drug_code as binary(4)) as varbinary(max))
       , cast(drug_code as varchar(max))
       , 1
       , cast(drug_name as varchar(max))
       , cast(substance_name as varchar(max))
       , cast(drug_dose as varchar(max))
       , drug_code
    from usage_info

  union all
  -- Recursive Part
  select prev.user_id
       , prev.combo_id+cast(curr.drug_code as binary(4))
       , prev.dc_combo+','+cast(curr.drug_code as varchar(max))
       , prev.dc_combo_size+1
       , prev.dn_combo+','+curr.drug_name
       , prev.sn_combo+','+curr.substance_name
       , prev.dd_combo+','+curr.drug_dose
       , curr.drug_code
    from recur prev
    join usage_info curr
      on prev.user_id = curr.user_id
     and prev.last_dc < curr.drug_code
     and prev.dc_combo_size < 3 -- Maximum combination size
)

Выбор из приведенных выше общих табличных выражений для данных, представленных в вашем вопросе:

select * from recur;

показывает, что некоторые нарушения в группировках для столбцов dn_combo, sn_combo и, возможно, dd_combo, например, существуют dn_combo s для 'CAZERTA, BEXERA' и 'BEXERA, CAZERTA', которые действительно должны быть эквивалентным

Чтобы исправить это, я нормализую комбинации, разделив их и перекомпоновав в отсортированном порядке. В процессе я также буду дедуплицировать любой экземпляр, в котором user_id может иметь два или более эквивалентных, но не идентичных продукта, например. две разные дозы одного и того же лекарства:

, combos as (
select user_id
     , combo_id
     , dc_combo
     , dc_combo_size
     , -- Normalize and deduplicate Drug_Name combos
       (select string_agg(value,',') within group (order by value)
          from (select distinct value from string_split(dn_combo,',')) dn
       ) dn_combo
     , (select count(distinct value) from string_split(dn_combo,',')) dn_combo_size
     , -- Normalize and deduplicate Substance_Name combos
       (select string_agg(value,',') within group (order by value)
          from (select distinct value from string_split(sn_combo,',')) sn
       ) sn_combo
     , (select count(distinct value) from string_split(sn_combo,',')) sn_combo_size
     , -- Normalize and deduplicate Drug_Dose combos
       (select string_agg(value,',') within group (order by value)
          from (select distinct value from string_split(dd_combo,',')) ddc
       ) dd_combo
     , (select count(distinct value) from string_split(dd_combo,',')) dd_combo_size
  from recur
)

Теперь, хотя вы можете просто выбрать count(user_id) over (partition by <grouping_column>), чтобы получить частоту встречаемости каждой комбинации лекарств, эти цифры могут быть завышены. Например, если ваши данные имеют дополнительные user_id из 999 с drug_code с 50, 100, 200 и 350 (это две разные дозы BEXERA вместе с AXIOM и CAZERTA), то user_id 999 будет отображаться несколько раз для каждой комбинации, которая включает в себя BEXERA. В зависимости от типа вашей базы данных вы можете просто выбрать count(DISTINCT user_id) over (partition by <grouping_column>), но с SQL Server 2017 он не позволяет использовать отдельный оператор в аналитических функциях. </shrug> Мы все еще можем сделать это, просто сделав еще один шаг для определения уникальных значений в группе. Введите Common Table combo2, где мы вычисляем номера строк в разных разделах:

, combo2 as (
select user_id
     , combo_id
     , dc_combo
     , dc_combo_size
     , row_number() over (partition by dc_combo, user_id order by dc_combo) dc_uid_rn
     , dn_combo
     , dn_combo_size
     , row_number() over (partition by dn_combo, user_id order by dc_combo) dn_uid_rn
     , row_number() over (partition by dn_combo, dc_combo order by user_id) dn_combo_rn
     , sn_combo
     , sn_combo_size
     , row_number() over (partition by sn_combo, user_id order by dc_combo) sn_uid_rn
     , row_number() over (partition by sn_combo, dc_combo order by user_id) sn_combo_rn
     , dd_combo
     , dd_combo_size
     , row_number() over (partition by dd_combo, user_id order by dc_combo) dd_uid_rn
     , row_number() over (partition by dd_combo, dc_combo order by user_id) dd_combo_rn
  from combos
)

И, наконец, вычислим количество, которых у нас есть два типа. Столбцы uid_cnt представляют собой количество различных user_id с для каждой комбинации, а столбцы combo_cnt указывают количество различных комбинаций drug_code, составляющих менее гранулированные группировки:

select user_id
     , combo_id
     , dc_combo
     , dc_combo_size
     , count(case dc_uid_rn when 1 then 1 end) over (partition by dc_combo) dc_uid_cnt
     , dn_combo
     , dn_combo_size
     , count(case dn_uid_rn when 1 then 1 end) over (partition by dn_combo) dn_uid_cnt
     , count(case dn_combo_rn when 1 then 1 end) over (partition by dn_combo) dn_combo_cnt
     , sn_combo
     , sn_combo_size
     , count(case sn_uid_rn when 1 then 1 end) over (partition by sn_combo) sn_uid_cnt
     , count(case sn_combo_rn when 1 then 1 end) over (partition by sn_combo) sn_combo_cnt
     , dd_combo
     , dd_combo_size
     , count(case dd_uid_rn when 1 then 1 end) over (partition by dd_combo) dd_uid_cnt
     , count(case dd_combo_rn when 1 then 1 end) over (partition by dd_combo) dd_combo_cnt
  from combo2
  order by dn_combo, dd_combo

Все вместе с моими дополнительными образцами данных приведенный выше код приводит к следующей таблице . Чтобы увидеть его в действии, см. SQL Fiddle :

| user_id |    dc_combo | dc_combo_size | dc_uid_cnt |             dn_combo | dn_combo_size | dn_uid_cnt | dn_combo_cnt |                        sn_combo | sn_combo_size | sn_uid_cnt | sn_combo_cnt |                                        dd_combo | dd_combo_size | dd_uid_cnt | dd_combo_cnt |
|---------|-------------|---------------|------------|----------------------|---------------|------------|--------------|---------------------------------|---------------|------------|--------------|-------------------------------------------------|---------------|------------|--------------|
|       3 |         200 |             1 |          2 |                AXIOM |             1 |          4 |            3 |                           nsaid |             1 |          4 |            3 |                                       nsaid10mg |             1 |          2 |            1 |
|     999 |         200 |             1 |          2 |                AXIOM |             1 |          4 |            3 |                           nsaid |             1 |          4 |            3 |                                       nsaid10mg |             1 |          2 |            1 |
|     175 |         300 |             1 |          1 |                AXIOM |             1 |          4 |            3 |                           nsaid |             1 |          4 |            3 |                                       nsaid25mg |             1 |          1 |            1 |
|       1 |          25 |             1 |          1 |                AXIOM |             1 |          4 |            3 |                           nsaid |             1 |          4 |            3 |                                        nsaid5mg |             1 |          1 |            1 |
|     999 |     200,350 |             2 |          1 |         AXIOM,BEXERA |             2 |          3 |            5 |                 nsaid,potassium |             2 |          3 |            5 |                         nsaid10mg,potassium12mg |             2 |          1 |            1 |
|     999 |  50,200,350 |             3 |          1 |         AXIOM,BEXERA |             2 |          3 |            5 |                 nsaid,potassium |             2 |          3 |            5 |           nsaid10mg,potassium12mg,potassium20mg |             3 |          1 |            1 |
|     999 |      50,200 |             2 |          1 |         AXIOM,BEXERA |             2 |          3 |            5 |                 nsaid,potassium |             2 |          3 |            5 |                         nsaid10mg,potassium20mg |             2 |          1 |            1 |
|     175 |      50,300 |             2 |          1 |         AXIOM,BEXERA |             2 |          3 |            5 |                 nsaid,potassium |             2 |          3 |            5 |                         nsaid25mg,potassium20mg |             2 |          1 |            1 |
|       1 |       25,50 |             2 |          1 |         AXIOM,BEXERA |             2 |          3 |            5 |                 nsaid,potassium |             2 |          3 |            5 |                          nsaid5mg,potassium20mg |             2 |          1 |            1 |
|     999 | 100,200,350 |             3 |          1 | AXIOM,BEXERA,CAZERTA |             3 |          2 |            3 | nsaid,potassium,sodium chloride |             3 |          2 |            3 |     nsaid10mg,potassium12mg,sodium chloride10mg |             3 |          1 |            1 |
|     999 |  50,100,200 |             3 |          1 | AXIOM,BEXERA,CAZERTA |             3 |          2 |            3 | nsaid,potassium,sodium chloride |             3 |          2 |            3 |     nsaid10mg,potassium20mg,sodium chloride10mg |             3 |          1 |            1 |
|       1 |   25,50,100 |             3 |          1 | AXIOM,BEXERA,CAZERTA |             3 |          2 |            3 | nsaid,potassium,sodium chloride |             3 |          2 |            3 |      nsaid5mg,potassium20mg,sodium chloride10mg |             3 |          1 |            1 |
|     999 |     100,200 |             2 |          1 |        AXIOM,CAZERTA |             2 |          2 |            2 |           nsaid,sodium chloride |             2 |          2 |            2 |                   nsaid10mg,sodium chloride10mg |             2 |          1 |            1 |
|       1 |      25,100 |             2 |          1 |        AXIOM,CAZERTA |             2 |          2 |            2 |           nsaid,sodium chloride |             2 |          2 |            2 |                    nsaid5mg,sodium chloride10mg |             2 |          1 |            1 |
|     201 |         350 |             1 |          2 |               BEXERA |             1 |          5 |            4 |                       potassium |             1 |          5 |            4 |                                   potassium12mg |             1 |          2 |            1 |
|     999 |         350 |             1 |          2 |               BEXERA |             1 |          5 |            4 |                       potassium |             1 |          5 |            4 |                                   potassium12mg |             1 |          2 |            1 |
|     999 |      50,350 |             2 |          1 |               BEXERA |             1 |          5 |            4 |                       potassium |             1 |          5 |            4 |                     potassium12mg,potassium20mg |             2 |          1 |            1 |
|     378 |         400 |             1 |          1 |               BEXERA |             1 |          5 |            4 |                       potassium |             1 |          5 |            4 |                                   potassium15mg |             1 |          1 |            1 |
|       1 |          50 |             1 |          3 |               BEXERA |             1 |          5 |            4 |                       potassium |             1 |          5 |            4 |                                   potassium20mg |             1 |          3 |            1 |
|     175 |          50 |             1 |          3 |               BEXERA |             1 |          5 |            4 |                       potassium |             1 |          5 |            4 |                                   potassium20mg |             1 |          3 |            1 |
|     999 |          50 |             1 |          3 |               BEXERA |             1 |          5 |            4 |                       potassium |             1 |          5 |            4 |                                   potassium20mg |             1 |          3 |            1 |
|     999 |  50,100,350 |             3 |          1 |       BEXERA,CAZERTA |             2 |          4 |            5 |       potassium,sodium chloride |             2 |          4 |            5 | potassium12mg,potassium20mg,sodium chloride10mg |             3 |          1 |            1 |
|     999 |     100,350 |             2 |          1 |       BEXERA,CAZERTA |             2 |          4 |            5 |       potassium,sodium chloride |             2 |          4 |            5 |               potassium12mg,sodium chloride10mg |             2 |          1 |            1 |
|     201 |     350,450 |             2 |          1 |       BEXERA,CAZERTA |             2 |          4 |            5 |       potassium,sodium chloride |             2 |          4 |            5 |               potassium12mg,sodium chloride30mg |             2 |          1 |            1 |
|     378 |     100,400 |             2 |          1 |       BEXERA,CAZERTA |             2 |          4 |            5 |       potassium,sodium chloride |             2 |          4 |            5 |               potassium15mg,sodium chloride10mg |             2 |          1 |            1 |
|       1 |      50,100 |             2 |          2 |       BEXERA,CAZERTA |             2 |          4 |            5 |       potassium,sodium chloride |             2 |          4 |            5 |               potassium20mg,sodium chloride10mg |             2 |          2 |            1 |
|     999 |      50,100 |             2 |          2 |       BEXERA,CAZERTA |             2 |          4 |            5 |       potassium,sodium chloride |             2 |          4 |            5 |               potassium20mg,sodium chloride10mg |             2 |          2 |            1 |
|       1 |         100 |             1 |          3 |              CAZERTA |             1 |          4 |            2 |                 sodium chloride |             1 |          4 |            2 |                             sodium chloride10mg |             1 |          3 |            1 |
|     378 |         100 |             1 |          3 |              CAZERTA |             1 |          4 |            2 |                 sodium chloride |             1 |          4 |            2 |                             sodium chloride10mg |             1 |          3 |            1 |
|     999 |         100 |             1 |          3 |              CAZERTA |             1 |          4 |            2 |                 sodium chloride |             1 |          4 |            2 |                             sodium chloride10mg |             1 |          3 |            1 |
|     201 |         450 |             1 |          1 |              CAZERTA |             1 |          4 |            2 |                 sodium chloride |             1 |          4 |            2 |                             sodium chloride30mg |             1 |          1 |            1 |
0 голосов
/ 31 мая 2019

Может быть что-то вроде (не проверено):

WITH meds_taken AS
  (SELECT sum(CASE WHEN d.drug_name = :namea THEN 1 ELSE 0 END) AS drug_a
        , sum(CASE WHEN d.drug_name = :nameb THEN 1 ELSE 0 END) AS drug_b
        , sum(CASE WHEN d.drug_name = :namec THEN 1 ELSE 0 END) AS drug_c
   FROM pharmacy_data AS p
   JOIN drug_reference AS d ON p.drug_code = d.drug_code
   GROUP BY p.user_id)
, med_counts AS
  (SELECT drug_a, drug_b, drug_c, count(*) AS "user total"
   FROM meds_taken
   GROUP BY drug_a, drug_b, drug_c)
SELECT rank() OVER (ORDER BY "user total" DESC) AS rank
     , drug_a, drug_b, drug_c, "user total"
FROM med_counts
ORDER BY "user total" DESC;
...