{"cells":[{"metadata":{"trusted":true},"cell_type":"code","source":"# Libraries Import\nimport pandas as pd\nimport numpy as np\n\n# Opening training dataset\ndf_train = pd.read_csv(\"../input/train.csv\")\ndf_test = pd.read_csv(\"../input/test.csv\")\ndf_str = pd.read_csv(\"../input/structures.csv\")\n\n# Infos\ndf_train.info()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Initial data processing"},{"metadata":{"trusted":true},"cell_type":"code","source":"df_test.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_train.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_str.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Auxiliary Functions"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Credits for: https://www.kaggle.com/arjanso/reducing-dataframe-memory-size-by-65\ndef reduce_memory(props):\n    start_mem_usg = props.memory_usage().sum() / 1024**2 \n    print(\"Memory usage of properties dataframe is :\",start_mem_usg,\" MB\")\n    NAlist = [] # Keeps track of columns that have missing values filled in. \n    for col in props.columns:\n        if props[col].dtype != object:  # Exclude strings\n            \n            # Print current column type\n            print(\"******************************\")\n            print(\"Column: \",col)\n            print(\"dtype before: \",props[col].dtype)\n            \n            # make variables for Int, max and min\n            IsInt = False\n            mx = props[col].max()\n            mn = props[col].min()\n            \n            # Integer does not support NA, therefore, NA needs to be filled\n            if not np.isfinite(props[col]).all(): \n                NAlist.append(col)\n                props[col].fillna(mn-1,inplace=True)  \n                   \n            # test if column can be converted to an integer\n            asint = props[col].fillna(0).astype(np.int64)\n            result = (props[col] - asint)\n            result = result.sum()\n            if result > -0.01 and result < 0.01:\n                IsInt = True\n\n            \n            # Make Integer/unsigned Integer datatypes\n            if IsInt:\n                if mn >= 0:\n                    if mx < 255:\n                        props[col] = props[col].astype(np.uint8)\n                    elif mx < 65535:\n                        props[col] = props[col].astype(np.uint16)\n                    elif mx < 4294967295:\n                        props[col] = props[col].astype(np.uint32)\n                    else:\n                        props[col] = props[col].astype(np.uint64)\n                else:\n                    if mn > np.iinfo(np.int8).min and mx < np.iinfo(np.int8).max:\n                        props[col] = props[col].astype(np.int8)\n                    elif mn > np.iinfo(np.int16).min and mx < np.iinfo(np.int16).max:\n                        props[col] = props[col].astype(np.int16)\n                    elif mn > np.iinfo(np.int32).min and mx < np.iinfo(np.int32).max:\n                        props[col] = props[col].astype(np.int32)\n                    elif mn > np.iinfo(np.int64).min and mx < np.iinfo(np.int64).max:\n                        props[col] = props[col].astype(np.int64)    \n            \n            # Make float datatypes 32 bit\n            else:\n                props[col] = props[col].astype(np.float32)\n            \n            # Print new column type\n            print(\"dtype after: \",props[col].dtype)\n            print(\"******************************\")\n    \n    # Print final result\n    print(\"___MEMORY USAGE AFTER COMPLETION:___\")\n    mem_usg = props.memory_usage().sum() / 1024**2 \n    print(\"Memory usage is: \",mem_usg,\" MB\")\n    print(\"This is \",100*mem_usg/start_mem_usg,\"% of the initial size\")\n    return props","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Reduce memory dataframe train\ndf_train = reduce_memory(df_train)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Reduce Memory test\ndf_test = reduce_memory(df_test)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Reduce memory data structures\ndf_str = reduce_memory(df_str)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\ndef merge_datasets(df,df_str):\n    '''\n    Operation:\n        To improve performance when joining large dataframes, using the left merge helps\n    because I can keep my main dataframe information and add information from my dataframes\n    more simply and computationally efficient.\n    \n    Input:\n        Receives two merge dataframes - 1 being either the training or the test and the other being\n    the dataframe that contains the information about the atom structures.\n    \n    Exit:\n        Returns the dataframe containing the columns with information of each atom, and inform which was\n    the atom in that molecule.\n    '''\n    # Parte 1\n    df = df.rename(columns = {\"atom_index_0\":\"atom_index\"})\n    df = pd.merge(df,df_str,on = [\"molecule_name\",\"atom_index\"], how = \"left\") \n    df = df.rename(columns = {\"atom_index\":\"atom_index_0\",\"x\":\"x_0\",\"y\":\"y_0\",\"z\":\"z_0\"})\n    # Parte 2\n    df = df.rename(columns = {\"atom_index_1\":\"atom_index\"})\n    df = pd.merge(df,df_str,on = [\"molecule_name\",\"atom_index\"], how = \"left\")\n    df = df.rename(columns = {\"atom_index\":\"atom_index_1\",\"x\":\"x_1\",\"y\":\"y_1\",\"z\":\"z_1\"})\n    # Return dataframe mesclado\n    \n    df = reduce_memory(df)\n    return df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Auxiliar\ndf_test_aux = df_test.copy()\n# Merge\ndf_train = merge_datasets(df_train,df_str)\ndf_test_aux = merge_datasets(df_test_aux,df_str)\ndf_train.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Feature Engineering\n\nAt this point I will create some attributes to test the model and then check if there really is any relationship.\n\nEuclidean distance between atoms\n* Diedric Angle Between Atoms\n* Midpoint between atoms\n* Include Atomic Mass, Electronegativity, Atomic Number, Atomic Radius information\n\n\nFor the other attributes created, were based on the kernel: https://www.kaggle.com/artgor/brute-force-feature-engineering\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"def create_features(df):\n    ## Auxiliary Information\n    # Atoms Information\n    m_atomica = {\"H\":1.0079,\"C\":12.0107,\"N\":14.0067,\"O\":15.9994,\"F\":18.9984}\n    n_atomico = {\"H\":1,\"C\":6,\"N\":7,\"O\":8,\"F\":9}\n    eletro_atomico = {\"H\":2.2,\"C\":2.55,\"N\":3.04,\"O\":3.44,\"F\":3.98}\n    \n    \n    ## Basic Calculations\n    # Euclidean distance calculation\n    df[\"dist\"] = np.sqrt((df[\"x_1\"]-df[\"x_0\"])**2 +\n                                       (df[\"y_1\"]-df[\"y_0\"])**2 +\n                                       (df[\"z_1\"]-df[\"z_0\"])**2)\n\n    # Angle Calculation\n    df[\"dihedral_angle\"] = np.abs(df[\"x_0\"]*df[\"x_1\"] + df[\"y_0\"]*df[\"y_1\"] + df[\"z_0\"]*df[\"z_1\"])/(\n                                  np.sqrt(df[\"x_0\"]**2 + df[\"y_0\"]**2 + df[\"z_0\"]**2) * \n                                  np.sqrt(df[\"x_1\"]**2 + df[\"y_1\"]**2 + df[\"z_1\"]**2))\n    # Midpoint \n    df[\"dx\"] = ((df[\"x_0\"] - df[\"x_1\"])**2)\n    df[\"dy\"] = ((df[\"y_0\"] - df[\"y_1\"])**2)\n    df[\"dz\"] = ((df[\"z_0\"] - df[\"z_1\"])**2)\n\n    # Creating atomic information columns\n    df[\"atom_mass_1\"] = df[\"atom_y\"].replace(m_atomica)\n    df[\"n_atom_1\"] = df[\"atom_y\"].replace(n_atomico)\n    df[\"eletro_atom_1\"] = df[\"atom_y\"].replace(eletro_atomico)\n    df[\"n_type\"] = [int(x[0]) for x in df[\"type\"].values]\n    \n    \n    ## Merging with other columns\n    # Creating miscellaneous columns with this information\n    # Coupling Amount per Molecule\n    df['molecule_couples'] = df.groupby('molecule_name')['id'].transform('count')\n    # Coupling distance average per molecule\n    df['molecule_dist_mean'] = df.groupby('molecule_name')['dist'].transform('mean')\n    # Minimum coupling distance per molecule\n    df['molecule_dist_min'] = df.groupby('molecule_name')['dist'].transform('min')\n    # Maximum coupling distance per molecule\n    df['molecule_dist_max'] = df.groupby('molecule_name')['dist'].transform('max')\n    # Amount of coupling per molecule and atoms\n    df['atom_0_couples_count'] = df.groupby(['molecule_name', 'atom_index_0'])['id'].transform('count')\n    df['atom_1_couples_count'] = df.groupby(['molecule_name', 'atom_index_1'])['id'].transform('count')\n    # Mean, Max, Min and Std of the diehdro angle of the molecules\n    df['molecule_dihedral_angle_mean'] = df.groupby('molecule_name')['dihedral_angle'].transform('mean')\n    df['molecule_dihedral_angle_max'] = df.groupby('molecule_name')['dihedral_angle'].transform('max')\n    df['molecule_dihedral_angle_min'] = df.groupby('molecule_name')['dihedral_angle'].transform('min')\n    df['molecule_dihedral_angle_std'] = df.groupby('molecule_name')['dihedral_angle'].transform('std')\n    #- Atributos Atom_index_0\n    # Dist\n    df[f'molecule_atom_index_0_x_1_std'] = df.groupby(['molecule_name', 'atom_index_0'])['x_1'].transform('std')\n    df[f'molecule_atom_index_0_y_1_mean'] = df.groupby(['molecule_name', 'atom_index_0'])['y_1'].transform('mean')\n    df[f'molecule_atom_index_0_y_1_mean_diff'] = df[f'molecule_atom_index_0_y_1_mean'] - df['y_1']\n    df[f'molecule_atom_index_0_y_1_mean_div'] = df[f'molecule_atom_index_0_y_1_mean'] / df['y_1']\n    df[f'molecule_atom_index_0_y_1_max'] = df.groupby(['molecule_name', 'atom_index_0'])['y_1'].transform('max')\n    df[f'molecule_atom_index_0_y_1_max_diff'] = df[f'molecule_atom_index_0_y_1_max'] - df['y_1']\n    df[f'molecule_atom_index_0_y_1_std'] = df.groupby(['molecule_name', 'atom_index_0'])['y_1'].transform('std')\n    df[f'molecule_atom_index_0_z_1_std'] = df.groupby(['molecule_name', 'atom_index_0'])['z_1'].transform('std')\n    df[f'molecule_atom_index_0_dist_mean'] = df.groupby(['molecule_name', 'atom_index_0'])['dist'].transform('mean')\n    df[f'molecule_atom_index_0_dist_mean_diff'] = df[f'molecule_atom_index_0_dist_mean'] - df['dist']\n    df[f'molecule_atom_index_0_dist_mean_div'] = df[f'molecule_atom_index_0_dist_mean'] / df['dist']\n    df[f'molecule_atom_index_0_dist_max'] = df.groupby(['molecule_name', 'atom_index_0'])['dist'].transform('max')\n    df[f'molecule_atom_index_0_dist_max_diff'] = df[f'molecule_atom_index_0_dist_max'] - df['dist']\n    df[f'molecule_atom_index_0_dist_max_div'] = df[f'molecule_atom_index_0_dist_max'] / df['dist']\n    df[f'molecule_atom_index_0_dist_min'] = df.groupby(['molecule_name', 'atom_index_0'])['dist'].transform('min')\n    df[f'molecule_atom_index_0_dist_min_diff'] = df[f'molecule_atom_index_0_dist_min'] - df['dist']\n    df[f'molecule_atom_index_0_dist_min_div'] = df[f'molecule_atom_index_0_dist_min'] / df['dist']\n    df[f'molecule_atom_index_0_dist_std'] = df.groupby(['molecule_name', 'atom_index_0'])['dist'].transform('std')\n    df[f'molecule_atom_index_0_dist_std_diff'] = df[f'molecule_atom_index_0_dist_std'] - df['dist']\n    df[f'molecule_atom_index_0_dist_std_div'] = df[f'molecule_atom_index_0_dist_std'] / df['dist']\n    # dihedral_angle\n    df[f'molecule_atom_index_0_dihedral_angle_mean'] = df.groupby(['molecule_name', 'atom_index_0'])['dihedral_angle'].transform('mean')\n    df[f'molecule_atom_index_0_dihedral_angle_mean_diff'] = df[f'molecule_atom_index_0_dihedral_angle_mean'] - df['dihedral_angle']\n    df[f'molecule_atom_index_0_dihedral_angle_mean_div'] = df[f'molecule_atom_index_0_dihedral_angle_mean'] / df['dihedral_angle']\n    df[f'molecule_atom_index_0_dihedral_angle_max'] = df.groupby(['molecule_name', 'atom_index_0'])['dihedral_angle'].transform('max')\n    df[f'molecule_atom_index_0_dihedral_angle_max_diff'] = df[f'molecule_atom_index_0_dihedral_angle_max'] - df['dihedral_angle']\n    df[f'molecule_atom_index_0_dihedral_angle_max_div'] = df[f'molecule_atom_index_0_dihedral_angle_max'] / df['dihedral_angle']\n    df[f'molecule_atom_index_0_dihedral_angle_min'] = df.groupby(['molecule_name', 'atom_index_0'])['dihedral_angle'].transform('min')\n    df[f'molecule_atom_index_0_dihedral_angle_min_diff'] = df[f'molecule_atom_index_0_dihedral_angle_min'] - df['dihedral_angle']\n    df[f'molecule_atom_index_0_dihedral_angle_min_div'] = df[f'molecule_atom_index_0_dihedral_angle_min'] / df['dihedral_angle']\n    df[f'molecule_atom_index_0_dihedral_angle_std'] = df.groupby(['molecule_name', 'atom_index_0'])['dihedral_angle'].transform('std')\n    df[f'molecule_atom_index_0_dihedral_angle_std_diff'] = df[f'molecule_atom_index_0_dihedral_angle_std'] - df['dihedral_angle']\n    df[f'molecule_atom_index_0_dihedral_angle_std_div'] = df[f'molecule_atom_index_0_dihedral_angle_std'] / df['dihedral_angle']\n    #- Atributos Atom_index_1\n    ## Dist\n    df[f'molecule_atom_index_1_dist_mean'] = df.groupby(['molecule_name', 'atom_index_1'])['dist'].transform('mean')\n    df[f'molecule_atom_index_1_dist_mean_diff'] = df[f'molecule_atom_index_1_dist_mean'] - df['dist']\n    df[f'molecule_atom_index_1_dist_mean_div'] = df[f'molecule_atom_index_1_dist_mean'] / df['dist']\n    df[f'molecule_atom_index_1_dist_max'] = df.groupby(['molecule_name', 'atom_index_1'])['dist'].transform('max')\n    df[f'molecule_atom_index_1_dist_max_diff'] = df[f'molecule_atom_index_1_dist_max'] - df['dist']\n    df[f'molecule_atom_index_1_dist_max_div'] = df[f'molecule_atom_index_1_dist_max'] / df['dist']\n    df[f'molecule_atom_index_1_dist_min'] = df.groupby(['molecule_name', 'atom_index_1'])['dist'].transform('min')\n    df[f'molecule_atom_index_1_dist_min_diff'] = df[f'molecule_atom_index_1_dist_min'] - df['dist']\n    df[f'molecule_atom_index_1_dist_min_div'] = df[f'molecule_atom_index_1_dist_min'] / df['dist']\n    df[f'molecule_atom_index_1_dist_std'] = df.groupby(['molecule_name', 'atom_index_1'])['dist'].transform('std')\n    df[f'molecule_atom_index_1_dist_std_diff'] = df[f'molecule_atom_index_1_dist_std'] - df['dist']\n    df[f'molecule_atom_index_1_dist_std_div'] = df[f'molecule_atom_index_1_dist_std'] / df['dist']\n    df[f'molecule_atom_1_dist_mean'] = df.groupby(['molecule_name', 'atom_y'])['dist'].transform('mean')\n    df[f'molecule_atom_1_dist_min'] = df.groupby(['molecule_name', 'atom_y'])['dist'].transform('min')\n    df[f'molecule_atom_1_dist_min_diff'] = df[f'molecule_atom_1_dist_min'] - df['dist']\n    df[f'molecule_atom_1_dist_min_div'] = df[f'molecule_atom_1_dist_min'] / df['dist']\n    df[f'molecule_atom_1_dist_std'] = df.groupby(['molecule_name', 'atom_y'])['dist'].transform('std')\n    df[f'molecule_atom_1_dist_std_diff'] = df[f'molecule_atom_1_dist_std'] - df['dist']\n    ## dihedral_angle\n    df[f'molecule_atom_index_1_dihedral_angle_mean'] = df.groupby(['molecule_name', 'atom_index_1'])['dihedral_angle'].transform('mean')\n    df[f'molecule_atom_index_1_dihedral_angle_mean_diff'] = df[f'molecule_atom_index_1_dihedral_angle_mean'] - df['dihedral_angle']\n    df[f'molecule_atom_index_1_dihedral_angle_mean_div'] = df[f'molecule_atom_index_1_dihedral_angle_mean'] / df['dihedral_angle']\n    df[f'molecule_atom_index_1_dihedral_angle_max'] = df.groupby(['molecule_name', 'atom_index_1'])['dihedral_angle'].transform('max')\n    df[f'molecule_atom_index_1_dihedral_angle_max_diff'] = df[f'molecule_atom_index_1_dihedral_angle_max'] - df['dihedral_angle']\n    df[f'molecule_atom_index_1_dihedral_angle_max_div'] = df[f'molecule_atom_index_1_dihedral_angle_max'] / df['dihedral_angle']\n    df[f'molecule_atom_index_1_dihedral_angle_min'] = df.groupby(['molecule_name', 'atom_index_1'])['dihedral_angle'].transform('min')\n    df[f'molecule_atom_index_1_dihedral_angle_min_diff'] = df[f'molecule_atom_index_1_dihedral_angle_min'] - df['dihedral_angle']\n    df[f'molecule_atom_index_1_dihedral_angle_min_div'] = df[f'molecule_atom_index_1_dihedral_angle_min'] / df['dihedral_angle']\n    df[f'molecule_atom_index_1_dihedral_angle_std'] = df.groupby(['molecule_name', 'atom_index_1'])['dihedral_angle'].transform('std')\n    df[f'molecule_atom_index_1_dihedral_angle_std_diff'] = df[f'molecule_atom_index_1_dihedral_angle_std'] - df['dihedral_angle']\n    df[f'molecule_atom_index_1_dihedral_angle_std_div'] = df[f'molecule_atom_index_1_dihedral_angle_std'] / df['dihedral_angle']\n    df[f'molecule_atom_1_dihedral_angle_mean'] = df.groupby(['molecule_name', 'atom_y'])['dihedral_angle'].transform('mean')\n    df[f'molecule_atom_1_dihedral_angle_min'] = df.groupby(['molecule_name', 'atom_y'])['dihedral_angle'].transform('min')\n    df[f'molecule_atom_1_dihedral_angle_min_diff'] = df[f'molecule_atom_1_dihedral_angle_min'] - df['dihedral_angle']\n    df[f'molecule_atom_1_dihedral_angle_min_div'] = df[f'molecule_atom_1_dihedral_angle_min'] / df['dihedral_angle']\n    df[f'molecule_atom_1_dihedral_angle_std'] = df.groupby(['molecule_name', 'atom_y'])['dihedral_angle'].transform('std')\n    df[f'molecule_atom_1_dihedral_angle_std_diff'] = df[f'molecule_atom_1_dihedral_angle_std'] - df['dihedral_angle']\n    # Eletronegatividade\n    #df[f'molecule_atom_index_10_eletro_mean'] = df.groupby(['molecule_name', 'atom_index_1','atom_index_0'])['eletro_atom_1'].transform('mean')\n    # Reduce Memory\n    #df = reduce_memory(df)\n    \n    return df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Applying Feature Engineering\n# Train\ndf_train = create_features(df_train)\n# Test\ndf_test_aux = create_features(df_test_aux)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#df_train.corr()[\"scalar_coupling_constant\"]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Feature Selection\n# Correlation with output variable \ncor = df_train.corr()\ncor_target = abs(cor[\"scalar_coupling_constant\"]) \n\n#Selecting Highly Correlated Features \nrelevant_features_test = cor_target[(cor_target > 0.1) & (cor_target.index != \"scalar_coupling_constant\")] \nrelevant_features_train = cor_target[(cor_target > 0.1)]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#del df_train, df_test_aux\ndf_train = df_train[relevant_features_train.index]\ndf_test_aux = df_test_aux[relevant_features_test.index]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_train.info()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Modeling"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Libraries Import\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import mean_absolute_error\nfrom sklearn.preprocessing import StandardScaler\nfrom catboost import CatBoostRegressor\nfrom sklearn import preprocessing\nfrom sklearn.preprocessing import StandardScaler","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\n# X and Y\n# Train\nX = df_train.drop(\"scalar_coupling_constant\", axis = 1)\nY = df_train[\"scalar_coupling_constant\"]\n# Test\nX_test = df_test_aux.copy()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Validation\nx_train, x_test, y_train, y_test = train_test_split(X, Y, test_size=0.2, random_state=23)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Catboost\n\n#categorical = [\"type\",\"atom_y\"]\n\ncb_model = CatBoostRegressor(iterations=100,\n                             learning_rate=0.6,\n                             max_depth = 9,\n                             eval_metric='MAE',\n                             random_seed = 23,\n                             bagging_temperature = 0.4,\n                             od_type='Iter',\n                             metric_period = 100,\n                             od_wait=200,\n                             nan_mode = \"Min\")\n\ncb_model.fit(x_train, y_train,\n             eval_set=(x_test,y_test),\n             use_best_model=True,\n             plot=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fea_imp = pd.DataFrame({'imp': cb_model.feature_importances_, 'col': X.columns})\nfea_imp = fea_imp.sort_values(['imp', 'col'], ascending=[True, False]).iloc[-30:]\n_ = fea_imp.plot(kind='barh', x='col', y='imp', figsize=(20, 10))\n#plt.savefig('catboost_feature_importance.png')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Predict\ny_prev_model = cb_model.predict(X_test)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission = pd.DataFrame({\"id\":df_test[\"id\"].values,\"scalar_coupling_constant\":y_prev_model})","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission.to_csv(\"submission.csv\", index = False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"[](http://)<a href=\"./submission.csv\"> Download File </a>"},{"metadata":{"trusted":true},"cell_type":"code","source":"submission.head()","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.7.1"}},"nbformat":4,"nbformat_minor":1}