Какво има в тази статия: Тази публикация описва как можете да приложите прости техники за машинно обучение, за да анализирате здравните данни по интересни и смислени начини. Като пример използваме прогноза за болнична реадмисия при болни с диабет и обясняваме как постигнахме 94% точност. Тази публикация е резултат от проект, направен от екип от четирима (Усман Раза, Харман Шах Сингх, Чинг-И Лин и Рохан Кар), и допълнително преработен за ваше удоволствие от четенето от Харман и аз. Ще ви преведем през процеса, като подчертаем обосновките за нашия избор и ще влезем малко в кода на Python в определени точки. Забележка: Въпреки че това е предназначено да бъде достъпно за повечето, предполага се известно познаване на python и пандите.

машинно

Забележка: Пълният код и файловете с данни са налични в нашия репозиторий на Git-hub тук.

Болнична реадмисия е, когато пациент, който е изписан от болницата, бъде повторно приет отново в рамките на определен период от време. Процентът на болничния реадмисия при определени условия сега се счита за показател за качеството на болницата и също така влияе неблагоприятно на разходите за грижи. Поради тази причина Центровете за услуги по Medicare & Medicaid създадоха програма за намаляване на болничния прием, която има за цел да подобри качеството на грижите за пациентите и да намали разходите за здравеопазване чрез прилагане на санкции за плащане на болници, които имат повече от очакваното ниво на реадмисия при определени условия. Въпреки че диабетът все още не е включен в наказателните мерки, програмата редовно добавя нови състояния на заболяването към списъка, сега общо 6 за 2018 финансова година. През 2011 г. американските болници похарчиха над 41 милиарда долара за пациенти с диабет, които бяха приети отново в рамките на 30 дни след изписването. Възможността да се определят фактори, които водят до по-голяма реадмисия при такива пациенти, и съответно възможността да се предскаже кои пациенти ще получат реадмисия може да помогне на болниците да спестят милиони долари, като същевременно подобрят качеството на грижите. Така че, имайки предвид това, използвахме набор от медицински претенции (описание по-долу), за да отговорим на тези въпроси:

  • Какви фактори са най-силните предсказатели за реадмисия в болница при пациенти с диабет?
  • Колко добре можем да предскажем реадмисия в болница в този набор от данни с ограничени функции?

Намирането на добър набор от данни е едно от първите предизвикателства (освен дефинирането на смислен въпрос), когато се изпробват методи за машинно обучение. Настоящото състояние на света на здравеопазването е такова, че можем лесно да намерим набори от данни, които са богати (пълни с полезна информация), но мръсни (неструктурирано съдържание или разхвърляни схеми) или набори от данни, които са много чисти, но иначе стерилни по отношение на съдържащата се информация (вижте фигурата по-долу ).

С това ограничение избрахме публично достъпен набор от данни от хранилището на UCI (връзка), съдържащ данни за деиндентифицирани пациенти с диабет за 130 американски болници (1999–2008 г.), съдържащи 101 766 наблюдения за 10 години. Наборът от данни има над 50 функции, включително характеристики на пациента, състояния, тестове и 23 лекарства. Включени са само срещи с диабет (т.е. поне една от трите първични диагнози е била диабет). Този набор от данни е използван от Strack et al. през 2014 г. за интересен анализ по същата тема (публикуван тук). Затова започваме с зареждането на набора от данни (csv файл, изтеглен от връзката по-горе) като рамка с данни на pandas:

Първото нещо, което трябва да направите, когато избирате набор от данни, е да направите профилиране на данни и да разгледате ключови характеристики, както направихме в таблицата по-долу:

От този момент нататък ние широко следвахме процеса, както е показано на фигурата по-долу. Важно е обаче да се отбележи, че няма много чисто разделяне между стъпките и има много итерации напред и назад, когато се изпробват различни подходи за проектиране и моделиране на функции. Също така имайте предвид, че има доста припокриване между термини като създаване на функции, проектиране на функции и предварителна обработка, в зависимост от това с кого разговаряте.

Преди да можем да стигнем до действителното моделиране, почти винаги са необходими спорове с данните. Тук приложихме три вида методи:

  1. Почистващи задачи като изпускане на лоши данни, справяне с липсващи стойности.
  2. Модификация на съществуващи функции напр. стандартизация, трансформиране на дневници и др.
  3. Създаване или деривация на нови функции, обикновено от съществуващи.

Отделните стъпки са описани подробно по-долу. Имайте предвид обаче, че това, което сега изглежда като хубава последователност от стъпки, е резултат от много опити и опити за грешки, за да се види какво работи добре за привеждане на данните ни в най-добра форма.

Справяне с липсващи стойности

Първо трябва да видим колко липсващи стойности са (които са кодирани като „?“ За повечето променливи в данните):

Това ни дава дълъг списък, но следните променливи са липсвали стойности:

Сега важната част - решение какво да се прави:

Създаване и/или Прекодиране на нови функции

Това е силно субективно и отчасти зависи от познанията за здравните услуги и осмислянето на потенциалните взаимоотношения между характеристиките. Тук може би има хиляди начини да опитате. Опитахме някои (нито едно не е перфектно) и ето защо.

Използване на услугата: Данните съдържат променливи за броя на болничните (прием), посещенията на спешното отделение и амбулаторните посещения за даден пациент през предходната една година. Това са (груби) мерки за това колко болнични/клинични услуги е използвал човек през последната година. Добавихме тези три, за да създадем нова променлива, наречена използване на услугата (вижте фигурата по-долу). Идеята беше да видим коя версия ни дава по-добри резултати. Разбира се, ние не приложихме никакво специално претегляне към трите съставки на използването на услугата, но искахме да опитаме нещо просто на този етап.

Правенето на това в python е доста лесно:

Брой промени в лекарствата: Наборът от данни съдържа 23 функции за 23 лекарства (или комбинации), които показват за всяко от тях, дали е направена промяна в това лекарство по време на настоящия болничен престой на пациента. Предишни изследвания показват, че промяната на лекарствата при диабетици при прием е свързана с по-ниски нива на повторно приемане. Решихме да преброим колко промени бяха направени общо за всеки пациент и обявихме, че това е нова функция. Мотивите тук бяха едновременно опростяване на модела и евентуално откриване на връзка с броя на промените, независимо от това кое лекарство беше променено. В python това се прави от:

За да проверим резултата от това, използваме метода value_counts (), който дава хубаво стесняващо се разпределение:

Брой използвани лекарства: Друг вероятно свързан фактор може да бъде общият брой лекарства, използвани от пациента (което може да показва тежестта на тяхното състояние и/или интензивността на грижите). Затова създадохме още една функция, като преброихме лекарствата, използвани по време на срещата (променливата на ключовете в кода по-долу продължава отгоре):

За да проверите изхода:

Категоризиране на диагнозите: Наборът от данни съдържа до три диагнози за даден пациент (първична, вторична и допълнителна). Въпреки това, всеки от тях имаше 700–900 уникални ICD кода и е изключително трудно да ги включите в модела и да ги интерпретирате смислено. Следователно, ние свихме тези диагностични кодове в 9 категории заболявания по почти подобен начин на този, направен в оригиналната публикация, използвайки този набор от данни. Тези 9 категории включват циркулаторна, дихателна, храносмилателна, диабет, нараняване, мускулно-скелетна, пикочно-полова, новообразувания и други. Въпреки че направихме това за първични, вторични и допълнителни диагнози, в крайна сметка решихме да използваме само първичната диагноза в нашия модел. Правенето на това в python беше малко тромаво, защото, ние, ние картографираме кодовете на болестта към определени имена на категории. По-долу кодът трябва лесно да демонстрира това.

Сега, за да проверите резултата:

На този етап може да искаме да запазим свършената работа далеч в csv файл като резервно копие, така че по-късно да може да се зареди от тази точка, без да е необходимо да правите всички горепосочени стъпки.

Свиване на някои други променливи: Точно като диагнозите, имаше доста категории за източника на прием, вида на приема и разпореждането с изписването. Свихме тези променливи в по-малко категории, където имаше смисъл. Например, видове прием 1, 2 и 7 съответстват на спешна помощ, спешна помощ и травма и по този начин са обединени в една категория, тъй като това са всички неизбираеми ситуации. За краткост, примерният код на python е даден по-долу само за тип прием:

Прекодиране на някои променливи: Оригиналният набор от данни използва низови стойности за пол, раса, промяна на лекарството и всяко от 23-те използвани лекарства. За да се поберат по-добре тези променливи в нашия модел, ние интерпретираме променливите в числови двоични променливи, за да отразят тяхната същност. Например кодирахме функцията „промяна на лекарството“ от „No“ (без промяна) и „Ch“ (променена) в 0 и 1. Кодът е даден по-долу:

Също така намалихме резултатите от теста A1C и резултатите от теста за глюкоза в категории Нормално, Ненормално и Не тествано.

Прекодиране на променливата на резултата: Резултатът, който разглеждаме, е дали пациентът ще бъде приет отново в болницата в рамките на 30 дни или не. Променливата всъщност има 30 и няма категории за реадмисия. За да намалим проблема си до двоична класификация, обединихме повторното приемане след 30 дни и никакво повторно приемане в една категория:

Справяне с възрастта: Има различни начини да се справите с това. Наборът от данни ни дава възраст само като 10-годишни категории, така че не знаем точната възраст на всеки пациент. Предишното проучване на този набор от данни използва възрастови категории като номинални променливи, но искахме да можем да видим ефекта от увеличаването на възрастта върху реадмисията, дори и по груб начин. За целта приемаме, че възрастта на пациента е средно в средата на възрастовата категория. Например, ако възрастовата категория на пациента е 20–30 години, тогава приемаме, че възрастта = 25 години. Така че преобразувахме възрастовите категории в средни точки, което доведе до числова променлива:

Свиване на множество срещи за един и същ пациент

Някои пациенти в набора от данни са имали повече от една среща. Не можем да ги броим като независими срещи, тъй като това отклонява резултатите към тези пациенти, които са имали няколко срещи. По този начин опитахме множество техники за свиване и консолидиране на множество срещи за един и същ пациент като:

  1. Разглеждането на повече от 2 реадмисии при множество срещи като реадмисия за свит запис.
  2. Като се има предвид средният престой в болница при множество срещи.
  3. Като се има предвид процентът на промените в лекарството при множество срещи
  4. Като се има предвид общият брой срещи, за да се замени уникалният идентификатор на срещата
  5. Като се има предвид комбинацията от диагнози при множество срещи като списък

Взимайки например функциите като „диагностика“ например, не сметнахме, че няма смисъл да комбинираме множество категорични стойности в масив за изграждане на модел на данни. След това разгледахме първата среща и последната среща отделно като възможни представяния на множество срещи. Последните срещи обаче дадоха изключително дисбалансирани данни за повторни приемания (96/4 повторни приемания срещу липса на повторни приемания) и по този начин решихме да използваме първите срещи на пациенти с многократни срещи. Това доведе до намаляване на набора от данни до около 70 000 срещи:

Предварителен анализ на нашите числени характеристики разкри, че много от тях са силно изкривени и имат висока ексцесия. Като референция, изкривяването на нормалното разпределение е 0, а излишъкът на куртоза (разлика на действителната куртоза от идеалната нормална стойност на разпределение 3), както се връща от функцията kurtosis () за нормално разпределение, е 0, което би повлияло на стандартизацията. Характеристики като брой спешни посещения, използване на услугата, брой стационарни приемания и брой амбулаторни посещения са имали голям наклон и ексцесия. По този начин, ние извършихме трансформация на дневник, когато изкривяване или ексцесия над границите на -2 ≤ изкривяване и ексцесия ≤ 2. Също така, тъй като log (0) не е дефиниран, решихме да използваме следното правило:

  1. Изчисляване на дневник (x) за всяка характеристика x, ако процент от 0s в x ≤ 2%, след премахване на нулите. Това гарантира, че няма да премахнем групово записи, които притежават предсказваща сила за други колони.
  2. Изчислява log1p (x) в противен случай (log1p (x) означава log (x + 1), като същевременно запазва нулите.

За да извършите действителната трансформация на регистрационния файл, python кодът е доста прав. Създаваме нова колона и я задаваме равна на np.log или np.log1p на колоната, която ще се трансформира:

Къде да използваме log1p вместо log зависи от това колко малки са стойностите. Имаме отделен урок за това и ще включва код за проверка и автоматично извършване на трансформации на журнали. Проверете отново скоро за това.

Тъй като бяхме използвали регистрационна трансформация, за да гарантираме, че числовите променливи имат подобно на Гаус или нормално разпределение (преди преобразуването на регистрационния файл) или са били трансформирани, за да осигурим нормално разпределение, решихме да стандартизираме нашите цифрови характеристики, като използваме формулата:

Кодирането на това в python е проста функция, която приема масиви и фреймове за данни като вход и връща техните стандартизирани версии:

Премахване на отклонения

Тъй като се уверихме, че всички наши променливи са почти нормални след преобразуване на регистрационния файл (където е необходимо), разпределенията трябва да бъдат повече или по-малко нормални. Използване на правилото за покритие за нормално разпределение, което може да бъде обобщено със следната диаграма:

Всичко в рамките на 3 SD от двете страни на средната стойност ще включва 99,7% от нашите данни, а останалите 0,3% сме третирани като извънредни стойности. Използвайки тази логика, ограничихме данните си до 3 SD от двете страни от средната стойност за всяка цифрова колона:

Условия за взаимодействие

Променливите могат да имат взаимозависими ефекти върху реадмисията, наречени взаимодействия. Можем да идентифицираме възможните кандидати за термини за взаимодействие, като разгледаме какво има теоретичен смисъл и като наблюдаваме матрица на корелация на прогностичните променливи, за да видим кои изглеждат силно корелирани. Създаването на корелационна матрица в python е лесно, но ние искаме чист начин за сортиране на корелационните стойности. Тъй като това води до дълъг списък, ние също трябва да заменим размера на дисплея по подразбиране на бележника jupyter за списъци:

Това ни дава малко добре форматиран изход за преглед. По-долу показваме само три, за да демонстрираме два типа ситуации:

В този списък има два вида ситуации:

  1. Една променлива се съдържа в/производна на друга: В горните примери броят на амбулаторните посещения е част от използването на услугата. Ние обаче създадохме тази функция сами, така че в този случай нашето решение е да не ги поставяме в един и същ набор от функции (използвахме два набора от функции, описани по-късно). По същия начин, diabetesMed (всяко предписано диабетно лекарство) се съдържа в броя на използваните лекарства. В този случай решихме да премахнем диабета от анализа, защото разбира се, че всички тези пациенти получават някои лекарства за диабет.
  2. Възможна действителна съвместна дисперсия: Другата ситуация е действителна съвместна дисперсия между две променливи. Това изглежда важи за броя на лекарствата и дали е направена промяна или не, а също така има известен интуитивен смисъл. Така че ние създадохме условия за взаимодействие за такива случаи.

В python това беше направено с помощта на:

Балансиране на данни

Данните бяха силно дисбалансирани по отношение на повторните приемания (само 10% записи за 30-дневни повторни приемания), което доведе до висока точност. Нещо повече, високата точност може да се отдаде не на обобщаемостта на нашия модел за разнообразни досиета на пациентите, а на базовата точност от 90%: прогнозиране, че нито един пациент няма да бъде приет отново. Това беше видно от лошата прецизност и изземване на нашия модел при предсказване на повторни приемания на пациенти. Използвахме синтетична техника за свръхпробни малцинства (SMOTE), за да превъзпитаме нашия недостатъчно представен клас на реадмисии и да получим еднакво представяне на нашите свръхпредставени и недостатъчно представени класове.

Повече за техниката можете да намерите тук. Долната снимка на прогнозите от един от нашите модели преди и след балансирането на данни показва ефекта като значително по-ниски грешки от тип 2:

Както виждаме, грешките от тип 2 са значително намалени след балансиране, което показва по-добро изземване на нашия модел. Това се изразява в по-нисък дял на повторно приетите хора, за които се предвижда, че не са приети отново: критичен показател за болниците и застрахователните агенции както от финансова, така и от превантивна гледна точка.