{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Imports"},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"import gc\nimport os\nimport numpy as np\nimport pandas as pd\nimport random\nimport sys\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport plotly.graph_objs as go\nimport plotly.express as px\nimport plotly.figure_factory as ff\nimport plotly.offline as py\n\nfrom pathlib import Path\nfrom glob import glob\nfrom tqdm import tqdm_notebook as tqdm \nfrom IPython.core.display import display, HTML\nfrom plotly import tools, subplots\nfrom sklearn.metrics import mean_squared_error\n\n\npy.init_notebook_mode(connected=True)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Metric"},{"metadata":{"trusted":true},"cell_type":"code","source":"def metric(y_true, y_pred):\n    return mean_squared_error(np.log1p(y_true), np.log1p(y_pred))**(0.5)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Prior Config"},{"metadata":{"trusted":true},"cell_type":"code","source":"USE_STACKING = False\nUSE_HYPEROPT = False\nUSE_GENETIC_ALG = True\nEXCLUDE_LIST =[\n    'bland-nn-on-pp-leaks-train-fe_version3',\n    'bland-lgbt-on-leaks_version1',\n    'bland-lgbt-on-leaks_version2',\n    'ashrae-exploiting-leak-site-5',\n    'ashrae-1-1-to-1-06-with-ucl',\n    'ashrae-divide-and-conquer-fix0'\n               ]\n\nADD_LIST = []","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Reduce MEM usage"},{"metadata":{"trusted":true},"cell_type":"code","source":"\n# Original code from https://www.kaggle.com/gemartin/load-data-reduce-memory-usage by @gemartin\n# Modified to support timestamp type, categorical type\n# Modified to add option to use float16 or not. feather format does not support float16.\nfrom pandas.api.types import is_datetime64_any_dtype as is_datetime\nfrom pandas.api.types import is_categorical_dtype\n\ndef reduce_mem_usage(df, use_float16=False):\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        if is_datetime(df[col]) or is_categorical_dtype(df[col]):\n            # skip datetime type or categorical type\n            continue\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 use_float16 and 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","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# LOAD Leak data"},{"metadata":{"trusted":true},"cell_type":"code","source":"leak_df = reduce_mem_usage(pd.read_csv('/kaggle/input/leakaggregator/leaked_test_target.csv'))\n\nnot_nan_values_in_leak_df = ~leak_df['meter_reading'].isna()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"not_nan_values_in_leak_df.value_counts(normalize=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"leak_df.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Load submissions"},{"metadata":{"trusted":true},"cell_type":"code","source":"datasets_to_take = [\n    '/kaggle/input/ashrae-submissions/*.csv',\n    '/kaggle/input/ashrae-submissions-1/*.csv',\n    '/kaggle/input/ashrae-submissions-2/*.csv',\n    '/kaggle/input/ashrae-submissions-3/*.csv',\n    '/kaggle/input/ashrae-submissions-4/*.csv',\n    '/kaggle/input/ashrae-submissions-5/*.csv',\n    '/kaggle/input/ashrae-submissions-6/*.csv'\n]\n\nfiles_to_take = []\nfor dtt in datasets_to_take:\n    files_to_take += glob(dtt)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"all_sumbissions = pd.DataFrame()\nall_sumbissions['row_id'] = leak_df['row_id']\n\nif ADD_LIST:\n    print('ADD MODE')\n    for df_name in tqdm(files_to_take):\n        if os.path.basename(df_name).split('.')[0] in ADD_LIST:\n            print(df_name)\n            all_sumbissions[os.path.basename(df_name).split('.')[0]] = reduce_mem_usage(pd.read_csv(df_name)).sort_values('row_id')['meter_reading']\n            gc.collect()\nelse:\n    print('EXCLUDE MODE')\n    for df_name in tqdm(files_to_take):\n        if os.path.basename(df_name).split('.')[0] in EXCLUDE_LIST:\n            continue\n        print(df_name)\n        all_sumbissions[os.path.basename(df_name).split('.')[0]] = reduce_mem_usage(pd.read_csv(df_name)).sort_values('row_id')['meter_reading']\n        gc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"gc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"all_sumbissions.columns","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Small postprocessing"},{"metadata":{"trusted":true},"cell_type":"code","source":"for col in all_sumbissions.columns[all_sumbissions.min() < 0]:\n    all_sumbissions.loc[all_sumbissions[col] < 0, col] = 0","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Leak Validation for public kernels(not used leak data)"},{"metadata":{"trusted":true},"cell_type":"code","source":"gc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for col in all_sumbissions.columns[1:]:\n    print(col)\n    leak_score = metric(leak_df.loc[not_nan_values_in_leak_df, 'meter_reading'],\n                        all_sumbissions.loc[not_nan_values_in_leak_df, col])\n    print ('score1=', leak_score)\n    gc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"leak_score = metric(leak_df.loc[not_nan_values_in_leak_df, 'meter_reading'], all_sumbissions.loc[not_nan_values_in_leak_df].iloc[:,1:].mean(axis=1))\nprint ('mean score=', leak_score) ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"leak_score = metric(leak_df.loc[not_nan_values_in_leak_df, 'meter_reading'], all_sumbissions.loc[not_nan_values_in_leak_df].iloc[:,1:].median(axis=1))\nprint ('mean score=', leak_score) # 0.963450549517071","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Genetic Alg "},{"metadata":{"trusted":true},"cell_type":"code","source":"from itertools import permutations\n\nclass GenerativeAlgorithm(object):\n    def __init__(self, \n                 x_shape,\n                 amout_ind_to_mutate,\n                 population_size, \n                 target_function, \n                 num_generations, \n                 populations_to_take,\n                 available_indices,\n                 mode='min'):\n        self.amout_ind_to_mutate = amout_ind_to_mutate\n        self.x_shape = x_shape\n        self.population_size = population_size\n        self.target_function = target_function\n        self.num_generations = num_generations\n        self.populations_to_take = populations_to_take\n        self.available_indices = available_indices\n        self.mode = mode\n        \n        self.gen_initial_generation()\n        \n        if self.mode == 'min':\n            self.best_score = 1000000\n        if self.mode == 'max':\n            self.best_score = -1000000\n            \n    def gen_initial_generation(self):\n        self.initial_generation = []\n        for i, el in enumerate(permutations(self.available_indices, self.x_shape)):\n            if i == self.population_size:\n                break\n            self.initial_generation.append(list(el))\n\n        self.initial_generation = np.array(self.initial_generation)\n        \n    def select_mating_pool(self, population, fitness):\n        \n        parents = np.empty((self.populations_to_take, population.shape[1]))\n\n        for parent_num in range(self.populations_to_take):\n            \n            if self.mode == 'min':\n                fitness_idx = np.argmin(fitness)\n            elif self.mode == 'max':\n                fitness_idx = np.argmax(fitness)\n            else:\n                raise ValueError('Not implemented!')\n                \n            parents[parent_num, :] = population[fitness_idx, :]\n            \n                        \n            if self.mode == 'min':\n                fitness[fitness_idx] = 1000000\n            elif self.mode == 'max':\n                fitness[fitness_idx] = - 1000000\n\n        return parents\n    \n    def crossover(self, parents, offspring_size):\n        offspring = np.empty(offspring_size)\n        crossover_point = np.uint8(offspring_size[1]/2)\n\n        for k in range(offspring_size[0]):\n            parent1_idx = np.random.randint(0, parents.shape[0])\n            parent2_idx = np.random.randint(0, parents.shape[0])\n\n            offspring[k, :crossover_point] = parents[parent1_idx, :crossover_point]\n            offspring[k, crossover_point:] = parents[parent2_idx, crossover_point:]\n            \n        return offspring\n\n    def mutation(self, offspring_crossover):\n\n        for idx in range(offspring_crossover.shape[0]):\n            \n            \n            for i in range(self.amout_ind_to_mutate):\n                cur_available_idices = list(set(self.available_indices) - set(offspring_crossover[idx,:]))\n                \n                value = np.random.choice(cur_available_idices)\n                idx_1 = np.random.randint(0,offspring_crossover.shape[1])\n            \n                offspring_crossover[idx, idx_1] = value\n                                \n        return offspring_crossover\n\n    def apply_function_on_array(self, ar):\n        return np.array([self.target_function(ar[i,:]) for i in range(ar.shape[0])])\n\n    def fit(self):\n        cur_population = self.initial_generation\n        history = {'best_points':[], 'best_fits':[]}\n        generation = 0\n        \n        while generation < self.num_generations:\n\n            fitness = self.apply_function_on_array(cur_population)\n                        \n            if self.mode == 'min':\n                best_idx = np.argmin(fitness)\n            elif self.mode == 'max':\n                best_idx = np.argmax(fitness)\n                \n            history['best_points'].append(cur_population[best_idx,:])\n            history['best_fits'].append(fitness[best_idx])\n            self.best_score = fitness[best_idx]\n            \n            print('Itteration {} best score: {}'.format(generation, self.best_score))\n            \n            parents = self.select_mating_pool(cur_population, fitness)\n            \n            offspring_crossover = self.crossover(parents,\n                                                 offspring_size=(self.population_size-parents.shape[0], cur_population.shape[1]))\n            \n            offspring_mutation = self.mutation(offspring_crossover)\n            \n            cur_population[:parents.shape[0], :] = parents\n            cur_population[parents.shape[0]:, :] = offspring_mutation\n                        \n            generation+=1 \n            \n        fitness = self.apply_function_on_array(cur_population)\n        \n        if self.mode == 'min':\n            best_idx = np.argmin(fitness)\n        elif self.mode == 'max':\n            best_idx == np.argmax(fitness)\n            \n        history['best_points'].append(cur_population[best_idx,:])\n        history['best_fits'].append(fitness[best_idx])\n            \n        return history","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"if USE_GENETIC_ALG:\n    def tgt_f(col_idx):\n        sc = metric(leak_df.loc[not_nan_values_in_leak_df, 'meter_reading'], \n                                   all_sumbissions.loc[not_nan_values_in_leak_df].iloc[:,col_idx].median(axis=1))\n        return sc\n\n\n    a_ind = np.array(range(1, all_sumbissions.shape[1]-1))\n    \n    alg = GenerativeAlgorithm(\n    amout_ind_to_mutate=6,\n    population_size=20,\n    x_shape=11,\n    target_function=tgt_f, \n    num_generations=2, \n    populations_to_take=6,\n    available_indices=a_ind\n    )\n    \n    h = alg.fit()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"if USE_GENETIC_ALG:\n    best_cols = h['best_points'][-1]\n    \n    m = metric(leak_df.loc[not_nan_values_in_leak_df, 'meter_reading'], all_sumbissions.loc[not_nan_values_in_leak_df].iloc[:,best_cols].median(axis=1))\n    print('best_comb: {}\\nbest_metric: {}'.format(all_sumbissions.columns[best_cols], m))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Hyperopt"},{"metadata":{"trusted":true},"cell_type":"code","source":"if USE_HYPEROPT:\n    import hyperopt as hp\n    \n    score_comb = []\n    \n    def objective(x):\n        cols_idx = [el[1] for el in x]\n        \n        if len(set(cols_idx)) != len(cols_idx):\n            return 100\n        \n        m = metric(leak_df.loc[not_nan_values_in_leak_df, 'meter_reading'], all_sumbissions[not_nan_values_in_leak_df].iloc[:,cols_idx].median(axis=1))\n        gc.collect()\n        score_comb.append([cols_idx,m])\n        return m\n    \n    space = [(str(i),1 + hp.hp.randint(str(i), all_sumbissions.shape[1]-1)) for i in range(10)]\n\n    best = hp.fmin(objective, space, algo=hp.tpe.suggest, max_evals=1000)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"if USE_HYPEROPT:\n    best_cols = pd.DataFrame(score_comb).sort_values(1).iloc[0,0]\n    \n    m = metric(leak_df.loc[not_nan_values_in_leak_df, 'meter_reading'], all_sumbissions.loc[not_nan_values_in_leak_df].iloc[:,best_cols].median(axis=1))\n    print('best_comb: {}\\nbest_metric: {}'.format(all_sumbissions.columns[best_cols], m))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Prepare data for stacking"},{"metadata":{"trusted":true},"cell_type":"code","source":"if USE_STACKING:\n    for col in all_sumbissions.columns[1:]:\n        all_sumbissions[col] = np.log1p(all_sumbissions[col])\n\n    leak_df['meter_reading'] = np.log1p(leak_df['meter_reading'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"gc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"if USE_STACKING:\n    all_sumbissions['target'] = leak_df['meter_reading']\n\n    del leak_df\n    gc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"if USE_STACKING:\n    train = all_sumbissions[not_nan_values_in_leak_df]\n\n    test = all_sumbissions[~not_nan_values_in_leak_df]\n\n    del all_sumbissions\n    gc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"if USE_STACKING:\n    train = train.set_index('row_id')\n    test = test.set_index('row_id')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"gc.collect()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Stacking"},{"metadata":{"trusted":true},"cell_type":"code","source":"class NNModel(object):\n    def __init__(self, model, target_variable='target'):\n        self.model = model\n        \n        self.es = callbacks.EarlyStopping(monitor='val_loss', min_delta=0.0001, patience=EARLY_STOPPING, verbose=False, mode='auto', restore_best_weights=True)\n        self.rlr = callbacks.ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=3, min_lr=1e-6, mode='auto', verbose=False)\n        \n        self.target_variable = target_variable\n        \n        self.scaling_stats = {}\n        self.predictors = ['bland-lgbt-folds_version_1', 'bland-lgbt-folds_version_2',\n       'pp-lgbt-refactored_version_7',\n       'bland-lgbt-on-pp-leaks-train-fe_version1',\n       'bland-nn-on-pp-leaks-train-fe_version1',\n       'Bland LGBT on PP  Leaks Train  FE_version4',\n       'bland-nn-on-pp-leaks-train-fe_version2',\n       'Bland LGBT on PP  Leaks Train  FE_version2']\n        \n    def train_preprocessing(self, data):\n\n        y = data[self.target_variable]\n        \n        data = data[self.predictors]\n        print(data.shape)\n        \n        for col in self.predictors:\n            self.scaling_stats[col] = {'mean':data[col].mean(), 'std':data[col].std()}\n            data[col] = (data[col] - self.scaling_stats[col]['mean']) / self.scaling_stats[col]['std']\n            \n        print(data.isna().sum().sum())\n        print('Scaling completed!')\n        \n        gc.collect()\n        \n        return data.values, y.values\n    \n    def test_preprocessing(self, data, is_val=False):\n        \n        if is_val:\n            y = data[self.target_variable]\n            \n        data = data[self.predictors]\n        gc.collect()\n        \n        for col in self.predictors:\n            data[col] = (data[col] - self.scaling_stats[col]['mean']) / self.scaling_stats[col]['std']\n        \n        gc.collect()\n        if is_val:\n            return data.values, y.values\n        else:\n            return data.values\n        \n    def fit(self, data, data_val):\n        \n        data, y_train = self.train_preprocessing(data)\n        data_val, y_val = self.test_preprocessing(data_val, is_val=True)\n        \n        self.model.fit(\n            data, y_train, epochs=N_EPOCHS, batch_size=BATCH_SIZE, validation_data=(data_val, y_val), verbose=True, callbacks=[self.es, self.rlr]\n        )\n            \n    def predict(self, data):\n        \n        data = self.test_preprocessing(data, is_val=False)\n        \n        return self.model.predict(data, batch_size=BATCH_SIZE, verbose=True).flatten()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.model_selection import KFold\n\ndef time_val(data, model_create_func, metric_to_use=mean_squared_error, target_var_name='target', test_to_predict=None):\n    kf = KFold(n_splits=NUM_FOLDS, shuffle=True, random_state=42)\n    \n    print('Starting Validation')\n    results = []\n    data['pred'] = 0\n    if test_to_predict is not None:\n        test_prediction = []\n        \n    for train_idx, test_idx in kf.split(data):\n        print('New Itter')\n        model = model_create_func()\n        model.fit(data.iloc[train_idx].reset_index(drop=True), \n                  data.iloc[test_idx].reset_index(drop=True))\n        \n        gc.collect()\n        \n        data['pred'].iloc[test_idx] = model.predict(data.iloc[test_idx].reset_index(drop=True))\n        \n        gc.collect()\n        itter_metric = metric_to_use(data.iloc[test_idx][target_var_name], data['pred'].iloc[test_idx])\n        print('Itter metric: '+str(itter_metric))\n        results.append(itter_metric)\n        \n        gc.collect()\n        \n        if test_to_predict is not None:\n            test_prediction.append(model.predict(test_to_predict))\n        \n        gc.collect()\n     \n    if test_to_predict is not None:\n        return results, sum(test_prediction)/NUM_FOLDS\n    else:\n        return results\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from tqdm import tqdm\n\nimport keras.backend as K\n\nfrom keras.layers import Input, Dense, Dropout, Embedding, Concatenate, Lambda\nfrom keras.models import Model\nfrom keras.optimizers import Adam, Nadam\nfrom keras import callbacks\n\ndef root_mean_squared_error(y_true, y_pred):\n        return K.sqrt(K.mean(K.square(y_pred - y_true)))\n\ndef create_model(inp_dim):\n    inps = Input(shape=(inp_dim,))\n    \n    x = Dense(1)(inps)\n    model = Model(inputs=inps, outputs=x)\n    model.compile(\n        optimizer=Nadam(lr=1e-3),\n        loss=root_mean_squared_error\n    )\n    return model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"gc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"if USE_STACKING:\n    NUM_FOLDS = 5\n    N_EPOCHS = 3\n    BATCH_SIZE = 256\n    EARLY_STOPPING = 2\n\n    my_model_create_f = lambda : NNModel(model = create_model(8))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"if USE_STACKING:\n    rf_res, test['target'] = time_val(train, my_model_create_f, test_to_predict=test)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"if USE_STACKING:\n    print('Result: {} +/- {}'.format(round(np.mean(rf_res),5), round(np.std(rf_res),5)))\n    print(mean_squared_error(train['pred'], train['target']))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"if USE_STACKING:\n    train = train.rename(columns={'pred':'meter_reading'})\n    test = test.rename(columns={'target':'meter_reading'})\n\n    gc.collect()\n\n    sub = pd.concat([train['meter_reading'], test['meter_reading']], axis=0)\n    sub = sub.reset_index()\n\n    sub['meter_reading'] = np.expm1(sub['meter_reading'])\n    sub.loc[sub['meter_reading'] < 0, 'meter_reading'] = 0\n\n    sub = sub.sort_values('row_id')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Prepare submit"},{"metadata":{"trusted":true},"cell_type":"code","source":"if USE_GENETIC_ALG or USE_HYPEROPT:\n    all_sumbissions['meter_reading'] = all_sumbissions.iloc[:,best_cols].median(axis=1)\n    all_sumbissions = all_sumbissions[['row_id','meter_reading']]\n    gc.collect()\n    all_sumbissions[['row_id','meter_reading']].to_csv('submission_blend.csv', index=False)\nelif USE_STACKING:\n    sub.to_csv('submission_blend.csv', index=False)\nelse:\n    raise ValueError('Not ready')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","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.6.6"}},"nbformat":4,"nbformat_minor":1}