{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Linear Decay and Quantile Regression Inference Notebook\n\n\nThis version started Sep 23, 2020. Submission: Pending\n- 2 models, one linear decay, and one quantile regression, trained on 5 different strategized Kfolds each.\n- Use tabular features only. \n- Linear Decay Model predicts the slope of the fvc vs weeks line, then makes inference on fvc values\n    - The slope is also used to build a heuristic confidence band (a.k.a. sigma) aroud the fvc predictions\n    - A customized `sigma` function applies to predictions from each fold. \n    - 8 features used in all 5 folds.\n    \n- Quantile Regression Model predicts FVC directly. \n    - 7 or 8 features for each fold, depending on performance during development. \n    \n    \n    \n- Models are ready, no need to pre-build. \n    - Quantile regression models need to be loaded with `compile = False` and compile separetely\n\nreference: colab notebooks `OSIC_linear_decay_and_quantile_02.ipynb` and  `OSIC_quantile_02Sep.ipyn`"},{"metadata":{},"cell_type":"markdown","source":"To DO:\n\n- submit predictions of Linear Decay Model trained with fold 0 [done]\n- submit predictions of Linear Decay Model trained with fold 1 [done]\n- submit predictions of Linear Decay Model trained with fold 2 [done]\n- submit predictions of Linear Decay Model trained with fold 3 [done]\n- submit predictions of Linear Decay Model trained with fold 4 [done]\n\nSep 24\n- submit predictions of Quantile Regression Model trained with fold 0 [done]\n- submit predictions of Quantile Regression Model trained with fold 1 [done]\n- submit predictions of Quantile Regression Model trained with fold 2 [done]\n- submit predictions of Quantile Regression Model trained with fold 3 [done]\n- submit predictions of Quantile Regression Model trained with fold 4 [done]\n\nSep 25\n- submit simple mean of Quantile Regression Model, all folds [done]\n- submit simple mean of Linear Decay Model, all folds [done]\n- submit simple mean of Linear Decay Model (all) + Quantile Reg (all), all folds [this run]"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import os, sys\nimport numpy as np\nimport pandas as pd\n\nimport tensorflow as tf\n\nfrom IPython.display import display\npd.set_option('display.max_columns', 50)\n\ntf_version = tf.__version__\nprint(\"\\nTensorflow version \" + tf_version)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Initial Data Preprocessing"},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"input_path = '../input/osic-pulmonary-fibrosis-progression'\npretrained_path = '../input/osic-linear-decay-and-quant-reg-base/pretrained_weights'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def height_proxy(fvc_e, age, sex):\n    # src: https://en.wikipedia.org/wiki/Vital_capacity\n    if sex == 'Female': h = fvc_e/(21.78-0.101*age)\n    else: h = fvc_e/(27.63-0.112*age)\n    return h\n\ndef process_init_week(df, train_df = False):\n    if train_df:\n        df['min_week'] = df.groupby('Patient')['Weeks'].transform('min')\n\n    base = df.loc[df.Weeks == df.min_week][['Patient', 'FVC', 'Percent', 'Age', 'Sex']]\n    base['FVC_init_avg']= base.groupby('Patient')['FVC'].transform('mean').astype(int)\n    #base['FVC_init_first']= base.groupby('Patient')['FVC'].transform('first')\n    base['Percent_init']= base.groupby('Patient')['Percent'].transform('mean')\n    base = base[['Patient', 'FVC_init_avg', 'Percent_init', 'Age', 'Sex']].drop_duplicates()\n    base['FVC_expected'] = (base['FVC_init_avg'] / (base['Percent_init']/100))\n    base['Height_proxy'] = base.apply(lambda x: height_proxy(x.FVC_expected, x.Age, x.Sex), axis=1)\n    base = base[['Patient', 'Height_proxy', 'FVC_init_avg', 'Percent_init']]\n\n    df = df.merge(base, on='Patient', how='left')\n    df['init_week'] = df['Weeks'] - df['min_week']\n    return df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train = pd.read_csv(input_path + '/train.csv')\ntrain = process_init_week(train, train_df = True)\ntrain.drop_duplicates(keep='first', inplace=True, subset=['Patient','Weeks'])\ntrain.head(3)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sub = pd.read_csv(input_path + '/sample_submission.csv') \ntest = pd.read_csv(input_path + '/test.csv')\n\nsub['Patient'] = sub['Patient_Week'].apply(lambda x:x.split('_')[0])\nsub['Weeks'] = sub['Patient_Week'].apply(lambda x: int(x.split('_')[-1]))\nsub =  sub[['Patient','Weeks','Confidence','Patient_Week']]\n\ntest = test.rename(columns={'Weeks': 'min_week'})\nsub = sub.merge(test, on='Patient')\n\nsub = process_init_week(sub, train_df = False)\nsub.head(3)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def scale_fn(var_name):\n    col = train[var_name]\n    return lambda x: (x - col.min())/(col.max()- col.min())\n\nscale_age = scale_fn('Age')\nscale_height = scale_fn('Height_proxy')\nscale_percent = scale_fn('Percent')\nscale_fvc = scale_fn('FVC_init_avg')\n\n# scale week manually. weeks on test data go from -12 to 133.\nscale_week = lambda x: (x - (-12))/(133-(-12))\n\ndef transform_features(df):\n    df = df.assign(sex_code = np.where(df['Sex'] == 'Female', 1, 0))\n    df = df.assign(ex_smoker = np.where(df['SmokingStatus'] == 'Ex-smoker', 1, 0))\n    df = df.assign(never_smoked = np.where(df['SmokingStatus'] == 'Never smoked', 1, 0))\n    df = df.assign(current_smoker = np.where(df['SmokingStatus'] == 'Currently smokes', 1, 0))\n    df['has_smoked'] = df['ex_smoker'] + df['current_smoker']\n\n    df['age'] = df['Age'].map(scale_age)\n    df['height'] = df['Height_proxy'].map(scale_height)\n    df['percent'] = df['Percent'].map(scale_percent)\n    df['percent_init'] = df['Percent_init'].map(scale_percent) # scale the same as Percent \n    df['week'] = df['Weeks'].map(scale_week) # this is to original week, use init week for validation analysis\n    df['fvc_init'] = df['FVC_init_avg'].map(scale_fvc) # can change 'FVC_init_avg' to 'FVC_init_first' see data exploration notes\n    return df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train = transform_features(train)\ntrain.reset_index(inplace=True, drop = True)\ntrain.head(3)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sub = transform_features(sub)\nsub.head(3)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Data for Linear Decay Model\n\n- Gathers the features using only the inital values. That is the values of the week in which fvc was first measures i.e. `init_week == 0.`\n- 1 row per patient"},{"metadata":{"trusted":true},"cell_type":"code","source":"linear_decay_features = ['age', 'sex_code', 'has_smoked', 'current_smoker', 'height', 'percent_init', 'fvc_init']\n\ndef get_patient_tab(df): # df is either train or sub\n    patients_init = df[df['init_week'] == 0].copy()\n    patients_init = patients_init[['Patient']+ linear_decay_features]\n    patients_init.set_index('Patient', inplace = True)\n    return patients_init\n\npatients_tab_train = get_patient_tab(train)\npatients_tab_test = get_patient_tab(sub)\nprint(patients_tab_train.shape)\ndisplay(patients_tab_train.head(3))\npatients_tab_test","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Inference"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Start a dataframe to put all predictons from both models\nPREDICTIONS = sub[['Patient', 'Weeks', 'Patient_Week']].copy()\nPREDICTIONS.head(5)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Inference Linear Decay"},{"metadata":{"trusted":true},"cell_type":"code","source":"LD_inference = pd.read_csv(pretrained_path + '/inference_linear_decay_2020Sep19.csv')\nLD_inference","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"LD_test = patients_tab_test.reset_index()\npred_cols = ['Patient', 'Weeks', 'Patient_Week', 'FVC_init_avg', 'init_week']\nreturn_cols = ['Patient', 'Weeks', 'Patient_Week', 'FVC_hat', 'sigma']\n\ndef get_sigma_function(s_intercept, s_multiplier, s_power):\n    def alt_sigma(coeff, init_week):\n        coeff = abs(coeff)\n        week_distance = abs(init_week)\n        sigma = s_intercept + s_multiplier*coeff*(week_distance**s_power)\n        return sigma\n    return alt_sigma\n\ndef pred_test(model, sigma_fn):\n    X = LD_test[linear_decay_features].copy()\n    XID = LD_test[['Patient']].copy()\n    XID['coeff_pred'] = model.predict(X, batch_size = 32)\n\n    P = sub[pred_cols].copy()\n    P = P.merge(XID, how='left', on='Patient')\n\n    P['FVC_hat'] = P['FVC_init_avg'] + (P['coeff_pred'] * P['init_week'])\n    P['sigma'] = P.apply(lambda x: sigma_fn(x.coeff_pred, x.init_week), axis = 1)\n    return P[return_cols]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for fold_num in range(5):\n    prefix = LD_inference.loc[fold_num].prefix\n    fname = '{}/{}_weights.h5'.format(pretrained_path, prefix)\n    s_intercept, s_multiplier, s_power = eval(LD_inference.loc[fold_num].alt_sigma_param)\n    f_sigma = get_sigma_function(s_intercept, s_multiplier, s_power)\n\n    model = tf.keras.models.load_model(fname)\n    P = pred_test(model, sigma_fn = f_sigma)\n    PREDICTIONS['FVC_LD{}'.format(fold_num)] = P['FVC_hat']\n    PREDICTIONS['Confidence_LD{}'.format(fold_num)] = P['sigma']\n    \ndel P, fold_num, fname, model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"PREDICTIONS","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Inference Quantile Regression"},{"metadata":{"trusted":true},"cell_type":"code","source":"QR_inference = pd.read_csv(pretrained_path + '/inference_quant_reg_2020Sep23.csv')\nQR_inference","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"qr_features8 = ['fvc_init','week', 'sex_code', 'age', 'height', 'has_smoked', 'current_smoker', 'percent_init']\nqr_features7 = ['fvc_init','week', 'sex_code', 'age', 'has_smoked', 'current_smoker', 'percent_init']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for fold_num in range(5):\n    prefix = QR_inference.loc[fold_num].prefix\n    fname = '{}/{}_weights.h5'.format(pretrained_path, prefix)\n    model = tf.keras.models.load_model(fname, compile = False)\n    model.compile(loss='mae', optimizer='adam', metrics=['mae'])\n\n    num_features = QR_inference.loc[fold_num].num_features\n    if num_features == 7: features = qr_features7\n    else: features = qr_features8\n\n    X = sub[features].copy()\n    preds = model.predict(X)\n\n    PREDICTIONS['FVC_QR{}'.format(fold_num)] = preds[:,1]\n    PREDICTIONS['Confidence_QR{}'.format(fold_num)] = preds[:,2] - preds[:,0]\n\ndel X, fold_num, fname, model, prefix, num_features, features, preds","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Get FVC combinations (mean, weighted average, etc)"},{"metadata":{"trusted":true},"cell_type":"code","source":"QR_FVC_cols = [col for col in PREDICTIONS.columns if 'FVC_QR' in col]\nLD_FVC_cols = [col for col in PREDICTIONS.columns if 'FVC_LD' in col]\nQR_Conf_cols = [col for col in PREDICTIONS.columns if 'Confidence_QR' in col]\nLD_Conf_cols = [col for col in PREDICTIONS.columns if 'Confidence_LD' in col]\n\nPREDICTIONS['QR_FVC_mean'] = PREDICTIONS[QR_FVC_cols].mean(axis = 1)\nPREDICTIONS['QR_Conf_mean'] = PREDICTIONS[QR_Conf_cols].mean(axis = 1)\nPREDICTIONS['LD_FVC_mean'] = PREDICTIONS[LD_FVC_cols].mean(axis = 1)\nPREDICTIONS['LD_Conf_mean'] = PREDICTIONS[LD_Conf_cols].mean(axis = 1)\n\n# mean of all LD + QR\nPREDICTIONS['QR_LD_FVC_mean'] = PREDICTIONS[['QR_FVC_mean','LD_FVC_mean']].mean(axis = 1)\nPREDICTIONS['QR_LD_Conf_mean'] = PREDICTIONS[['QR_Conf_mean','LD_Conf_mean']].mean(axis = 1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"PREDICTIONS","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Readying submission"},{"metadata":{"trusted":true},"cell_type":"code","source":"to_submit = PREDICTIONS[['Patient_Week','QR_LD_FVC_mean', 'QR_LD_Conf_mean']]\nto_submit.columns = ['Patient_Week','FVC','Confidence']\nto_submit","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"to_submit.describe().T","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"to_submit.to_csv('submission.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!head -3 submission.csv","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}