{"cells":[{"metadata":{"_uuid":"6be09a5b5d693f26fdaed67169830874d74228e5"},"cell_type":"markdown","source":"This kernel try to use PCA to reduce dimention and train model with XGBoost. The final result is acceptable. Thanks for above kernels:\n* Memory Saving: https://www.kaggle.com/sarmat/sklearn-lgbm-ensemble-baseline\n* Data Exploration: https://www.kaggle.com/theoviel/starter-code-eda-and-lgbm-baseline\n* Data Preprocess: https://www.kaggle.com/stuartbman/introduction-to-physiological-data/comments"},{"metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true,"_uuid":"c49ff70c4eefab027dd1783eb9fcf5e1964a5027"},"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom tqdm import tqdm_notebook as tqdm\nimport os\nprint(os.listdir(\"../input\"))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"33b4d9c69accd9327b868c01c4ce1a3da8a4b285"},"cell_type":"markdown","source":"# 1. Import Data"},{"metadata":{"trusted":true,"_uuid":"9d700df2e429f7e348a15e3e1a8c593e62a5710c"},"cell_type":"code","source":"#https://www.kaggle.com/gemartin/load-data-reduce-memory-usage\ndef reduce_mem_usage(df):\n    \"\"\" iterate through all the columns of a dataframe and modify the data type\n        to reduce memory usage.        \n    \"\"\"\n    start_mem = df.memory_usage().sum() / 1024**2\n    print('Memory usage of dataframe is {:.2f} MB'.format(start_mem))\n    \n    for col in df.columns:\n        col_type = df[col].dtype\n        \n        if col_type != object:\n            c_min = df[col].min()\n            c_max = df[col].max()\n            if str(col_type)[:3] == 'int':\n                if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:\n                    df[col] = df[col].astype(np.int8)\n                elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:\n                    df[col] = df[col].astype(np.int16)\n                elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:\n                    df[col] = df[col].astype(np.int32)\n                elif c_min > np.iinfo(np.int64).min and c_max < np.iinfo(np.int64).max:\n                    df[col] = df[col].astype(np.int64)  \n            else:\n                if c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:\n                    df[col] = df[col].astype(np.float16)\n                elif c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max:\n                    df[col] = df[col].astype(np.float32)\n                else:\n                    df[col] = df[col].astype(np.float64)\n        else:\n            df[col] = df[col].astype('category')\n\n    end_mem = df.memory_usage().sum() / 1024**2\n    print('Memory usage after optimization is: {:.2f} MB'.format(end_mem))\n    print('Decreased by {:.1f}%'.format(100 * (start_mem - end_mem) / start_mem))\n    \n    return df\n\n\ndef import_data(file):\n    \"\"\"create a dataframe and optimize its memory usage\"\"\"\n    df = pd.read_csv(file, parse_dates=True, keep_date_col=True)\n    df = reduce_mem_usage(df)\n    return df\n\nimport matplotlib.pyplot as plt\nimport itertools\ndef plot_confusion_matrix(cm, classes,\n                          normalize=True,\n                          title='Confusion matrix',\n                          cmap=plt.cm.Blues):\n    \"\"\"\n    This function prints and plots the confusion matrix.\n    Normalization can be applied by setting `normalize=True`.\n    \"\"\"\n    if normalize:\n        cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]\n        print(\"Normalized confusion matrix\")\n    else:\n        print('Confusion matrix, without normalization')\n\n    print(cm)\n\n    plt.imshow(cm, interpolation='nearest', cmap=cmap)\n    plt.title(title)\n    plt.colorbar()\n    tick_marks = np.arange(len(classes))\n    plt.xticks(tick_marks, classes, rotation=45)\n    plt.yticks(tick_marks, classes)\n\n    fmt = '.2f' if normalize else 'd'\n    thresh = cm.max() / 2.\n    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):\n        plt.text(j, i, format(cm[i, j], fmt),\n                 horizontalalignment=\"center\",\n                 color=\"white\" if cm[i, j] > thresh else \"black\")\n\n    plt.ylabel('True label')\n    plt.xlabel('Predicted label')\n    plt.tight_layout()\n    \n\ndf_train = import_data(\"../input/train.csv\")\ndf_train.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"6037a130830a855f49690bb3127ea8efdd5d6944"},"cell_type":"code","source":"df_train.head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"5962de05fb97b620274365a406061fcdfdda1a5f"},"cell_type":"markdown","source":"# 2. Data Exploration\n## 2.1 Target/Experiment"},{"metadata":{"trusted":true,"_uuid":"c335e70a3da0a1060cf500b450e9355278987495"},"cell_type":"code","source":"plt.style.use('ggplot')\nplt.figure(figsize=(13,5))\n\nplt.subplot(1,3,1)\nsns.countplot(df_train['event'])\nplt.xlabel(\"State of the pilot\", fontsize=12)\nplt.ylabel(\"Count\", fontsize=12)\nplt.title(\"Target repartition\", fontsize=15)\n\nplt.subplot(1,3,2)\nsns.countplot('experiment', hue='event', data=df_train)\nplt.xlabel(\"Experiment and state of the pilot\", fontsize=12)\nplt.ylabel(\"Count (log)\", fontsize=12)\nplt.yscale('log')\nplt.title(\"Experiments\", fontsize=15)\n\nplt.subplot(1,3,3)\nsns.countplot('event', hue='seat', data=df_train)\nplt.xlabel(\"Seat and state of the pilot\", fontsize=12)\nplt.ylabel(\"Count (log)\", fontsize=12)\nplt.yscale('log')\nplt.title(\"Seat\", fontsize=15)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"ebe202440f85f6b13e3aa73e53732a17769e6dc4"},"cell_type":"markdown","source":"## 2.2 Respiration\nThis is a simple measure of the rise and fall of the chest. It represents muscle activity of the diaphragm and abdomen. We know that when someone is physiologically stressed, this rate increases."},{"metadata":{"trusted":true,"_uuid":"a3cba555bd2c1cbc7a8a866e150cd4db0b0bb966"},"cell_type":"code","source":"# Just looking at a single trial for now\nsubset = df_train.loc[(df_train['crew'] == 1) & (df_train['experiment'] == 'CA')]\nsubset.sort_values(by='time')\n\n# remove the high frequency signals by scipy\nfrom scipy import signal\nb, a = signal.butter(8,0.05)\ny = signal.filtfilt(b, a, subset['r'], padlen=150)\n\nplt.style.use('seaborn')\nplt.figure(figsize=(15,5))\nplt.subplot(1,2,1)\nplt.title(\"Respiratoin Trend\", fontsize=15)\nplt.plot(subset['r'][3000:4024])\nplt.subplot(1,2,2)\nplt.plot(y[3000:4024])\nplt.title(\"Respiratoin Trend after remove the high frequency signals\", fontsize=15)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"e1dce3a352d095b30575e2ed4461a2de1df49574"},"cell_type":"markdown","source":"Use Biosppy to filter data to get useful insignts and count the respiration rate"},{"metadata":{"trusted":true,"_uuid":"3d560854a69d7ead4132a27abcab6db852510838"},"cell_type":"code","source":"from biosppy.signals import ecg, resp\n\nout = resp.resp(y,sampling_rate=256, show=False)\n\nplt.plot(out['resp_rate_ts'], out['resp_rate'])\nplt.ylabel('Respiratory frequency [Hz]')\nplt.xlabel('Time [s]')\nplt.title(\"Respiratoin Rate\", fontsize=15)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"614212e8830a12d6796c6f5490449a843caae1c5"},"cell_type":"markdown","source":"## 2.3 Electrocardiogram (ECG)"},{"metadata":{"trusted":true,"_uuid":"9d0f33d6cd52fc3b2a64a8d21b7a7ff432b8695f"},"cell_type":"code","source":"plt.style.use('bmh')\nplt.figure(figsize=(15,5))\nplt.subplot(1,2,1)\nsns.violinplot(x='event', y='ecg', data=df_train.sample(50000))\nplt.ylabel(\"Electrocardiogram Signal (µV)\", fontsize=12)\nplt.xlabel(\"Event\", fontsize=12)\nplt.title(\"Electrocardiogram signal influence\", fontsize=15)\n\nplt.subplot(1,2,2)\nsns.distplot(df_train['ecg'], label='Train set')\nplt.legend()\nplt.xlabel(\"Electrocardiogram Signal (µV)\", fontsize=12)\nplt.title(\"Electrocardiogram Signal Distribution\", fontsize=15)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"7d7237ac7c5abf2054cf344b03a51e0bfa017c43"},"cell_type":"code","source":"plt.figure(figsize=(15,5))\n\nplt.subplot(1,2,1)\nb, a = signal.butter(8,0.05)\ny = signal.filtfilt(b, a, subset['ecg'], padlen=150)\nplt.plot(y[3000:4024])\nplt.title(\"Electrocardiogram Trend\", fontsize=15)\n\n#Convert ECG into heart rate data by Biosppy to detect the R waves\nplt.subplot(1,2,2)\nout = ecg.ecg(signal=subset['ecg'], sampling_rate=256, show=False)\nplt.plot(out['heart_rate_ts'], out['heart_rate'])\nplt.ylabel('Heart Rate (BPM)')\nplt.xlabel('Time [s]');\nplt.title(\"Electrocardiogram Heart Rate\", fontsize=15)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"ede975d0e084d32c91d02dd0558a4896c28803aa"},"cell_type":"markdown","source":"## 2.4 Electroencephalogram (EEG)"},{"metadata":{"trusted":true,"_uuid":"b10bafa4757043c36328cdba9f98683d5183d0e5"},"cell_type":"code","source":"eeg_features = [\"eeg_fp1\", \"eeg_f7\", \"eeg_f8\", \"eeg_t4\", \"eeg_t6\", \"eeg_t5\", \"eeg_t3\", \"eeg_fp2\", \"eeg_o1\", \"eeg_p3\", \"eeg_pz\", \"eeg_f3\", \"eeg_fz\", \"eeg_f4\", \"eeg_c4\", \"eeg_p4\", \"eeg_poz\", \"eeg_c3\", \"eeg_cz\", \"eeg_o2\"]\n\nplt.figure(figsize=(20,25))\n\ni = 0\nfor egg in eeg_features:\n    i += 1\n    plt.subplot(5, 4, i)\n    sns.boxplot(x='event', y=egg, data=df_train.sample(50000), showfliers=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"699de2a771246e5ff2426226ab0901e67b64c47c"},"cell_type":"code","source":"plt.figure(figsize=(20,25))\nplt.title('EEG features distributions')\ni = 0\n\nfor eeg in eeg_features:\n    i += 1\n    plt.subplot(5, 4, i)\n    sns.distplot(df_train.sample(10000)[eeg], label='Train set', hist=False)\n    plt.xlim((-500, 500))\n    plt.legend()\n    plt.xlabel(eeg, fontsize=12)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"4397d897b397448cb933bba603d24c6001b97407"},"cell_type":"markdown","source":"# 3. Data Preprocess\n## 3.1 Electroencephalogram recordings\nThe EEG data is prepared in a fairly typical arrangement of 20 electrodes across the scalp. The letter in each lead signifies the part of the brain that that lead is nearest to (Temporal, Frontal, Parietal etc), with odd numbers on the left, evens on the right. Usually in the clinic, we don't look at the electrical potentials at each electrode, but at the potential difference between pairs of electrodes. This gives us an idea of the electrical field in the brain region between these two points as a way to infer what the brain is doing in that region. \n\n![10-20 Montage system](https://ai2-s2-public.s3.amazonaws.com/figures/2017-08-08/f2861bea35e87ac1fe5c053e4cc58911d28d112f/3-Figure1-1.png)"},{"metadata":{"_uuid":"92a2d0920810f554bd8e8a3e4b925cda7266ecf2"},"cell_type":"markdown","source":"For this experiment, I chose the middle montage because it's one that's used clinically and I'm familiar with. To montage the data, you just have to subtract the value of one electrode from another."},{"metadata":{"trusted":true,"_uuid":"7d40e1eb4039b1e8ce2721752ddbcdd71df2224c"},"cell_type":"code","source":"df_train['fp1_f7'] = df_train['eeg_fp1'] - df_train['eeg_f7']\ndf_train['f7_t3'] = df_train['eeg_f7'] - df_train['eeg_t3']\ndf_train['t3_t5'] = df_train['eeg_t3'] - df_train['eeg_t5']\ndf_train['t5_o1'] = df_train['eeg_t5'] - df_train['eeg_o1']\ndf_train['fp1_f3'] = df_train['eeg_fp1'] - df_train['eeg_f7']\ndf_train['f3_c3'] = df_train['eeg_f3'] - df_train['eeg_c3']\ndf_train['c3_p3'] = df_train['eeg_c3'] - df_train['eeg_p3']\ndf_train['p3_o1'] = df_train['eeg_p3'] - df_train['eeg_o1']\n\ndf_train['fz_cz'] = df_train['eeg_fz'] - df_train['eeg_cz']\ndf_train['cz_pz'] = df_train['eeg_cz'] - df_train['eeg_pz']\ndf_train['pz_poz'] = df_train['eeg_pz'] - df_train['eeg_poz']\n\ndf_train['fp2_f8'] = df_train['eeg_fp2'] - df_train['eeg_f8']\ndf_train['f8_t4'] = df_train['eeg_f8'] - df_train['eeg_t4']\ndf_train['t4_t6'] = df_train['eeg_t4'] - df_train['eeg_t6']\ndf_train['t6_o2'] = df_train['eeg_t6'] - df_train['eeg_o2']\ndf_train['fp2_f4'] = df_train['eeg_fp2'] - df_train['eeg_f4']\ndf_train['f4_c4'] = df_train['eeg_f4'] - df_train['eeg_c4']\ndf_train['c4_p4'] = df_train['eeg_c4'] - df_train['eeg_p4']\ndf_train['p4_o2'] = df_train['eeg_p4'] - df_train['eeg_o2']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"18bf3b1de0532359d9a39c29448d4a1945479b8e"},"cell_type":"code","source":"df_train.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"1935ab561430c9a519c65b1cc2f4ff44b38eddd0"},"cell_type":"code","source":"df_train.shape","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"49a17014980d7fccefa654acd5f20c0b1fa76b05"},"cell_type":"markdown","source":"## 3.2 OneHotEncoding"},{"metadata":{"trusted":true,"_uuid":"35615f6c24715fe3a5ae8643dda730ad9210b7fc"},"cell_type":"code","source":"df_train.info()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"48bc704ca6d930f426721de3feadc716ef6ccb44"},"cell_type":"markdown","source":"The datatype of experiment and event is category. It need be handle with onehotencoding."},{"metadata":{"trusted":true,"_uuid":"e531883c28481b8f70d906e59bf205da24bce2cb"},"cell_type":"code","source":"from sklearn.preprocessing import LabelEncoder\nLE1 = LabelEncoder()\nLE2 = LabelEncoder()\n\ndf_train['experiment'] = LE1.fit_transform(df_train['experiment'])\ndf_train['event'] = LE2.fit_transform(df_train['event'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"4d7ab0797a5766ec4e3810f8b9ab51c15c4f14f9"},"cell_type":"code","source":"df_train.head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"71ca904d3eebf6b2b71eaeaa8ab613fa22543feb"},"cell_type":"markdown","source":"## 3.3 Normalizing\nWe apply a Min/Max Scaler for each pilot."},{"metadata":{"trusted":true,"_uuid":"612864637be04d52cb91338675ebad7f9aca77f6"},"cell_type":"code","source":"df_train['pilot'] = 100 * df_train['seat'] + df_train['crew']\nprint(\"Number of pilots : \", len(df_train['pilot'].unique()))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"145bff3333a7fd37a4693b61cc6508d35b5502ca"},"cell_type":"code","source":"df_train.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"64b110a90d5f9a021c540f29f79522566f65a741"},"cell_type":"code","source":"from sklearn.preprocessing import MinMaxScaler\ndef normalize_by_pilots(df):\n    pilots = df[\"pilot\"].unique()\n    for pilot in tqdm(pilots):\n        ids = df[df[\"pilot\"] == pilot].index\n        scaler = MinMaxScaler()\n        df.loc[ids, features_m] = scaler.fit_transform(df.loc[ids, features_m])\n    return df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"3ac83e1ff7b8da07be97c9f04dafe1c667e7f2d0"},"cell_type":"code","source":"features_m = [\"eeg_fp1\", \"eeg_f7\", \"eeg_f8\", \"eeg_t4\", \"eeg_t6\", \"eeg_t5\", \"eeg_t3\", \"eeg_fp2\", \"eeg_o1\", \"eeg_p3\", \"eeg_pz\", \"eeg_f3\", \"eeg_fz\", \"eeg_f4\", \"eeg_c4\", \"eeg_p4\", \"eeg_poz\", \"eeg_c3\", \"eeg_cz\", \"eeg_o2\", \"ecg\", \"r\", \"gsr\",'fp1_f7', 'f7_t3', 't3_t5', 't5_o1', 'fp1_f3', 'f3_c3', 'c3_p3', 'p3_o1', 'fz_cz', 'cz_pz',\n                'pz_poz', 'fp2_f8', 'f8_t4', 't4_t6', 't6_o2', 'fp2_f4', 'f4_c4', 'c4_p4', 'p4_o2']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":true,"_uuid":"5a5cb5b659502d475325f0d7c499ddd7d38d9443"},"cell_type":"code","source":"df_train = normalize_by_pilots(df_train)\ndf_train.head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"2336b6bf8b2217f1ec2300b26dcd677806a3e946"},"cell_type":"markdown","source":" ## 3.4 Split data as feature & Label"},{"metadata":{"trusted":true,"_uuid":"8a8a8a0dace5081217f402ee48f3188715e19217"},"cell_type":"code","source":"y = df_train['event']\ndf_train.drop(['event'], axis=1, inplace=True)\ndf_train.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"e2df7ccc32c6773f96dc357cd033baae83f0bc53"},"cell_type":"code","source":"df_train.head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"ff92f0b9bf07052d07bd2aeadde5069c58f65660"},"cell_type":"markdown","source":"# 4. PCA\n## 4.1 PCA dimension Survey"},{"metadata":{"trusted":true,"_uuid":"47c73286f57c1350b3cc6a9f3bb9f07ece3658af"},"cell_type":"code","source":"from sklearn.decomposition import PCA\nimport matplotlib.pyplot as plt\n%matplotlib inline\nlista = range(1,47)\naa=[]\nbb=[]\nfor f in tqdm(lista):\n    aa.append(f)\n    pca = PCA(n_components=f).fit(df_train)\n    \na = 0\nfor e in lista:\n    a=a+pca.explained_variance_ratio_[e-1]\n    bb.append(a)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"dbd159a3a3e226f8c4de2054de6f4cdc32f2b7ac"},"cell_type":"code","source":"fig, ax = plt.subplots(figsize = (10,6))\nplot = plt.plot(aa, bb, '-o')\nax.set_xlabel(\"Dimensions\")\nax.set_ylabel(\"Variance_ratio\"); ","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"0e246381ce5cfd4ee7b76c3c400ef70d8a580b7f"},"cell_type":"markdown","source":"## 4.2 PCA for dimension reduction\nBased on PCA result, we reduce dimension from 47 to 10 in order to avoid overfit problem."},{"metadata":{"trusted":true,"_uuid":"a4d841f9e511d6a14052a3677ebe4ee96bffc663"},"cell_type":"code","source":"# Apply PCA for dimension reduction\npca = PCA(n_components=10).fit(df_train)\nX_pca = pca.transform(df_train)\nprint(sum(pca.explained_variance_ratio_)) ","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"bfb114cf288005a0059859c03c8d6f0e30dc4b1f"},"cell_type":"markdown","source":"# 5. Model Training\n## 5.1 Split data as training & testing set"},{"metadata":{"trusted":true,"_uuid":"1bb8700d9712cbdc8e26c9385254211ee79cbba3"},"cell_type":"code","source":"from sklearn.model_selection import train_test_split\nX_train, X_val, y_train, y_val = train_test_split(X_pca, y, test_size = 0.2)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"24508a837746228d8b97ae66be43e4b01bd5b3b1"},"cell_type":"markdown","source":"## 5.2 Imbalance learning"},{"metadata":{"trusted":true,"_uuid":"d323ef9d72764cbc6690ee9dec8f507c13dd6395"},"cell_type":"code","source":"from imblearn.over_sampling import SMOTE\nX_train, y_train = SMOTE().fit_resample(X_train, y_train.ravel())","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"6b66ae7aeee9175963f1cb279987ae876c8cf804"},"cell_type":"markdown","source":"## 5.3 Model Training"},{"metadata":{"trusted":true,"_uuid":"41bae7ee385003ceda5d5707bda07059d576f259"},"cell_type":"code","source":"from sklearn.metrics import accuracy_score\nfrom xgboost import XGBClassifier\n\nparams = {\"objective\" : \"multiclass\",\n              \"num_class\": 4,\n              \"metric\" : \"multi_error\",\n              \"num_leaves\" : 30,\n              \"min_child_weight\" : 50,\n              \"learning_rate\" : 0.1,\n              \"bagging_fraction\" : 0.7,\n              \"feature_fraction\" : 0.7,\n              \"bagging_seed\" : 420,\n              \"verbosity\" : -1\n         }\n\nXGB = XGBClassifier()\n\n# XGB = XGBClassifier(max_depth=5, learning_rate=0.01, n_estimators=100, gamma=0, min_child_weight=1, subsample=0.8, colsample_bytree=0.8, reg_alpha=0.005)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"8eb25bdb1c04b7c681a82b227056c947e3565397"},"cell_type":"code","source":"XGB.fit(X_train,y_train)\nprint (\"accuracy_score on testing data of XGBoost: {:.4f}\".format(accuracy_score(y_val, XGB.predict(X_val))))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"aa620bd96a423fdc77710c0e74df6f53ca825523"},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}