{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"from __future__ import unicode_literals\nfrom nltk.classify import NaiveBayesClassifier\nfrom nltk.corpus import stopwords\nfrom nltk.stem import PorterStemmer\nfrom nltk import ngrams\n\nimport re\n\nimport matplotlib.pyplot as plt\nimport itertools\nimport pandas as pd\nimport numpy as np\nimport json\n\nfrom sklearn.feature_extraction.text import TfidfVectorizer\nfrom sklearn.metrics import classification_report, confusion_matrix, accuracy_score, f1_score\nfrom sklearn.naive_bayes import MultinomialNB\n\nfrom scipy import sparse","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"dfe8b13afead591fbddba17257c0fce45e479bb3"},"cell_type":"code","source":"def neg_tag(text):\n    \n    \"\"\"\n    Input is string (e.g. I am not happy.)\n    Output is string with neg tags (e.g. I am not NEG_happy.)\n    \"\"\"\n    \n    transformed = re.sub(r\"\\b(?:never|nothing|nowhere|noone|none|not|haven't|hasn't|hasnt|hadn't|hadnt|can't|cant|couldn't|couldnt|shouldn't|shouldnt|won't|wont|wouldn't|wouldnt|don't|dont|doesn't|doesnt|didn't|didnt|isnt|isn't|aren't|arent|aint|ain't|hardly|seldom)\\b[\\w\\s]+[^\\w\\s]\", lambda match: re.sub(r'(\\s+)(\\w+)', r'\\1NEG_\\2', match.group(0)), text, flags=re.IGNORECASE)\n    return(transformed)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"eb4d0a761af499279dee4aa5c19b4f55de5ea21f"},"cell_type":"code","source":"def preprocessing_baseline(list_of_sentences):\n    \n    \"\"\"\n    Input is list of raw sentences.\n    Output is sentences without punctuations.\n    Used for preprocessing method B\n    \"\"\"\n    processed_sentences = []\n    \n    for sent in list_of_sentences:\n        \n        processed = [word.lower() for word in sent.split()]\n        remove_punc = [re.sub('[^a-zA-Z_]+','',t) for t in processed]\n        processed_sentences.append(remove_punc)\n    \n    if len(processed_sentences) == len(list_of_sentences):\n        return processed_sentences\n    else:\n        print('Length of processed is different from input')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"a38ed98c22311ca22f463ae746dd9247318e50c6"},"cell_type":"code","source":"def preprocessing_stpwrd_remvl(list_of_sentences):\n    \n    \"\"\"\n    Input is list of raw sentences.\n    Output is list of processed sentences.\n    Used for preprocessing methods S1, S2, S3\n    \"\"\"\n    \n    processed_sentences = []\n    \n    for sent in list_of_sentences:\n        \n        stemmed = [stemmer.stem(word).lower() for word in sent.split() if word.lower() not in stpwrds]\n        processed = [re.sub('[^a-zA-Z]+','',t) for t in stemmed]\n        morethan3 = [t for t in processed if len(t) > 3]\n        processed_sentences.append(' '.join(morethan3))\n        \n\n    if len(processed_sentences) == len(list_of_sentences):\n        return processed_sentences\n    else:\n        print('Length of processed is different from input')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"3f0dce6f774d5a2111ef8061346a50eb96fcad99"},"cell_type":"code","source":"def preprocessing_neg_tag(list_of_sentences):\n    \n    \"\"\"\n    Input is list of raw sentences.\n    Output is list of neg-tagged and processed sentences.\n    Used for preprocessing method N1, N2, N3\n    \"\"\"\n    \n    processed_sentences = []\n    \n    for sent in list_of_sentences:\n        \n        stemmed = [stemmer.stem(word).lower() \n                   if word.lower() not in neg_stw and word.lower() not in stpwrds_wo_neg \n                   else word.lower() \n                   for word in sent.split()]\n        \n        processed = [t for t in stemmed if t not in stpwrds_wo_neg]\n        \n        neg_tagged = neg_tag(' '.join(processed))\n        \n        remove_punc = [re.sub('[^a-zA-Z_]+','',t) for t in neg_tagged.split() if t not in neg_stw]\n        \n        morethan3 = [t for t in remove_punc if len(t) > 3]\n        \n        processed_sentences.append(' '.join(morethan3))\n\n    if len(processed_sentences) == len(list_of_sentences):\n        return processed_sentences\n    else:\n        print('Length of processed is different from input')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"aceb54c06a30e9d07d95bbc34a2c16242cb1cf2c"},"cell_type":"code","source":"def get_uni_bi_tri_grams(list_of_reviews):\n    \n    \"\"\"\n    Input is list of reviews.\n    Outputs are:\n    1. Unigrams tokens\n    2. Unigrams + Bigrams tokens\n    3. Unigrams + Bigrams + Trigram tokens\n    \"\"\"\n    \n    uni_tokens = [s.split() for s in list_of_reviews]\n    bi_tokens = [[' '.join([x,y]) for x,y in ngrams(s.split(),2)] for s in list_of_reviews]\n    tri_tokens = [[' '.join([x,y,z]) for x,y,z in ngrams(s.split(),3)] for s in list_of_reviews]\n\n    uni_bi_tokens = [x + uni_tokens[i] for i, x in enumerate(bi_tokens)]\n    uni_bi_tri_tokens = [x + uni_tokens[i] + bi_tokens[i] for i, x in enumerate(tri_tokens)]\n\n    return uni_tokens, uni_bi_tokens, uni_bi_tri_tokens","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"d409f9033152d91994123b0d178baf10f33f87c2"},"cell_type":"code","source":"# To allow TFIDF Vectorizer to take in tokenized texts directly\n\ndef dummy(doc):\n    \n    \"\"\"\n    Dummy function to allow TfidVectorizer to take in tokenized texts directly\n    \"\"\"\n    \n    return doc","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"9805e0e06f06f9eb493e0eeaff445289db360199"},"cell_type":"code","source":"def build_models(x_train, y_train, x_test, y_test, feature_range):\n    \n    \"\"\"\n    Standard model building function for B, S, N and NC methods.    \n    \"\"\"\n    \n    \n    model_list = []\n    \n    for features in range(feature_range[0],feature_range[1],500):\n        \n        tfid = TfidfVectorizer(max_features=features,analyzer=dummy,preprocessor=dummy)\n        \n        train_set = tfid.fit_transform(x_train)\n        test_set = tfid.transform(x_test)\n        \n        mnb = MultinomialNB()\n        mnb.fit(train_set,y_train)\n        \n        r = {}\n        r['features'] = features\n        r['train_acc'], r['test_acc'], r['train_f1'], r['test_f1'], r['tr_cf'] , r['te_cf'], _, _ = get_train_test_score(mnb,\n                                                                                                                         train_set, \n                                                                                                                         test_set, \n                                                                                                                         y_train, \n                                                                                                                         y_test)\n        model_list.append(r)\n    \n    return model_list","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"936aece4977d6c680f55b6bec167fc235a6985f2"},"cell_type":"code","source":"def get_train_test_score(model, x_train, x_test, y_train, y_test):\n    \n    \"\"\"\n    Function to get train and test score\n    \"\"\"\n    \n    y_train_pred = model.predict(x_train)\n    y_test_pred = model.predict(x_test)\n    \n    train_acc = accuracy_score(y_train,y_train_pred)\n    test_acc = accuracy_score(y_test,y_test_pred)\n    \n    train_f1 = f1_score(y_train,y_train_pred,average='weighted')\n    test_f1 = f1_score(y_test,y_test_pred,average='weighted')\n    \n    train_cf = confusion_matrix(y_train,y_train_pred)\n    test_cf = confusion_matrix(y_test,y_test_pred)\n    \n    train_acc, test_acc, train_f1, test_f1 = [round(x*100,1) for x in [train_acc, test_acc, train_f1, test_f1]]\n    \n    return train_acc, test_acc, train_f1, test_f1, train_cf, test_cf, y_train_pred, y_test_pred","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"d913c3315eb9ef84e9d5fb14fabd71723a9651fb"},"cell_type":"code","source":"def print_classification_results(description, train_acc, test_acc, train_f1, test_f1, train_cf, test_cf):\n    \n    \"\"\"\n    Plot the classification results\n    \"\"\"\n    \n    print(description)\n\n    \n    plt.subplot(1,4,1)\n    plt.bar(['Accuracy','F1-Score'], [train_acc,train_f1] ,color=['blue','orange'])\n    plt.ylim(55,100)\n    if train_acc <= 90:\n        plt.annotate(str(train_acc) + '%', (-0.07, train_acc + 1))\n        plt.annotate(str(train_f1) + '%', (0.93, train_f1 + 1))\n\n    else:\n        plt.annotate(str(train_acc) + '%', (-0.07, train_acc - 3))\n        plt.annotate(str(train_f1) + '%', (0.93, train_f1 - 3))\n        \n    plt.title('Training Score')\n\n    plt.subplot(1,4,2)\n    plt.bar(['Accuracy','F1-Score'], [test_acc,test_f1] ,color=['blue','orange'])\n    plt.ylim(55,100)\n    \n    if test_acc <= 90:\n        plt.annotate(str(test_acc) + '%', (-0.07, test_acc + 1))\n        plt.annotate(str(test_f1) + '%', (0.93, test_f1 + 1))\n\n    else:\n        plt.annotate(str(test_acc) + '%', (-0.07, test_acc - 3))\n        plt.annotate(str(test_f1) + '%', (0.93, test_f1 - 3))\n\n        \n        \n    plt.title('Testing Score')\n\n    plt.subplot(1,4,3)\n    plot_confusion_matrix(train_cf,classes=['negative','positive'],title='Confusion Matrix (Training)')\n\n    plt.subplot(1,4,4)\n    plot_confusion_matrix(test_cf,classes=['negative','positive'],title='Confusion Matrix (Testing)')\n\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"b84e26a51c51c4b72d102a2e2128ea276932e2e2"},"cell_type":"code","source":"def plot_confusion_matrix(cm, classes,\n                          normalize=False,\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    Obtained from sklearn documentations.\n    \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)\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()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"5c5272e7a876a162ba566b22435cec63b5516901"},"cell_type":"code","source":"def plot_pp_best_models(pp_types, pp_desc):\n    \n    pp_acc, pp_f1 = [], []\n    \n    pp_labels = list(pp_desc.values())\n    \n    for pp in pp_types:\n        \n        best_acc = max(x['test_acc'] for x in models[pp])\n        best_f1 = max(x['test_f1'] for x in models[pp])\n        \n        pp_acc.append(best_acc)\n        pp_f1.append(best_f1)\n        \n    \n    \n    plt.plot(pp_labels,pp_acc,label='test_acc',marker='o',markersize =10)\n    plt.plot(pp_labels,pp_f1,label='test_f1',marker='o',markersize=10, alpha=0.8)\n    plt.ylabel('Percentage (%)',fontsize=15)\n    plt.xlabel('Preprocessing Method',fontsize=15)\n\n    plt.ylim(min(pp_f1) - 1, max(pp_f1) + 1)\n    plt.xticks(fontsize=8)\n    \n    for index, x in enumerate(pp_types):\n        plt.annotate(x,(index - 0.3,min(pp_f1)-0.88),fontsize=20,bbox=dict(boxstyle=\"round\", fc=\"w\"))\n    \n    plt.grid(True,which='both')\n    plt.legend()\n    plt.title('Pre-processing Benchmark Results (Multinomial Naive Bayes)\\n',fontsize='15')\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"d61df730a0665bc601931564a88bfa02e9722f3a"},"cell_type":"code","source":"# Plot acc and f1 on y, features on x\ndef plot_acc_f1(pp,feature_range):\n        \n    tr_acc =[x['train_acc'] for x in models[pp]]\n    tr_f1 =[x['train_f1'] for x in models[pp]]\n    tr_fea = [x['features'] for x in models[pp]]\n    \n    te_acc =[x['test_acc'] for x in models[pp]]\n    te_f1 =[x['test_f1'] for x in models[pp]]\n    te_fea = [x['features'] for x in models[pp]]\n\n    for x in models[pp]:\n        if x['test_acc'] == max(te_acc):\n            best_model = x\n            break\n        \n    print('===============================================================================================================================\\n')\n    print('Pre-processing Method\\t\\t: {}'.format(pp))\n    print('Pre-processing Details\\t\\t: {}'.format(str(pp_desc[pp].replace('\\n',' | '))))\n    print('Best Model Training Accuracy\\t: {} %'.format(best_model['train_acc']))\n    print('Best Model Training F1-Score\\t: {} %'.format(best_model['train_f1']))\n    print('Best Model Testing Accuracy\\t: {} %'.format(best_model['test_acc']))\n    print('Best Model Testing F1-Score\\t: {} %'.format(best_model['test_f1']))\n    print('Best Model No. of Features\\t: {}'.format(best_model['features']))\n\n    plt.subplot(1,4,1)\n    plot_confusion_matrix(best_model['tr_cf'],\n                          classes=['negative','positive'],\n                          title='Confusion Matrix \\n(Best Model, Train Set)\\n')\n\n    plt.subplot(1,4,2)\n    plot_confusion_matrix(best_model['te_cf'],\n                          classes=['negative','positive'],\n                          title='Confusion Matrix \\n(Best Model, Test Set)\\n')\n    \n    plt.subplot(1,4,3)\n    plt.plot(tr_fea,tr_acc,label='train_acc')\n    plt.plot(tr_fea,tr_f1,label='train_f1')\n    plt.ylabel('Percentage (%)')\n    plt.xlim(feature_range[0],feature_range[1])\n    plt.xlabel('Number of Features')\n    plt.grid(True,which='both')\n    plt.legend()\n    plt.title('Train Set Models Results\\n')\n    \n    plt.subplot(1,4,4)\n    plt.plot(te_fea,te_acc,label='test_acc')\n    plt.plot(te_fea,te_f1,label='test_f1')\n    plt.ylabel('Percentage (%)')\n    plt.xlim(feature_range[0],feature_range[1])\n    plt.xlabel('Number of Features')\n    plt.grid(True,which='both')\n    plt.legend()\n    plt.title('Test Set Models Results\\n')\n    \n\n    \n\n    plt.tight_layout()\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"12ed0e521a2965fee556e6035699b9379c899bc8"},"cell_type":"code","source":"neg_stw = [\"never\",\"nothing\",\"nowhere\",\"noone\",\n           \"none\",\"not\",\"haven't\",\"hasn't\",\"hasnt\",\n           \"hadn't\",\"hadnt\",\"can't\",\"cant\",\"couldn't\",\n           \"couldnt\",\"shouldn't\",\"shouldnt\",\"won't\",\n           \"wont\",\"wouldn't\",\"wouldnt\",\"don't\",\"dont\",\n           \"doesn't\",\"doesnt\",\"didn't\",\"didnt\",\"isnt\",\n           \"isn't\",\"aren't\",\"arent\",\"aint\",\n           \"ain't\",\"hardly\",\"seldom\"]\nstpwrds_wo_neg = [x for x in stopwords.words('english') if x not in neg_stw]\nstpwrds = [x for x in stopwords.words('english')]\n\nstemmer = PorterStemmer()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"efad95670ebe4188d2a63be4acbf0b1ae3bbfeb1"},"cell_type":"code","source":"encoding = 'utf-8'\n\ndf_train = pd.read_csv('../input/train.csv', encoding=encoding)\ndf_test = pd.read_csv('../input/test.csv', encoding=encoding)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"d110ea72ba3327e3f6b39de56b2d544c8afd4c40"},"cell_type":"code","source":"df_train.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"6135cdcca4fe1e47d679d00718ab781cc1645580"},"cell_type":"code","source":"df_test.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"ae78d6cf43324e6cda9c29b56bb8c6d2bd03d624"},"cell_type":"code","source":"from sklearn.model_selection import train_test_split","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"625923841530f8221195b73c209323c6ca7946de"},"cell_type":"code","source":"# Split the training set into train set and test set\n\nX = df_train.loc[:,'question_text'].values\nY = df_train.loc[:,'target'].values\n\nx_train, x_test, y_train, y_test = train_test_split(X,\n                                                    Y,\n                                                    stratify=Y,\n                                                    test_size=0.3,\n                                                    random_state=20)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"0e1a94ff66e1cd31249e91b197ea17e8e6240da9"},"cell_type":"code","source":"print(len(x_train),len(x_train),len(x_test),len(y_test))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"16bc008bf955c2afd4c381aa4933f2371600cf6a"},"cell_type":"code","source":"print('Percentage of insincere questions in train set : {0:.2f} %'.format(float(y_train.sum())/len(y_train)*100))\nprint('Percentage of insincere questions in test set  : {0:.2f} %'.format(float(y_test.sum())/len(y_test)*100))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"ac0a404edd1b4deda13e5b1abe459901da652e9e"},"cell_type":"code","source":"pp_types = ['BASE','SIM1','SIM2','SIM3','NEG1','NEG2','NEG3']\n\npp_desc = {  'BASE': 'Baseline',\n             'SIM1': '1-gram\\nsw, punc removal\\nstemming',\n             'SIM2': '1,2-grams\\nsw, punc removal\\nstemming',\n             'SIM3': '1,2,3-grams\\nsw, punc removal\\nstemming',\n             'NEG1': '1-gram\\nneg tag\\nsw, punc removal\\nstemming',\n             'NEG2': '1,2-grams\\nneg tag\\nsw, punc removal\\nstemming',\n             'NEG3': '1,2,3-grams\\nneg tag\\nsw, punc removal\\nstemming',}\n\nmodels = dict((pp,[]) for pp in pp_types)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"0307d7e87f2c9b06377e6438e6116d474996dd9f"},"cell_type":"code","source":"x_train_b = preprocessing_baseline(x_train)\nx_train_s1, x_train_s2, x_train_s3 = get_uni_bi_tri_grams(preprocessing_stpwrd_remvl(x_train))\nx_train_n1, x_train_n2, x_train_n3 = get_uni_bi_tri_grams(preprocessing_neg_tag(x_train))\n\nx_test_b = preprocessing_baseline(x_test)\nx_test_s1, x_test_s2, x_test_s3 = get_uni_bi_tri_grams(preprocessing_stpwrd_remvl(x_test))\nx_test_n1, x_test_n2, x_test_n3 = get_uni_bi_tri_grams(preprocessing_neg_tag(x_test))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"def640ae16eee78e3832fd2fc81d57b81df4a94b"},"cell_type":"code","source":"feature_range = (15000,45000)\n\nmodels['BASE'] = build_models(x_train_b,y_train,x_test_b,y_test,feature_range)\nmodels['SIM1'] = build_models(x_train_s1,y_train,x_test_s1,y_test,feature_range)\nmodels['SIM2'] = build_models(x_train_s2,y_train,x_test_s2,y_test,feature_range)\nmodels['SIM3'] = build_models(x_train_s3,y_train,x_test_s3,y_test,feature_range)\nmodels['NEG1'] = build_models(x_train_n1,y_train,x_test_n1,y_test,feature_range)\nmodels['NEG2'] = build_models(x_train_n2,y_train,x_test_n2,y_test,feature_range)\nmodels['NEG3'] = build_models(x_train_n3,y_train,x_test_n3,y_test,feature_range)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false,"_uuid":"c06646dcf6e0152074815fab3383631a90323d7a"},"cell_type":"code","source":"plt.rcParams['figure.figsize'] = [18,4]\n\nfor pp in pp_types:\n    plot_acc_f1(pp,feature_range)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"4132358a73721169cede304a380a796780f834c0"},"cell_type":"code","source":"plt.rcParams['figure.figsize'] = [18,8]\n\nplot_pp_best_models(pp_types,pp_desc)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"3c6b00d2b960907485650860a384fae7b4edf1fb"},"cell_type":"code","source":"x_test_qn_text = df_test.loc[:,'question_text'].values\nx_test_qid = df_test.loc[:,'qid'].values\n\n\nx_test_b = preprocessing_baseline(x_test_qn_text)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"b40fd96bf3e3241eb267e4541c7c6f73d34ae87a"},"cell_type":"code","source":"tfid = TfidfVectorizer(max_features=15000,analyzer=dummy,preprocessor=dummy)\n\ntrain_set = tfid.fit_transform(x_train_b)\ntest_set = tfid.transform(x_test_b)\n\nmnb = MultinomialNB()\nmnb.fit(train_set,y_train)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"591f8d20d3fe820b202efd9bbfd53d5c76c47a33"},"cell_type":"code","source":"predictions = mnb.predict(test_set)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"ef4669def5b14ee83b19974461fc29673b8f7f02"},"cell_type":"code","source":"#prepare the submission\nsubmission = pd.DataFrame({\"qid\": x_test_qid,\"prediction\":predictions })\nsubmission.to_csv(\"submission.csv\", index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"ecd0b160684eaf0583b357c4524415e12a9146d9"},"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}