Некоторым кажется, что разработчики BI делают магию. Например, как в нашем сегодняшнем примере – анализируют данные, которых в факт-таблицах нет. Заинтриговал? Значит, теперь подробности =)
Учет пациентов
Предположим, что у нас есть частная клиника, которая ведет прием и госпитализацию пациентов. У клиники несколько источников данных и есть цель создать единый аналитический интерфейс для учета и прогнозирования, в том числе потока пациентов и их оплат.
В чем магия? По сути, у нас нет источника данных с повторными приемами пациентов, а есть только значения записей по приемам такого вида:
Имя пациента | Дата приема | Дата выписки |
Пациент 1 | 1/1/2016 | 1/4/2016 |
Пациент 1 | 1/7/2016 | 1/10/2016 |
Пациент 1 | 1/30/2016 | 2/4/2016 |
Для владельца клиники в этих данных все очевидно. Пациент пришел 7 января, а затем пришел еще раз в конце того же месяца – 30 января. Но для разработчика нужна отдельная строка с четким указанием, что прием был повторным.
Получается, что в голове сотрудника повторный прием учтен, но в базе данных этих данных нет. Конечно, можно сделать дополнительный отчет с колонкой – повторный прием, первичный прием. Но нам нужен отчет более глубокий, поскольку нам нужно точно учитывать посещения каждого пациента, их количество, обратился ли человек с тем же диагнозом или другим.
ID | Имя пациента | Прием | Выписка | Тип пациента | Диагноз |
1 | Пациент 1 | 1/1/2016 | 1/4/2016 | Стационар | 1 |
2 | Пациент 1 | 1/7/2016 | 1/10/2016 | Амбулаторно | 2 |
3 | Пациент 1 | 1/30/2016 | 2/4/2016 | Стационар | 1 |
4 | Пациент 1 | 2/6/2016 | 2/10/2016 | Стационар | 2 |
5 | Пациент 1 | 2/11/2016 | 2/16/2016 | Стационар | 1 |
Так, например, мы не хотим сравнивать визиты с разными диагнозами, а лишь, например, 2/11 с 2/4.
Как правило, в таблицах хранится код диагноза, согласно принятой системе медицинской учета:
ID | ICD9_1 | ICD9_2 | ICD9_3 | ICD9_4 | ICD9_…. | ICD9_25 |
1 | 491.1 | 023.2 | 33.5 | V16.9 | 37.52 |
То есть у нас есть таблица с такими цифрами:
491.1, 491.20, 491.21, 491.22, 491.8, 491.9, 492.0, 492.8, 493.20, 493.21, 493.22, 494.0, 494.1, 496
А для дальнейшей классификации идут другие подкатегории:
33.51, 33.52, 37.51, 37.52, 37.53, 37.54, 37.62, 37.63′, 33.50, 33.6, 50.51, 50.59, 52.80, 52.82, 55.69′,’196.0, 196.1, 196.2, 196.3, 196.5, 196.6, 196.8, 196.9, 197.0, 197.1, 197.2, 197.3, 197.4, 197.5, 197.6, 197.7, 197.8, 198.0, 198.1, 198.2, 198.3, 198.4, 198.5, 198.6, 198.7, 198.81, 198.82, 198.89, 203.02, 203.12, 203.82, 204.02, 204.12, 204.22, 204.82, 204.92, 205.02, 205.12, 205.22, 205.82, 205.92, 206.02, 206.12, 206.22, 206.82, 206.92, 207.02, 207.12, 207.22, 207.82, 208.02, 208.12, 208.22, 208.82, 208.92, 480.3, 480.8, 996.80, 996.81, 996.82, 996.83, 996.84, 996.85, 996.86, 996.87, 996.89, V42.0, V42.1, V42.4, V42.6, V42.7, V42.81, V42.82, V42.83, V42.84, V42.89, V42.9, V43.21, V46.11
Итак, как же представить все это в Qlik?
Шаг 1
Если объединить коды диагнозов 15-25 в одно поле, то сравнить их будет проще и понять, какой назначить пациенту. Qlik поможет это сделать через скрипт загрузки и функцию Concatenate:
1 |
ICD9_Diagnoses_1 & ‘, ‘ & ICD9_Diagnoses_2 & ‘, ‘ & ICD9_Diagnoses_3 & ‘, ‘ & ICD9_Diagnoses_4 & ‘, ‘ & ICD9_Diagnoses_5 & ‘, ‘ & ICD9_Diagnoses_6 & ‘, ‘ & ICD9_Diagnoses_7 & ‘, ‘ & ICD9_Diagnoses_8 & ‘, ‘ & ICD9_Diagnoses_9 & ‘, ‘ & ICD9_Diagnoses_10 & ‘, ‘ & ICD9_Diagnoses_11 &’, ‘ & ICD9_Diagnoses_12 & ‘, ‘ & ICD9_Diagnoses_13 & ‘, ‘ & ICD9_Diagnoses_14 & ‘, ‘ & ICD9_Diagnoses_15 as [All Diagnosis] |
Шаг 2
Далее вы можете добавить новые поля сразу из скрипта загрузки. Скрипт говорит Qlik загрузить все со второго выражения сначала, затем вернуться и сделать код диагноза. Так мы создаем поле диагнозов. Такую же процедуру можно проделать и других, похожих задач.
Encounters:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
Preceeding load Load *, // Если первые совпадают, то это, возможно, нужный диагноз, а если нет других из 14 перечисленных подкатегорий, то это точно нужный диагноз IF ( Match([ICD9 Diagnoses 1] , ‘491.1’, ‘491.20’ … ‘493.21’, ‘493.22’, ‘494.0’, ‘494.1’, ‘496’) > 0 And WildMatch([All Diagnosis], ‘*33.51*’, ‘*33.52*’, ‘*37.51*’ … ‘*V43.21*’, ‘*V46.11*’) = 0, ‘COPD’, // Если мы нашли нужный диагноз, проверяем подтипы IF (Match ([ICD9 Diagnoses 1] , ‘003.1’, ‘027.0’, … ‘785.52’ ) > 0 And WildMatch([All Diagnosis], ‘*33.50*’, ‘*33.51*’ … ‘*V43.21*’, ‘*205.32*’) = 0, ‘Sepsis’, ‘Nothing’)) as [Core Diagnosis]; Стандартная загрузка из файла LOAD MRN, EncounterID, …… [ICD9 Diagnoses 1], [ICD9 Diagnoses 2] ….. |
Шаг 3
Далее предлагаю воспользоваться функцией “Previous”. Она позволяет посмотреть предыдущую строку. Если вы на второй строке, вы можете проверить значение первой строки. Вот как это работает:
IF(MRN = Previous(MRN),’Yes’, ‘No’) as [Inpatient IsReadmission Flag],
Если значение то же, что и в строке выше, тогда это повторный прием, иначе нет. Если нет, то это новый пациент.
Конечно, это упрощенная версия кода. Значение «да-нет» – хорошо для компьютера, а нам нужно читаемый вид для человека, поэтому используем функцию DUAL, что позволит иметь одно поле для значений и да, и нет.
IF(MRN = Previous(MRN),Dual(‘Yes’, 1),Dual(‘No’,0)) as [Inpatient IsReadmission Flag],
Благодаря этой функции, мы можем использовать такое выражение:
Sum([Inpatient IsReadmission Flag])
Итоговый скрипт выглядит следующим образом:
1 2 3 4 5 6 7 8 9 10 11 |
Left Join (Encounters) LOAD EncounterID, IF(MRN = Previous(MRN),Dual(‘Yes’, 1),Dual(‘No’,0)) as [Inpatient IsReadmission Flag], IF(MRN = Previous(MRN),Previous([Discharge Dt/Tm])) as [Inpatient Previous Discharge Date], IF(MRN = Previous(MRN),Previous(EncounterID)) as [Inpatient Previous EncounterID], IF(MRN = Previous(MRN),NUM(Interval([Admit Dt/Tm]-Previous([Discharge Dt/Tm])),’#,##0.00′)) as [Inpatient Readmission Difference], IF(MRN = Previous(MRN),IF(Interval([Admit Dt/Tm]-Previous([Discharge Dt/Tm])) <= 30.0, Dual(‘Yes’, 1), Dual(‘No’,0)), Dual(‘No’,0)) as [Inpatient IsReadmission within 30] Resident Encounters Where [Patient Type] = ‘Inpatient’ Order by MRN, [Admit Dt/Tm]; |
Назовем наши поля по-другому:
1 2 3 4 5 6 7 8 9 10 11 12 |
Left Join (Encounters) LOAD EncounterID, IF(MRN = Previous(MRN),Dual(‘Yes’, 1),Dual(‘No’,0)) as [COPD IsReadmission Flag], IF(MRN = Previous(MRN),Previous([Discharge Dt/Tm])) as [COPD Previous Discharge Date], IF(MRN = Previous(MRN),Previous(EncounterID)) as [COPD Previous EncounterID], IF(MRN = Previous(MRN),NUM(Interval([Admit Dt/Tm]-Previous([Discharge Dt/Tm])),’#,##0.00′)) as [COPD Readmission Difference], IF(MRN = Previous(MRN),IF(Interval([Admit Dt/Tm]-Previous([Discharge Dt/Tm])) <= 30.0, Dual(‘Yes’, 1), Dual(‘No’,0)), Dual(‘No’,0)) as [COPD IsReadmission within 30], IF(MRN = Previous(MRN),IF(Interval([Admit Dt/Tm]-Previous([Discharge Dt/Tm])) <= 90.0,’Yes’, ‘No’), ‘No’) as [COPD IsReadmission within 90] Resident Encounters Where [Patient Type] = ‘Inpatient’ and [Core Diagnosis] = ‘COPD’ Order by MRN, [Admit Dt/Tm]; |
Аналогичный скрипт пишем для другого диагноза.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
Left Join (Encounters) LOAD EncounterID, IF(MRN = Previous(MRN),Dual(‘Yes’, 1),Dual(‘No’,0)) as [Sepsis IsReadmission Flag], IF(MRN = Previous(MRN),Previous([Discharge Dt/Tm])) as [Sepsis Previous Discharge Date], IF(MRN = Previous(MRN),Previous(EncounterID)) as [Sepsis Previous EncounterID], IF(MRN = Previous(MRN),NUM(Interval([Admit Dt/Tm]-Previous([Discharge Dt/Tm])),’#,##0.00′)) as [Sepsis Readmission Difference], IF(MRN = Previous(MRN),IF(Interval([Admit Dt/Tm]-Previous([Discharge Dt/Tm])) <= 30.0,Dual(‘Yes’, 1), Dual(‘No’,0)), Dual(‘No’,0)) as [Sepsis IsReadmission within 30], IF(MRN = Previous(MRN),IF(Interval([Admit Dt/Tm]-Previous([Discharge Dt/Tm])) <= 90.0,’Yes’, ‘No’), ‘No’) as [Sepsis IsReadmission within 90], IF(MRN = Previous(MRN),IF(Interval([Admit Dt/Tm]-Previous([Discharge Dt/Tm])) <= 120.0,’Yes’, ‘No’), ‘No’) as [Sepsis IsReadmission within 120], IF(MRN = Previous(MRN),IF(Interval([Admit Dt/Tm]-Previous([Discharge Dt/Tm])) > 120.0,’Yes’, ‘No’), ‘No’) as [Sepsis IsReadmission > 120] Resident Encounters Where [Patient Type] = ‘Inpatient’ and [Core Diagnosis] = ‘Sepsis’ Order By MRN, [Admit Dt/Tm]; |
И то же мы делаем, когда нам нужно создать поля, которых нет в модели.
Дэшборд приема пациентов
В итоге у нас получился такой дэшборд:
Вот и все на сегодня. Хороших вам разработок!
Свежие комментарии