{"cells":[{"metadata":{"trusted":true,"_uuid":"332fa118684da9a9278d2caf565620686fd73a68"},"cell_type":"markdown","source":"# Elo world \n\nIn this kernel, I build a LGBM model that aggregates the `new_merchant_transactions.csv` and `historical_transactions.csv` tables to the main train table. New features are built by successive grouping on`card_id` and `month_lag`, in order to recover some information from the time serie.\n\nDuring the competition, I took into account the enlightments provided by others kernels, and included a few features that appeared to be important. In particular, I closely looked at the following kernels (ordered by release time):\n1.  [You're Going to Want More Categories [LB 3.737] by Peter Hurford](https://www.kaggle.com/peterhurford/you-re-going-to-want-more-categories-lb-3-737)\n2. [EloDA with Feature Engineering and Stacking by Bojan Tunguz](https://www.kaggle.com/tunguz/eloda-with-feature-engineering-and-stacking)\n3. [A Closer Look at Date Variables by Robin Denz](https://www.kaggle.com/denzo123/a-closer-look-at-date-variables)\n4. [LGB + FE (LB 3.707) by Konrad Banachewicz](https://www.kaggle.com/konradb/lgb-fe-lb-3-707)\n5. [My first kernel (3.699) by Chau Ngoc Huynh](https://www.kaggle.com/chauhuynh/my-first-kernel-3-699/)\n\n## Notebook  Content\n1. [Loading the data](#1)\n1. [Feature engineering](#2)\n1. [Training the model](#3)\n1. [Feature importance](#4)\n1. [Submission](#5)"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"import numpy as np \nimport pandas as pd \nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport lightgbm as lgb\nfrom sklearn.model_selection import KFold\nimport warnings\nimport time\nimport sys\nimport datetime\nimport matplotlib.pyplot as plt\nfrom sklearn.metrics import mean_squared_error\nwarnings.simplefilter(action='ignore', category=FutureWarning)\npd.set_option('display.max_columns', 500)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"6a059a8dcf9d93a650f1ccaa8e2bfa3e087219f3"},"cell_type":"markdown","source":"<a id=\"1\"></a> <br>\n## 1. Loading the data\n\nFirst, we load the `new_merchant_transactions.csv` and `historical_transactions.csv`. In practice, these two files contain the same variables and the difference between the two tables only concern the position with respect to a reference date.  Also, booleans features are made numeric:"},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"7a7877dff5c337c09ca111cdcbf527362c9217c7"},"cell_type":"code","source":"def reduce_mem_usage(df, verbose=True):\n    numerics = ['int16', 'int32', 'int64', 'float16', 'float32', 'float64']\n    start_mem = df.memory_usage().sum() / 1024**2    \n    for col in df.columns:\n        col_type = df[col].dtypes\n        if col_type in numerics:\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    end_mem = df.memory_usage().sum() / 1024**2\n    if verbose: print('Mem. usage decreased to {:5.2f} Mb ({:.1f}% reduction)'.format(end_mem, 100 * (start_mem - end_mem) / start_mem))\n    return df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"b6b5a505bfecabd70c6dac0ee30386d074b03953"},"cell_type":"code","source":"new_transactions = pd.read_csv('../input/new_merchant_transactions.csv',\n                               parse_dates=['purchase_date'])\n\nhistorical_transactions = pd.read_csv('../input/historical_transactions.csv',\n                                      parse_dates=['purchase_date'])\n\ndef binarize(df):\n    for col in ['authorized_flag', 'category_1']:\n        df[col] = df[col].map({'Y':1, 'N':0})\n    return df\n\nhistorical_transactions = binarize(historical_transactions)\nnew_transactions = binarize(new_transactions)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"156acd1e83dfba2c2896561b75a6a5a7782cab1d"},"cell_type":"markdown","source":"We then load the main files, formatting the dates and extracting the target:"},{"metadata":{"trusted":true,"_uuid":"a24cf99ad6b0785b5af2b101e06b400e26360d1e"},"cell_type":"code","source":"def read_data(input_file):\n    df = pd.read_csv(input_file)\n    df['first_active_month'] = pd.to_datetime(df['first_active_month'])\n    df['elapsed_time'] = (datetime.date(2018, 2, 1) - df['first_active_month'].dt.date).dt.days\n    return df\n#_________________________________________\ntrain = read_data('../input/train.csv')\ntest = read_data('../input/test.csv')\n\ntarget = train['target']\ndel train['target']","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"b3c2a5853f29c314dca418e1ad8420358fbf74fe"},"cell_type":"markdown","source":"<a id=\"2\"></a> <br>\n## Feature engineering\nFollowing [Chau Ngoc Huynh's kernel](https://www.kaggle.com/chauhuynh/my-first-kernel-3-699/), I add the following features:"},{"metadata":{"trusted":true,"_uuid":"f787e3f81121b236528b59d0e0a723bcef510e4b"},"cell_type":"code","source":"historical_transactions['month_diff'] = ((datetime.datetime.today() - historical_transactions['purchase_date']).dt.days)//30\nhistorical_transactions['month_diff'] += historical_transactions['month_lag']\n\nnew_transactions['month_diff'] = ((datetime.datetime.today() - new_transactions['purchase_date']).dt.days)//30\nnew_transactions['month_diff'] += new_transactions['month_lag']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"9db35828b8cb62fbe95a9d30e63b30b49966a304"},"cell_type":"code","source":"historical_transactions[:5]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"b1da58e20da9fbb6d200119b242c168ae8fc5843"},"cell_type":"code","source":"historical_transactions = pd.get_dummies(historical_transactions, columns=['category_2', 'category_3'])\nnew_transactions = pd.get_dummies(new_transactions, columns=['category_2', 'category_3'])\n\nhistorical_transactions = reduce_mem_usage(historical_transactions)\nnew_transactions = reduce_mem_usage(new_transactions)\n\nagg_fun = {'authorized_flag': ['mean']}\nauth_mean = historical_transactions.groupby(['card_id']).agg(agg_fun)\nauth_mean.columns = ['_'.join(col).strip() for col in auth_mean.columns.values]\nauth_mean.reset_index(inplace=True)\n\nauthorized_transactions = historical_transactions[historical_transactions['authorized_flag'] == 1]\nhistorical_transactions = historical_transactions[historical_transactions['authorized_flag'] == 0]","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"73e0936c181e4cec16da05d6987cd96b99ed87d1"},"cell_type":"markdown","source":"First, following [Robin Denz](https://www.kaggle.com/denzo123/a-closer-look-at-date-variables) analysis, I define a few dates features:"},{"metadata":{"trusted":true,"_uuid":"76c41ff3af21c7bfa9194f873f6f47c5849eb4d8"},"cell_type":"code","source":"historical_transactions['purchase_month'] = historical_transactions['purchase_date'].dt.month\nauthorized_transactions['purchase_month'] = authorized_transactions['purchase_date'].dt.month\nnew_transactions['purchase_month'] = new_transactions['purchase_date'].dt.month","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"7493b32cb783d6fb6afdad60964eb41c9e42c2e3"},"cell_type":"markdown","source":"Then I define two functions that aggregate the info contained in these two tables. The first function aggregates the function by grouping on `card_id`:"},{"metadata":{"trusted":true,"_uuid":"82c25c7cd0d075195fb7bd63211c66f0dac9304b"},"cell_type":"code","source":"def aggregate_transactions(history):\n    \n    history.loc[:, 'purchase_date'] = pd.DatetimeIndex(history['purchase_date']).\\\n                                      astype(np.int64) * 1e-9\n    \n    agg_func = {\n    'category_1': ['sum', 'mean'],\n    'category_2_1.0': ['mean'],\n    'category_2_2.0': ['mean'],\n    'category_2_3.0': ['mean'],\n    'category_2_4.0': ['mean'],\n    'category_2_5.0': ['mean'],\n    'category_3_A': ['mean'],\n    'category_3_B': ['mean'],\n    'category_3_C': ['mean'],\n    'merchant_id': ['nunique'],\n    'merchant_category_id': ['nunique'],\n    'state_id': ['nunique'],\n    'city_id': ['nunique'],\n    'subsector_id': ['nunique'],\n    'purchase_amount': ['sum', 'mean', 'max', 'min', 'std'],\n    'installments': ['sum', 'mean', 'max', 'min', 'std'],\n    'purchase_month': ['mean', 'max', 'min', 'std'],\n    'purchase_date': [np.ptp, 'min', 'max'],\n    'month_lag': ['mean', 'max', 'min', 'std'],\n    'month_diff': ['mean']\n    }\n    \n    agg_history = history.groupby(['card_id']).agg(agg_func)\n    agg_history.columns = ['_'.join(col).strip() for col in agg_history.columns.values]\n    agg_history.reset_index(inplace=True)\n    \n    df = (history.groupby('card_id')\n          .size()\n          .reset_index(name='transactions_count'))\n    \n    agg_history = pd.merge(df, agg_history, on='card_id', how='left')\n    \n    return agg_history","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"44ea0524d0af0001c5ea57a6593be35e2402d0b6"},"cell_type":"code","source":"history = aggregate_transactions(historical_transactions)\nhistory.columns = ['hist_' + c if c != 'card_id' else c for c in history.columns]\nhistory[:5]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"be09e1a7d4f307beb59c18bf61261239877da4e3"},"cell_type":"code","source":"authorized = aggregate_transactions(authorized_transactions)\nauthorized.columns = ['auth_' + c if c != 'card_id' else c for c in authorized.columns]\nauthorized[:5]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"78b87a7863b62fa3758ef3167504d3ad8c279f3d","scrolled":true},"cell_type":"code","source":"new = aggregate_transactions(new_transactions)\nnew.columns = ['new_' + c if c != 'card_id' else c for c in new.columns]\nnew[:5]","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"843a929ddea5e317f882c58c3b69f5e5a4476a38"},"cell_type":"markdown","source":"The second function first aggregates on the two variables `card_id` and `month_lag`. Then a second grouping is performed to aggregate over time:"},{"metadata":{"trusted":true,"scrolled":false,"_uuid":"f210acd074326ea74c1b9316eec3136f4ab73855"},"cell_type":"code","source":"def aggregate_per_month(history):\n    grouped = history.groupby(['card_id', 'month_lag'])\n\n    agg_func = {\n            'purchase_amount': ['count', 'sum', 'mean', 'min', 'max', 'std'],\n            'installments': ['count', 'sum', 'mean', 'min', 'max', 'std'],\n            }\n\n    intermediate_group = grouped.agg(agg_func)\n    intermediate_group.columns = ['_'.join(col).strip() for col in intermediate_group.columns.values]\n    intermediate_group.reset_index(inplace=True)\n\n    final_group = intermediate_group.groupby('card_id').agg(['mean', 'std'])\n    final_group.columns = ['_'.join(col).strip() for col in final_group.columns.values]\n    final_group.reset_index(inplace=True)\n    \n    return final_group\n#___________________________________________________________\nfinal_group =  aggregate_per_month(authorized_transactions) \nfinal_group[:10]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"95bfafc86d846204d7defa8015d9d2b936a72844"},"cell_type":"code","source":"def successive_aggregates(df, field1, field2):\n    t = df.groupby(['card_id', field1])[field2].mean()\n    u = pd.DataFrame(t).reset_index().groupby('card_id')[field2].agg(['mean', 'min', 'max', 'std'])\n    u.columns = [field1 + '_' + field2 + '_' + col for col in u.columns.values]\n    u.reset_index(inplace=True)\n    return u","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"5a85689aef5f58b6af8d8617b0d8b97ee5e85a43"},"cell_type":"code","source":"additional_fields = successive_aggregates(new_transactions, 'category_1', 'purchase_amount')\nadditional_fields = additional_fields.merge(successive_aggregates(new_transactions, 'installments', 'purchase_amount'),\n                                            on = 'card_id', how='left')\nadditional_fields = additional_fields.merge(successive_aggregates(new_transactions, 'city_id', 'purchase_amount'),\n                                            on = 'card_id', how='left')\nadditional_fields = additional_fields.merge(successive_aggregates(new_transactions, 'category_1', 'installments'),\n                                            on = 'card_id', how='left')\n","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"dcf0403c10b8ee817257a51e5edf8f1f81fcd593"},"cell_type":"markdown","source":"<a id=\"3\"></a> <br>\n## 3. Training the model\nWe now train the model with the features we previously defined. A first step consists in merging all the dataframes:"},{"metadata":{"trusted":true,"_uuid":"d27264c6c7f0af6af7ba141177bfd38f7a68dec3"},"cell_type":"code","source":"train = pd.merge(train, history, on='card_id', how='left')\ntest = pd.merge(test, history, on='card_id', how='left')\n\ntrain = pd.merge(train, authorized, on='card_id', how='left')\ntest = pd.merge(test, authorized, on='card_id', how='left')\n\ntrain = pd.merge(train, new, on='card_id', how='left')\ntest = pd.merge(test, new, on='card_id', how='left')\n\ntrain = pd.merge(train, final_group, on='card_id', how='left')\ntest = pd.merge(test, final_group, on='card_id', how='left')\n\ntrain = pd.merge(train, auth_mean, on='card_id', how='left')\ntest = pd.merge(test, auth_mean, on='card_id', how='left')\n\ntrain = pd.merge(train, additional_fields, on='card_id', how='left')\ntest = pd.merge(test, additional_fields, on='card_id', how='left')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"f48c931c326bf1a7ef21614f6e10b2b538059ab5"},"cell_type":"code","source":"# cols = [c for c in train if c.startswith('hist')]\n# train.loc[train['hist_transactions_count'].isnull(), cols] = 0\n# test.loc[test['hist_transactions_count'].isnull(), cols] = 0\n\n# cols = [c for c in train if c.startswith('new')]\n# train.loc[train['new_transactions_count'].isnull(), cols] = 0\n# test.loc[test['new_transactions_count'].isnull(), cols] = 0","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"6a96a54f9f074363064723cdadc6d1f93a5a4f73"},"cell_type":"code","source":"# cols = [c for c in train if c.endswith('std')]\n# for c in cols:\n#     train.loc[train[c].isnull(), c] = 0\n#     test.loc[test[c].isnull(), c] = 0","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"bf3620bb17525f0261c9559077de54e0051900dd"},"cell_type":"code","source":"# train['transactions_ratio'] = train['new_transactions_count'] / train['hist_transactions_count']\n# test['transactions_ratio'] = test['new_transactions_count'] / test['hist_transactions_count']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"d93d5177600acd75fc4b3b2a35df6208850aeab5"},"cell_type":"code","source":"# hist_columns = [(c, c.replace('new', 'auth')) for c in train.columns if 'hist' in c]\n# for c in hist_columns:\n#     col_name = 'ratio_{}_{}'.format(c[0], c[1])\n#     train[col_name] = train[c[0]] / train[c[1]]\n#     test[col_name] = test[c[0]] / test[c[1]]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"8643ca44e3762d6dc61a7d7afdc2815e06c7ed5e","_kg_hide-input":true},"cell_type":"code","source":"test.to_csv('test.csv')\ntrain['target'] = target\ntrain.to_csv('train.csv')\ndel train['target']","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"e06ddc8d941360b1b3e32bc59221e5f6fe729763"},"cell_type":"markdown","source":"and to define the features we want to keep to train the model. For that purpose, I use the results obtained in the [Selecting features kernel](https://www.kaggle.com/fabiendaniel/selecting-features/notebook):"},{"metadata":{"trusted":true,"_uuid":"d121b2bf5c6adf83495edc63c08662899fd39e0b"},"cell_type":"code","source":"# unimportant_features = [\n#     'auth_category_2_1.0_mean',\n#     'auth_category_2_2.0_mean',\n#     'auth_category_2_3.0_mean',\n#     'auth_category_2_5.0_mean',\n#     'hist_category_2_3.0_mean',\n#     'hist_category_2_4.0_mean',\n#     'hist_category_2_5.0_mean',\n#     'hist_category_3_A_mean',\n#     'hist_installments_min',\n#     'hist_installments_std',\n#     'hist_month_lag_std',\n#     'hist_purchase_amount_max',\n#     'hist_purchase_month_max',\n#     'hist_purchase_month_min',\n#     'hist_purchase_month_std',\n#     'installments_min_mean',\n#     'new_category_2_1.0_mean',\n#     'new_category_2_2.0_mean',\n#     'new_category_2_3.0_mean',\n#     'new_category_2_5.0_mean',\n#     'new_city_id_nunique',\n#     'new_installments_std',\n#     'new_state_id_nunique',\n#     'purchase_amount_mean_mean'\n# ]\nfeatures = [c for c in train.columns if c not in ['card_id', 'first_active_month']]\n#features = [f for f in features if f not in unimportant_features]\ncategorical_feats = ['feature_2', 'feature_3']","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"e67399b25b3886e306b274069ab240af7d060397"},"cell_type":"markdown","source":"We then set the hyperparameters of the LGBM model, these parameters are obtained by an [bayesian optimization done in another kernel](https://www.kaggle.com/fabiendaniel/hyperparameter-tuning/edit):"},{"metadata":{"trusted":true,"_uuid":"c610f51450145101732f4e9ed3247f2a9fa0b091"},"cell_type":"code","source":"param = {'num_leaves': 111,\n         'min_data_in_leaf': 149, \n         'objective':'regression',\n         'max_depth': 9,\n         'learning_rate': 0.005,\n         \"boosting\": \"gbdt\",\n         \"feature_fraction\": 0.7522,\n         \"bagging_freq\": 1,\n         \"bagging_fraction\": 0.7083 ,\n         \"bagging_seed\": 11,\n         \"metric\": 'rmse',\n         \"lambda_l1\": 0.2634,\n         \"random_state\": 133,\n         \"verbosity\": -1}","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"b7a7377cd7401f2cbd13ea707fbc2a2bebe229a6"},"cell_type":"markdown","source":"We now train the model. Here, we use a standard KFold split of the dataset in order to validate the results and to stop the training. Interstingly, during the writing of this kernel, the model was enriched adding new features, which improved the CV score. **The variations observed on the CV were found to be quite similar to the variations on the LB**: it seems that the current competition won't give us headaches to define the correct validation scheme:"},{"metadata":{"trusted":true,"_uuid":"b23550968ef3fb49ae0fcc5533551d702297c990","scrolled":true},"cell_type":"code","source":"folds = KFold(n_splits=5, shuffle=True, random_state=15)\noof = np.zeros(len(train))\npredictions = np.zeros(len(test))\nstart = time.time()\nfeature_importance_df = pd.DataFrame()\n\nfor fold_, (trn_idx, val_idx) in enumerate(folds.split(train.values, target.values)):\n    print(\"fold n°{}\".format(fold_))\n    trn_data = lgb.Dataset(train.iloc[trn_idx][features],\n                           label=target.iloc[trn_idx],\n                           categorical_feature=categorical_feats\n                          )\n    val_data = lgb.Dataset(train.iloc[val_idx][features],\n                           label=target.iloc[val_idx],\n                           categorical_feature=categorical_feats\n                          )\n\n    num_round = 10000\n    clf = lgb.train(param,\n                    trn_data,\n                    num_round,\n                    valid_sets = [trn_data, val_data],\n                    verbose_eval=100,\n                    early_stopping_rounds = 200)\n    \n    oof[val_idx] = clf.predict(train.iloc[val_idx][features], num_iteration=clf.best_iteration)\n    \n    fold_importance_df = pd.DataFrame()\n    fold_importance_df[\"feature\"] = features\n    fold_importance_df[\"importance\"] = clf.feature_importance()\n    fold_importance_df[\"fold\"] = fold_ + 1\n    feature_importance_df = pd.concat([feature_importance_df, fold_importance_df], axis=0)\n    \n    predictions += clf.predict(test[features], num_iteration=clf.best_iteration) / folds.n_splits\n\nprint(\"CV score: {:<8.5f}\".format(mean_squared_error(oof, target)**0.5))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"9a1f0a866e05f8a450960e2d787a641fc35991a1"},"cell_type":"markdown","source":"<a id=\"4\"></a> <br>\n## 4. Feature importance\nFinally, we can have a look at the features that were used by the model:"},{"metadata":{"trusted":true,"_uuid":"d479e83032448481b40c216264a039cacdb2f9a1","scrolled":false},"cell_type":"code","source":"cols = (feature_importance_df[[\"feature\", \"importance\"]]\n        .groupby(\"feature\")\n        .mean()\n        .sort_values(by=\"importance\", ascending=False)[:1000].index)\n\nbest_features = feature_importance_df.loc[feature_importance_df.feature.isin(cols)]\n\nplt.figure(figsize=(14,25))\nsns.barplot(x=\"importance\",\n            y=\"feature\",\n            data=best_features.sort_values(by=\"importance\",\n                                           ascending=False))\nplt.title('LightGBM Features (avg over folds)')\nplt.tight_layout()\nplt.savefig('lgbm_importances.png')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"003ae1b1bd522b1b0d992ff220ed98d2a6d7477a"},"cell_type":"markdown","source":"<a id=\"5\"></a> <br>\n## 5. Submission\nNow, we just need to prepare the submission file:"},{"metadata":{"trusted":true,"_uuid":"82d5ac08a13603b2a66c59d98584c4b709daee2d"},"cell_type":"code","source":"sub_df = pd.DataFrame({\"card_id\":test[\"card_id\"].values})\nsub_df[\"target\"] = predictions\nsub_df.to_csv(\"submit.csv\", index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"c1c6c1d40dbb2137ddd6404670b5f3dd3d381207"},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"3900aa85af1f706015c7e8ed219395348f9852ae"},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"e155324a5fd4a7e07b06a5e00b7fb4a02afc38d9"},"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}