<jupyter_start><jupyter_text>Predicting next lab results  배경- 환자가 받은 다양한 치료로 근미래의 검사 결과를 맞출 수 있을까? 문제 종류- binary classification (검사 결과가 정상범위인지 아닌지) 실험- Sepsis 환자를 대상으로 다음 lab 검사 결과의 정상범위 여부를 예측    - Variables used in SOFA:    -  GCS, MAP, FiO2, Ventilation status (sourced from CHARTEVENTS)    -  Creatinine, Bilirubin, FiO2, PaO2, Platelets (sourced from LABEVENTS)    -  Dobutamine, Epinephrine, Norepinephrine (sourced from INPUTEVENTS_MV and INPUTEVENTS_CV)    -  Urine output (sourced from OUTPUTEVENTS) 교차검증 평가 예측값의 활용- 수련의 교육용 첫번째 검사 결과 예측 대상 : Fi02- https://github.com/MIT-LCP/mimic-code/blob/master/notebooks/aline/aline_sofa.sql<jupyter_code>import sys
sys.path.insert(0, './db')
import db_con
import sqlalchemy

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
# Fi02 itemid 찾기
engine = db_con.get_engine()
pd.read_sql_query("""
            SELECT *
            FROM d_items
            WHERE label LIKE '%%FiO2%%';
            """, engine)<jupyter_output><empty_output><jupyter_text>의학 의견- Item선정 기준<jupyter_code># sepsis diagnosis 환자들의 FiO2 기록(입원 시점으로 분단위)
pat = pd.read_sql("""
                SELECT
                chi.icustay_id,
                chi.mins,
                chi.itemid,
                chi.valuenum,
                d_items.label
                FROM (
                SELECT
                 ch.icustay_id,
                 EXTRACT(MINS FROM ch.charttime - ic.intime) AS mins,
                 ch.itemid,
                 ch.value,
                 ch.valuenum
                FROM (SELECT *
                     FROM chartevents
                       LEFT JOIN diagnoses_icd di
                         ON chartevents.subject_id = di.subject_id
                     WHERE di.icd9_code IN ('77181', '99591', '99592', '67020', '67022', '67024')
                           AND chartevents.itemid IN (3420, 190, 223835, 3422)) AS ch LEFT JOIN icustays ic
                   ON ch.icustay_id = ic.icustay_id
                ) AS chi
                LEFT JOIN d_items
                ON chi.itemid = d_items.itemid
                """, engine)
pat.shape
pat.head()
pat.describe()
pat = pat[~pat.icustay_id.isna()]
pat = pat[pat.mins>=0]
pat.shape<jupyter_output><empty_output><jupyter_text>전처리pre-process the FiO2s to ensure they are between 21-100%<jupyter_code>pat = pat[(pat.valuenum>21) | ((pat.valuenum<1) & (pat.valuenum>0))]
pat.shape
# 제거 되었는지 확인
pat[(pat.valuenum<21) & (pat.valuenum<0.2)]
# 백분위로 변환
def convert_to_percent(num):
    return num * 100

pat.loc[pat.valuenum<1, 'valuenum'] = pat[pat.valuenum<1].valuenum.map(lambda x: convert_to_percent(x))

pat[pat.valuenum<1]
pat.valuenum.describe()<jupyter_output><empty_output><jupyter_text>한 환자의 FiO2값 예측하기<jupyter_code># 가장 긴 관찰 값을 가지고 있는 환자 찾기
find_pat = pat.groupby(['mins', 'icustay_id']).mean().reset_index().pivot("icustay_id", 'mins', 'valuenum')
find_pat.isnull().sum(axis=1).sort_values(ascending=True)
target_pat = find_pat.iloc[find_pat.index==295669].values[0]
target_pat
train, test = target_pat[0:len(target_pat)-10], target_pat[len(target_pat)-10:]
train
test<jupyter_output><empty_output><jupyter_text>Time series 데이터를 기반으로 다음 값을 예측하기- Model : Autoregression(자기회귀)    - output values based on a linear combination of input values    - 앞선 관측값이 다음 값을 예측하는데 효과적이라는 가정을 기반으로 한다.    - 만약 input과 output이 같은 방향(같이 증가하거나 같이 감소하거나)으로 변화한다면 positive correlation이라고 지칭하고, 반대 방향으로 변화한다면 negative correlation이라고 한다.    - y = b0 + b1*x1     -> x(t+1) = b0 + b1*x(t-1) + b2*x(t-2)....<jupyter_code>from statsmodels.tsa.ar_model import AR

model = AR(train)
model_fit = model.fit()
print('Lag : %s' % model_fit.k_ar)
print('Coefficients : %s' % model_fit.params)

predictions = model_fit.predict(start=len(train), end=len(train)+len(test)-1, dynamic=False)
for i in range(len(predictions)):
    print('Predicted= %f, expected=%f' % (predictions[i], test[i]))
from sklearn.metrics import mean_squared_error

error = mean_squared_error(test, predictions)
print('Test MSE: %.3f' % error)
plt.plot(test)
plt.plot(predictions, color='red')
plt.show()
# 전체 시간을 5분 단위로 변경
def convert_to_min_range(min_num):
    return int(min_num/5)*5

pat.mins = pat.mins.map(lambda x: convert_to_min_range(x))
pat.head()
# 같은 시간 내 관찰값이 여러번 추가되었다면, 평균으로 정리한다 -- 통계적, 의학적 타당성(?)
mean_pat = pat.groupby(['mins', 'icustay_id']).mean().reset_index()
mean_pat.head()
## Pivot table: 데이터 컬럼 중에 두 개의 컬럼을 각각 행의 인덱스, 열의 인덱스로 사용하여 테이블을 구성하는 것
# pandas에서는 피봇데이블을 만들기 위해 pivot(행 인덱스로 사용할 컬럼이름, 열 인덱스로 활용할 컬럼이름, 데이터로 사용할 컬럼이름) 을 입력한다
# - https://datascienceschool.net/view-notebook/76dcd63bba2c4959af15bec41b197e7c/
mean_pivot = mean_pat.pivot("icustay_id", 'mins', 'valuenum')
mean_pivot<jupyter_output><empty_output><jupyter_text>missing value- If any individual vital signs were missing for model estimation then the previous value was pulled forward. If no prior values were available then a median value was imputed, similar to prior work in this area.- Churpek, <NAME>., <NAME>, and <NAME>. "The value of vital sign trends for detecting clinical deterioration on the wards." Resuscitation 102 (2016): 1-5.<jupyter_code># missing data visualization with heatmap
import seaborn as sns

plt.figure(figsize=(20, 10))

sns.heatmap(mean_pivot.isnull(), yticklabels=False, cbar=False, cmap='viridis')
# 결측값 여부 확인 : isnull(), notnull()
# 열별 결측값 갯수 : df.isnull().sum()
# 행별 결측값 갯수 : df.isnull().sum(axis=1)

mean_pivot.isnull().sum(axis=1).sort_values(ascending=True)
mean_pivot.loc[mean_pivot.isnull().sum(axis=1).sort_values(ascending=True).index]
mean_pivot.columns
len(mean_pivot.columns)
# missing value가 전체 데이터 갯수의 절반 이상인 경우 제외
mean_pivot = mean_pivot.loc[mean_pivot.isnull().sum(axis=1)<(len(mean_pivot.columns)/2)]
mean_pivot<jupyter_output><empty_output><jupyter_text>결측값 처리하기- 특정 값으로 채우기 : df.fillna(0)- 결측값을 앞 방향(forward) 혹은 뒷 방향(backward)으로 채우기 : 앞 df.fillna(method='ffill') or df.fillna(method='pad') 뒤 df.fillna(method='bfill')    - 앞/뒤 방향으로 결측값 채우는 횟수를 제한하기 : df.fillna(method='ffill', limit=number)- 결측값을 변수별 평균으로 대체하기 : df.fillna(df.mean()), df.where(pd.notnull(df), df.mean(), axis='columns')- 결측값을 다른 변수의 값으로 대체하기시계열 데이터에서 결측값 보간 하기https://rfriend.tistory.com/264?category=675917<jupyter_code># 나머지 missing value 채우기
mean_pivot = mean_pivot.fillna(method='ffill', limit=1, axis=1)
mean_pivot
mean_pivot[mean_pivot.index==200282.0]
mean_pivot.median(axis=1)
# mean_pivot.fillna(mean_pivot.median(axis=1))
mean_pivot = mean_pivot.where(pd.notnull(mean_pivot), mean_pivot.median(axis=1), axis=0)
mean_pivot
# 확인
mean_pivot[mean_pivot.index==200282.0]
# 시각화해보기 - 전체 데이터는 차트가 너무 지저분해서 일부만을 시각화함(해당 코드의 필요성 고민)
fig = plt.figure(figsize=(20, 10))
index = 0
for m_ind in mean_pivot.index:
    if index % 30 == 0:
        row = mean_pivot.xs(m_ind)
    #     print(row.get_values())
        plt.plot(mean_pivot.columns, row.get_values(), linestyle='-')
    index += 1

plt.title("Change of FiO2 from admission")
plt.xlabel("Minutes after admission")
plt.ylabel("% of FiO2")<jupyter_output><empty_output><jupyter_text>Vanilla LSTM<jupyter_code>from keras.models import Sequential
from keras.layers import LSTM, Dense
mean_pivot.shape
n_steps = mean_pivot.shape[1]-1
n_features = 1
mean_n = mean_pivot.iloc[:, :-1].values
Y = mean_pivot.iloc[:, -1].values
X = mean_n.reshape((mean_pivot.shape[0], n_steps, n_features))
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.2, random_state=10, shuffle=True)
X_train.shape
X_test.shape
model = Sequential()
model.add(LSTM(50, activation='relu', input_shape=(n_steps, n_features)))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
model.fit(X_train, y_train, epochs=200, verbose=0)
y_predict = model.predict(X_test, verbose=0)<jupyter_output><empty_output>