{"cells":[{"metadata":{"_uuid":"d9aa482b18a633db0387fd7b904737c6789b4c3e"},"cell_type":"markdown","source":"**Mercari Price Suggestion**\n\nThe objective is to come up with the right pricing algorithm that can we can use as a pricing recommendation to the users.\nSince its a regression problem, the evaluation metric that should be used is RMSE (Root Mean Squared Error). But in this case for the competition, we'll be using the RMSLE; which puts less penalty on large errors and focuses more on the smaller errors (since our main distribution in price is centered at around $10)"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"#Import Packages\nimport time\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom scipy.sparse import csr_matrix, hstack\nfrom sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer\nfrom sklearn.preprocessing import LabelBinarizer\n\nfrom sklearn.metrics import mean_squared_error\nfrom sklearn.pipeline import make_pipeline\nfrom sklearn.model_selection import KFold, cross_val_score, train_test_split\n\nfrom sklearn.linear_model import Ridge\nimport os\nprint(os.listdir(\"../input\"))\n\n","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"# Observe the training set\ntrain = pd.read_table('../input/train.tsv')\ntrain.head()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"a9b21faf8da1927cdc430e9413b308bc223c40cc"},"cell_type":"code","source":"print(\"The size of the training data is: \" + str(train.shape))\nprint(train.dtypes)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"1fa759cc5c2a4d9ee90942f26f118a7d8915fc25"},"cell_type":"markdown","source":"**Summary Statistics**"},{"metadata":{"trusted":true,"_uuid":"0d05d21ecee814b0eba987b29eb57d16b4746e93"},"cell_type":"code","source":"train.astype('object').describe().transpose()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"f912552613d2d67f881a217e8f34b8d078f95eb3"},"cell_type":"code","source":"# Observe test set\ntest = pd.read_table('../input/test.tsv')\ntest.head()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"b7fc06970708fcdcb239cab9283233c53f33a122"},"cell_type":"code","source":"test.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"f28d6780a56e5d48ab2107e9681f01aed0b30a95"},"cell_type":"code","source":"# Get 10% of the Training Data\nreduced_X_train = train.sample(frac=0.1).reset_index(drop=True)\nreduced_y_train = np.log1p(reduced_X_train['price'])","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"94a5a434704e46aa9ecf65660fe605bfae53738c"},"cell_type":"markdown","source":"**Data Cleaning**"},{"metadata":{"trusted":true,"_uuid":"eb901495a4bd89a2545cd1c5b21831f4b36a7cd6"},"cell_type":"code","source":"# Fast Cleaning of Data\nreduced_X_train['category_name'] = reduced_X_train['category_name'].fillna('Other').astype(str)\nreduced_X_train['brand_name'] = reduced_X_train['brand_name'].fillna('missing').astype(str)\nreduced_X_train['shipping'] = reduced_X_train['shipping'].astype(str)\nreduced_X_train['item_condition_id'] = reduced_X_train['item_condition_id'].astype(str)\nreduced_X_train['item_description'] = reduced_X_train['item_description'].fillna('None')\n","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"3eb4307da9619f903785b78d0f43b7bc16c261b6"},"cell_type":"markdown","source":"Topic Modeling\n\nIts foundations are Probabilistic Graphical Models\n\nHow does LDA Work?\n\nIt is an iterative algorithm\n* In the initialization stage, each word is assigned to a random topic.\n* Iteratively, the algorithm goes through each word and reassigns the word to a topic taking into consideration: What’s the probability of the word belonging to a topic and What’s the probability of the document to be generated by a topic\n    "},{"metadata":{"trusted":true,"_uuid":"a7c560f751408107dae1ee9e48b1aec0fd4624b5"},"cell_type":"code","source":"%%time\nfrom sklearn.decomposition import LatentDirichletAllocation\n\n# Initialize CountVectorizer\ncvectorizer = CountVectorizer(max_features=20000,\n                              stop_words='english', \n                              lowercase=True)\n\n# Fit it to our dataset\ncvz = cvectorizer.fit_transform(reduced_X_train['item_description'])\n\n# Initialize LDA Model with 10 Topics\nlda_model = LatentDirichletAllocation(n_topics=10,\n                                      random_state=42)\n\n# Fit it to our CountVectorizer Transformation\nX_topics = lda_model.fit_transform(cvz)\n\n# Define variables\nn_top_words = 10\ntopic_summaries = []\n\n# Get the topic words\ntopic_word = lda_model.components_\n# Get the vocabulary from the text features\nvocab = cvectorizer.get_feature_names()\n\n# Display the Topic Models\nfor i, topic_dist in enumerate(topic_word):\n    topic_words = np.array(vocab)[np.argsort(topic_dist)][:-(n_top_words+1):-1]\n    topic_summaries.append(' '.join(topic_words))\n    print('Topic {}: {}'.format(i, ' | '.join(topic_words)))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"c73d9cfdbb1012a442154875ffca322b0b771402"},"cell_type":"markdown","source":"**Eli5**\n\nLooking at features helps to understand how classifier works. Maybe even more importantly, it helps to notice preprocessing bugs, data leaks, issues with task specification - all these nasty problems you get in a real world.\n\n**How does Eli5 Work?**\nIt shows you the correlation of each feature/text with the target variable. We can inspect features and weights because we’re using a bag-of-words vectorizer and a linear classifier (so there is a direct mapping between individual words and classifier coefficients)."},{"metadata":{"_uuid":"d6759ebcf3d13cd3eb612f08ca6edad171823975"},"cell_type":"markdown","source":"**Analyzing Item Description with Eli5**"},{"metadata":{"trusted":true,"_uuid":"470d54d0e48e51331d94aac607025119e494a378"},"cell_type":"code","source":"# Definte RMSLE Cross Validation Function\ndef rmsle_cv(model):\n    kf = KFold(shuffle=True, random_state=42).get_n_splits(reduced_X_train['item_description'])\n    rmse= np.sqrt(-cross_val_score(model, reduced_X_train['item_description'], reduced_y_train, scoring=\"neg_mean_squared_error\", cv = kf))\n    return(rmse.mean())","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"58ec49bfd84f72e8f082126a2ea1edfc7fe5827a"},"cell_type":"markdown","source":"**Baseline Model with CountVectorizer**"},{"metadata":{"trusted":true,"_uuid":"3603f2b73d7236b246d6d6c638979a74870c414b"},"cell_type":"code","source":"from sklearn.linear_model import Ridge\n\nvec = CountVectorizer()\nclf = Ridge(random_state=42)\npipe = make_pipeline(vec, clf)\npipe.fit(reduced_X_train['item_description'], reduced_y_train)\n\ncv_rmsle = rmsle_cv(pipe)\n\nprint(\"The Validation Score is: \" + str(cv_rmsle))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"3a513ffbc5a5a8491902b8b8137533793a088dde"},"cell_type":"code","source":"import eli5\neli5.show_weights(pipe, vec=vec, top=100, feature_filter=lambda x: x != '<BIAS>')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"811330cebcbda6da170dd0dcd9da0bcfaed9a6f9"},"cell_type":"code","source":"eli5.show_prediction(clf, doc=reduced_X_train['item_description'][1297], vec=vec)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"e900e8933b01a97fdcc186d65b6867b3d7e8a585"},"cell_type":"markdown","source":"**Baseline Model with CountVectorizer and Stop Words**"},{"metadata":{"trusted":true,"_uuid":"de1334b215765f5ed9b49bf3beab3ef271a12a20"},"cell_type":"code","source":"vec = CountVectorizer(stop_words='english')\nclf = Ridge(random_state=42)\npipe = make_pipeline(vec, clf)\npipe.fit(reduced_X_train['item_description'], reduced_y_train)\n\ncv_sw_rmsle = rmsle_cv(pipe)\n\nprint(\"The Validation Score is: \" + str(cv_sw_rmsle))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"75562674a20ab9066f6469c8d0998addff03ccfd"},"cell_type":"code","source":"eli5.show_prediction(clf, doc=reduced_X_train['item_description'][1297], vec=vec)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"a9db16a7617c2e97f7ee5da978a7adb7af39a340"},"cell_type":"markdown","source":"**Baseline Model with TF-IDF**"},{"metadata":{"trusted":true,"_uuid":"4546a10040ee8ca1c26cfbb0f5ae088a8caf2e67"},"cell_type":"code","source":"vec = TfidfVectorizer()\nclf = Ridge(random_state=42)\npipe = make_pipeline(vec, clf)\npipe.fit(reduced_X_train['item_description'], reduced_y_train)\n\ntfidf_rmsle = rmsle_cv(pipe)\n\nprint(\"The Validation Score is: \" + str(tfidf_rmsle))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"e9185b5f2f3b96cc58901d70acc22f2f4827c9a5"},"cell_type":"code","source":"eli5.show_prediction(clf, doc=reduced_X_train['item_description'][1297], vec=vec)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"4171eda5f569feecb65a77ce8eb38690add06738"},"cell_type":"markdown","source":"**Baseline Model with TF-IDF and Stop Words**"},{"metadata":{"trusted":true,"_uuid":"03fc41e4f53805d474c0f1987ab8af590416167d"},"cell_type":"code","source":"vec = TfidfVectorizer(stop_words='english')\nclf = Ridge(random_state=42)\npipe = make_pipeline(vec, clf)\npipe.fit(reduced_X_train['item_description'], reduced_y_train)\n\ntfidf_sw_rmsle = rmsle_cv(pipe)\n\nprint(\"The Validation Score is: \" + str(tfidf_sw_rmsle))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"64e7110ca860c0e27d06da6d0061141b3cc7f80c"},"cell_type":"code","source":"eli5.show_prediction(clf, doc=reduced_X_train['item_description'][1297], vec=vec)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"9feb847e83efab82254761cc4ada202ac01637e8"},"cell_type":"markdown","source":"**Baseline Model with TF-IDF, Stop Words, and N-Grams**"},{"metadata":{"trusted":true,"_uuid":"6277bfc9228800e1190f07aec9cf0d1d9ef5be91"},"cell_type":"code","source":"vec = TfidfVectorizer(stop_words='english', ngram_range=(1,2))\nclf = Ridge(random_state=42)\npipe = make_pipeline(vec, clf)\npipe.fit(reduced_X_train['item_description'], reduced_y_train)\n\ntfidf_sw_ng_rmsle = rmsle_cv(pipe)\n\nprint(\"The Validation Score is: \" + str(tfidf_sw_ng_rmsle))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"b0180711d2228116fe4761399ab82317fdc6bca9"},"cell_type":"code","source":"eli5.show_prediction(clf, doc=reduced_X_train['item_description'][1297], vec=vec)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"c2da46d3228852b6d20f70b7398c9971eb6d1532"},"cell_type":"markdown","source":"**RMSLE Summary**"},{"metadata":{"trusted":true,"_uuid":"cbc3d2c10181e43fcd5fb6899250f72c44e39bff"},"cell_type":"code","source":"print (\"RMSLE Score: \" + str(cv_rmsle) + \" | CountVectorizer\")\nprint (\"RMSLE Score: \" + str(cv_sw_rmsle) + \" | CountVectorizer | Stop Words\")\nprint (\"RMSLE Score: \" + str(tfidf_rmsle) + \" | TF-IDF\")\nprint (\"RMSLE Score: \" + str(tfidf_sw_rmsle) + \" | TF-IDF | Stop Words\")\nprint (\"RMSLE Score: \" + str(tfidf_sw_ng_rmsle) + \" | TF-IDF | Stop Words | N-Grams\")","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"0fe6f016d98afeeeca162baec6085da6b042656a"},"cell_type":"markdown","source":"**Feature Pre-Processing / Transformation**"},{"metadata":{"trusted":true,"_uuid":"3e1bd6c2cebc392fb36547e6c67b71c926da521b"},"cell_type":"code","source":"from sklearn.pipeline import FeatureUnion\n\ndefault_preprocessor = CountVectorizer().build_preprocessor()\n\ndef build_preprocessor(field):\n    field_idx = list(reduced_X_train.columns).index(field)\n    return lambda x: default_preprocessor(x[field_idx])\n\nvectorizer = FeatureUnion([\n    ('name', CountVectorizer(\n        ngram_range=(1, 2),\n        max_features=50000,\n        preprocessor=build_preprocessor('name'))),\n    ('category_name', CountVectorizer(\n        token_pattern='.+',\n        preprocessor=build_preprocessor('category_name'))),\n    ('brand_name', CountVectorizer(\n        token_pattern='.+',\n        preprocessor=build_preprocessor('brand_name'))),\n    ('shipping', CountVectorizer(\n        token_pattern='\\d+',\n        preprocessor=build_preprocessor('shipping'))),\n    ('item_condition_id', CountVectorizer(\n        token_pattern='\\d+',\n        preprocessor=build_preprocessor('item_condition_id'))),\n    ('item_description', TfidfVectorizer(\n        ngram_range=(1, 2),\n        max_features=55000,\n        stop_words='english',\n        preprocessor=build_preprocessor('item_description'))),\n])","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"1b1cc6e68a731d121e23a1373528ff50ef4ffb65"},"cell_type":"markdown","source":"**Modeling**\n\n* Ridge Regression\n* LASSO Regression\n* Light GBM"},{"metadata":{"_uuid":"ba8c086748bb68329f95bb6d5e720c5989e2ca86"},"cell_type":"markdown","source":"**Create Transformed Training Set**"},{"metadata":{"trusted":true,"scrolled":false,"_uuid":"b1a502a142f4acfcb1eeceb5a5040d172c5fdef4"},"cell_type":"code","source":"# Create Transformed Train Set\nreduced_Xt_train = vectorizer.fit_transform(reduced_X_train.values)\n","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"68c607af152bbbf78596898c2cfa7a8370718faf"},"cell_type":"markdown","source":"**Define RMSLE Function**"},{"metadata":{"trusted":true,"_uuid":"0d29a0f0f7be9e042141f03c44ea0a2d5d9b15fa"},"cell_type":"code","source":"def get_rmsle(y, pred): return np.sqrt(mean_squared_error(y, pred))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"0cdd1e61d15a3f745c6e005b4958c447b5d215bd"},"cell_type":"markdown","source":"**Ridge Cross Validation**"},{"metadata":{"trusted":true,"_uuid":"1459bd3a666926d33eb39a17b36f4e0de79d7ac0"},"cell_type":"code","source":"%%time\n\n# Create 3-Fold CV\ncv = KFold(n_splits=3, shuffle=True, random_state=42)\nfor train_ids, valid_ids in cv.split(reduced_Xt_train):\n    # Define LGBM Model\n    model_ridge = Ridge(solver = \"lsqr\", fit_intercept=True, random_state=42)\n    \n    # Fit LGBM Model\n    model_ridge.fit(reduced_Xt_train[train_ids], reduced_y_train[train_ids])\n    \n    # Predict & Evaluate Training Score\n    y_pred_train = model_ridge.predict(reduced_Xt_train[train_ids])\n    rmsle_train = get_rmsle(y_pred_train, reduced_y_train[train_ids])\n    \n    # Predict & Evaluate Validation Score\n    y_pred_valid = model_ridge.predict(reduced_Xt_train[valid_ids])\n    rmsle_valid = get_rmsle(y_pred_valid, reduced_y_train[valid_ids])\n    \n    print(f'LGBM Training RMSLE: {rmsle_train:.5f}')\n    print(f'LGBM Validation RMSLE: {rmsle_valid:.5f}')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"237568247bd9ff6ad2f1892b0a2a15bcd64e492b"},"cell_type":"markdown","source":"LASSO Cross Validation\n\n\nOne reason why LASSO Perform way worse than Ridge could be because since LASSO performs automatic feature selection. So keep in mind majority of our features are just words. It'll remove some of our text features. And this may not generalize well with new data. Because our dataset is suppose to capture and use all our words as features."},{"metadata":{"trusted":true,"_uuid":"7ad9dd5359666fc46f28a2d4379e7b462c6911c8"},"cell_type":"code","source":"%%time\nfrom sklearn.linear_model import Lasso\n\n# Create 3-Fold CV\ncv = KFold(n_splits=3, shuffle=True, random_state=42)\nfor train_ids, valid_ids in cv.split(reduced_Xt_train):\n    # Define LGBM Model\n    model_LASSO = Lasso(fit_intercept=True, random_state=42)\n    \n    # Fit LGBM Model\n    model_LASSO.fit(reduced_Xt_train[train_ids], reduced_y_train[train_ids])\n    \n    # Predict & Evaluate Training Score\n    y_pred_train = model_LASSO.predict(reduced_Xt_train[train_ids])\n    rmsle_train = get_rmsle(y_pred_train, reduced_y_train[train_ids])\n    \n    # Predict & Evaluate Validation Score\n    y_pred_valid = model_LASSO.predict(reduced_Xt_train[valid_ids])\n    rmsle_valid = get_rmsle(y_pred_valid, reduced_y_train[valid_ids])\n    \n    print(f'LASSO Training RMSLE: {rmsle_train:.5f}')\n    print(f'LASSO Validation RMSLE: {rmsle_valid:.5f}')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"94689054ef65014d1f04e58ca036827fa3083c6c"},"cell_type":"markdown","source":"**LGBM Cross Validation**"},{"metadata":{"trusted":true,"_uuid":"b39566427bd481b689e8877dfc8d3cddfcebd40f"},"cell_type":"code","source":"%%time\nimport lightgbm as lgb\n\n# Create 3-Fold CV\ncv = KFold(n_splits=3, shuffle=True, random_state=42)\nfor train_ids, valid_ids in cv.split(reduced_Xt_train):\n    # Define LGBM Model\n    model_lgb = lgb.LGBMRegressor(num_leaves=31, n_jobs=-1, learning_rate=0.1, n_estimators=500, random_state=42)\n    \n    # Fit LGBM Model\n    model_lgb.fit(reduced_Xt_train[train_ids], reduced_y_train[train_ids])\n    \n    # Predict & Evaluate Training Score\n    y_pred_train = model_lgb.predict(reduced_Xt_train[train_ids])\n    rmsle_train = get_rmsle(y_pred_train, reduced_y_train[train_ids])\n    \n    # Predict & Evaluate Validation Score\n    y_pred_valid = model_lgb.predict(reduced_Xt_train[valid_ids])\n    rmsle_valid = get_rmsle(y_pred_valid, reduced_y_train[valid_ids])\n    \n    print(f'LGBM Training RMSLE: {rmsle_train:.5f}')\n    print(f'LGBM Validation RMSLE: {rmsle_valid:.5f}')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"c49669b30bc7a36d5152023c5aaff4f73d68c10f"},"cell_type":"markdown","source":"**Ensemble (Ridge + LGBM)**"},{"metadata":{"trusted":true,"_uuid":"965eef8ae99a26fd11b4c13e5ce80570f1c74011"},"cell_type":"code","source":"#Create Train/Test Split\ntrain_X, test_X, train_y, test_y = train_test_split(reduced_Xt_train, reduced_y_train, test_size=0.2, random_state=211)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"83e3555a6390cf8188f3201bf3c996e3e58a32c7"},"cell_type":"markdown","source":"**LGBM Model**"},{"metadata":{"trusted":true,"_uuid":"149a7f24ae1bf97628e45b1e46aa7620ec46ff09"},"cell_type":"code","source":"# Define LGBM Model\nmodel_lgb = lgb.LGBMRegressor(num_leaves=31, n_jobs=-1, learning_rate=0.1, n_estimators=500, random_state=40)\n\n# Fit LGBM Model\nmodel_lgb.fit(train_X, train_y)\n\n# Predict with LGBM Model\nlgbm_y_pred = model_lgb.predict(test_X)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"f938cee246cb3ae4da685fe76c96620703654965"},"cell_type":"markdown","source":"**Ridge Model**"},{"metadata":{"trusted":true,"_uuid":"027938715d54d63a5cb3301312c01b360a9f296d"},"cell_type":"code","source":"# Define Ridge Model\nmodel_ridge = Ridge(solver = \"lsqr\", fit_intercept=True, random_state=42)\n    \n# Fit Ridge Model\nmodel_ridge.fit(train_X, train_y)\n    \n# Evaluate Training Score\nridge_y_pred = model_ridge.predict(test_X)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"e52246cfdac6b39c477912838b8538d09972079d"},"cell_type":"markdown","source":"**Ensemble Model**"},{"metadata":{"trusted":true,"_uuid":"af610108e9b80ff9e48bbaa69c3b7dc27533fce7"},"cell_type":"code","source":"ensemble_y_pred = (lgbm_y_pred+ridge_y_pred)/2\n\nensemble_rmsle = get_rmsle(ensemble_y_pred, test_y)\n\nprint(f'Ensemble RMSLE: {ensemble_rmsle:.5f}')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"ba0ba6766a5be918369cbb7025c8f88213abf31e"},"cell_type":"markdown","source":"**Predictions**"},{"metadata":{"trusted":true,"_uuid":"034baf0710f4db870411c0a57988f08d04fdffac"},"cell_type":"code","source":"#Ensemble Predictions without Inverse Log Transformation\nensemble_y_pred[0:20]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"f010cae025dbd2b634a312a9af78a06dfc332995"},"cell_type":"code","source":"# Ensemble Predictions (Inverse Log - Exponential)\nensemble_y = (np.expm1(lgbm_y_pred)+np.expm1(ridge_y_pred))/2\nensemble_y[200:220]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"79cd0b5cf154810ac33a04d7f17a725fdefc7b0f"},"cell_type":"code","source":"# Test Predictions \nnp.expm1(test_y[200:220])","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"cb19e0c9ed4ae8a726de69d1bceaef7119f95db5"},"cell_type":"markdown","source":""}],"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}