{"cells":[{"metadata":{},"cell_type":"markdown","source":"# 4º Simpósio de Tecnologias Aplicadas - UNISO\n\n<h2>\n<a id=\"sumario\">Sumário</a><br>\n<a href=\"#jupyter\">1. Jupyter Notebook</a><br>\n<a href=\"#python\">2. Python - sintaxe básica</a><br>\n<a href=\"#dados\">3. Lendo os dados</a><br>\n<a href=\"#wrag\">4. Manipulando os dados</a><br>\n<a href=\"#prep\">5. Preparando os dados</a><br>\n<a href=\"#train\">6. Treino e avaliação</a></h2><br>\n\n## <a id=\"jupyter\" href=\"#sumario\">1. Jupyter Notebook</a>\n\nUma das ferramentas mais populares do Python é o Jupyter Notebook, nele você pode misturar textos (Markdwon) e código.\nMuito útil para escrever relatórios que incluem saídas de códigos, gráficos, equações.\nPara instanciar um Notebook localmente, siga os seguintes passos:\n\n1. Garanta que o Python instalado (sugiro 3.6+):\n    * https://www.python.org/downloads/\n2. (Opcional) se tiver o Anaconda instalado você pode criar um ambiente virtual para evitar possíveis conflitos de pacotes. Em seu terminal digite:\n```\nconda create -n iv_simposio\nconda activate iv_simposio\n```\nps: no Linux talvez você tenha que adicionar conda ao .bash. No Windows é melhor usar o prompt do Anaconda.\n3. Garanta que o jupyter esta instalado (terminal):\n```\npip install jupyter\n```\n4. Instancie um servidor local jupyter:\n```\njupyter notebook\n```\n\n### Dica Jupyter\n\nNos blocos de codigo, você pode rodar comandos no terminal começando suas linhas com `!`. E.g., `!pip install pandas` instala a biblioteca pandas.\n\n## <a id=\"python\" href=\"#sumario\">2. Python - sintaxe básica</a>\n\nNão é preciso declarar o tipo da variável. Basta entrar com `<nome_da_variavel> = <valor_da_variavel>` para criar uma nova variável ou modificar uma existente. "},{"metadata":{"trusted":true},"cell_type":"code","source":"x = 2\ny = 3.0\numa_string = \"aspas simples ou duplas marcam strings\"\nprint(\"x vale:\", x)\nprint(\"x é um\", type(x))\nprint(\"y vale:\", y)\nprint(\"y é um\", type(y))\nprint(\"uma_string vale:\", uma_string)\nprint(\"uma_string é uma\", type(uma_string))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Os símbolos de soma (+), subtração (-), multiplicação (\\*) e divisão (/) fazem o que você espera que eles façam. Hashtags (\\#) marcam comentários nos códigos e não serão executados."},{"metadata":{"trusted":true},"cell_type":"code","source":"x = y/x # x agora y divido por ele mesmo\n# dividir um inteiro por float resulta em um inteiro\nprint(\"O novo valor de x é:\", x, \". Seu tipo agora é\", type(x))\n# somar duas strings concatena ambas\nnova_string = \"+ concatena \" + \"strings\"\nprint(nova_string)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Comparar valores com < (menor), <= (menor igual), >= (maior igual) ou > (maior) resulta em variáveis boolenas (True ou False)."},{"metadata":{"trusted":true},"cell_type":"code","source":"\"R\" > \"Python\"","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Listas são declaradas usando \\[ \\]."},{"metadata":{"trusted":true},"cell_type":"code","source":"uma_lista = [1, 2, 3, 4, 5]\nprint(\"Os elementos da lista são:\", uma_lista)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Para selecionar um elemento da sua lista use a sintaxe `<lista>[indice do elemento]`. Os indíces começam em zero. Você pode fatiar uma lista com a sintaxe `<lista>[indice_inicial : indice_final (nao incluso)]`. Se emitir o indice inicial ele começa do primeiro e omitindo o indíce final ele vai até o último."},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"O primeiro elemento é o:\", uma_lista[0])\nprint(\"Os elementos de índice 1 até 3 são:\", uma_lista[1:4])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Listam ainda podem misturar elementos de diversos tipos. Loops básicos seguem o formato:\n```\nfor <i> in <iterador>:\n    \\\\ operacoes\n```"},{"metadata":{"trusted":true},"cell_type":"code","source":"# trocando o primeiro elemento da lista\numa_lista[0] = \"Zero\"\n# uma lista pode servir como iterador\ncontador = 0                     # iniciando um contador\nfor elemento in uma_lista:       # para cada elemento da lista\n    print(contador, elemento)    # imprime o contado e o valor do elemento\n    contador += 1                # adiciona mais 1 ao contador \n                                 #(<var>+=<valor> é o mesmo <var> = <var> + <valor>)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Um loop usando while..."},{"metadata":{"trusted":true},"cell_type":"code","source":"contador = 0            # Iniciamos o contador em zero\nwhile contador < 15:    # enquanto o contador for menor do que 15, repete o loop\n    print(contador)     # imprime o valor do contador\n    if 5%2 == 0:        # o operador % retorna a sobra de uma divisao, entao com <numero>%2 == 0 queremos saber se é \n        contador += 1   # se par, soma um\n    else:               # se impar\n        contador += 3   # soma 3\n# o ultimo valor do contador foi:\nprint(\"Contador final:\", contador)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Para definir novas funoes basta seguir a sintaxe:\n\n```\ndef <nome_da_funcao>(argumentos):\n    \\\\ operacoes da funcao\n    return <valor_a_ser_retornado>\n```\n\nPor exemplo:"},{"metadata":{"trusted":true},"cell_type":"code","source":"def soma(a, b):\n    return a + b\nsoma(1,2)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## <a id=\"dados\" href=\"#sumario\">3. Lendo os dados</a>\n\nUma biblioteca ou módulo é um conjunto de funções prontas para serem usadas - não é preciso reinventar a roda. Para instalar um novo pacote (se disponível no repositório oficial), basta digitar `pip install <nome_do_modulo>` no terminal. Pacotes devem ser importados no seu programa: `import <nome_do_modulo>`. Você pode dar um apelido para o pacote em seu programa usando `import <nome_do_modulo> as <apelido>`.\n\nOs pacotes que vamos usar são:\n* os - de Operational System, vamos ver quais os arquivos disponíveis;\n* pandas - um dos mais populares, usado para leitura e manipulaçao de dados;\n* sklearn - Scikit Learn e um dos frameworks mais populares para treinar modelos.\n\nUsuários de Python costumam carregar todas as bibliotecas necessárias no topo do código."},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import os           # operações do sistema operacional\nimport pandas as pd # leitura e manipulacao de dados\nfrom sklearn.ensemble import RandomForestRegressor # modelos de ML\nimport random       # processos pseudo-aleatorios\nimport numpy as np  # algebra linear","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Em que pasta nos estamos?"},{"metadata":{"trusted":true},"cell_type":"code","source":"os.getcwd()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Quais são os arquivos disponíveis?"},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"import os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"A função read_csv do pandas permite a leitura de CSVs. Os argumentos que ela recebe são:\n\n* filepath_or_buffer = caminho ate o arquivo (obrigatório) str\n* sep = separador de colunas (opcional, padrão \",\") str\n* encoding = encoding do arquivo (opcional, padrão \"utf-8\") str - se der errado tente latin1 ou latin3 (mais comuns no BR)\n* outros argumentos https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_csv.html\n\nA função `pandas.read_csv()` retorna uma variável do tipo DataFrame, com seus próprios métodos. É muito comum depois de ler um CSV, chamar por `.head()` e `.tail()` para ler as primeiras e ultimas linhas respectivamente."},{"metadata":{"trusted":true},"cell_type":"code","source":"df_train = pd.read_csv(filepath_or_buffer=\"/kaggle/input/ashrae-energy-prediction/train.csv\", \n                       sep=\",\", \n                       encoding=\"utf-8\")\n\ndf_train.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Lendo os documentos disponibilizados eu sei que:\n* `building_id` corresponde a um ID único do edifício;\n* `meter` é o tipo de medidor (0 para eletricidade, 1 para água fria, 2 para vapor e 3 para água quente);\n* `timestamp` data de quando a medida foi tirada;\n* `meter_reading` é o TARGET, o gasto de energia que queremos prever."},{"metadata":{"trusted":true},"cell_type":"code","source":"df_train.tail()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Me chamou a atenção a quantidade de zeros que encontramos neste exame rápido. Qual a porcentagem de zeros no target do dataset de treino?"},{"metadata":{"trusted":true},"cell_type":"code","source":"(df_train[\"meter_reading\"] == 0).mean()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Em `df_train[\"meter_reading\"] == 0` estamos criando uma espécie de lista (pandas Series para ser mais exato) e para cada valor da lista retornamos um booleano com True se o target for igual a 0 ou False caso contrário. Em seguida chamamos o método `mean()` para toda essa série. Ele vai retornar a média para tal, interpretando True como 1 e False como 0, logo, o resultado é a porcentagem de valores iguais a zero, que foi cerca de 9,27%.\n\nRepare que para selecionar apenas uma coluna do nosso dataset chamamos ele mesmo e em seguida o nome da coluna entre colchetes. Os dois principais métodos para \"fatiar\" datasets são `.loc[]` e `.iloc[]`.\n\nAlém do `head()` e `tail()`, outro método muito útil é o `describe()`, que traz algumas estatísticas descritivas sobre o dataset."},{"metadata":{"trusted":true},"cell_type":"code","source":"df_train.describe()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Uma pergunta muito importante é \"temos dados suficientes?\". Para responder essa pergunta usamos o método `shape` que devolve uma lista em que o primeiro elemento é o número de linhas e o segundo é o número de colunas."},{"metadata":{"trusted":true},"cell_type":"code","source":"df_train.shape","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Vamos dar uma olhada no dataset de testes:"},{"metadata":{"trusted":true},"cell_type":"code","source":"df_test = pd.read_csv(filepath_or_buffer=\"/kaggle/input/ashrae-energy-prediction/test.csv\", \n                      sep=\",\", \n                      encoding=\"utf-8\")\ndf_test.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"No mundo real isso serviria como entrada para os nossos modelos depois de treinamos. É o que gostariamos de prever! Note que a coluna `meter_reading` não está disponível."},{"metadata":{},"cell_type":"markdown","source":"## <a id=\"wrang\" href=\"#sumario\">4. Manipulando os dados</a>\n\nMas vamos prever o consumo sem melhorias só com o ID do prédio, tipo de medidor? Não! A competição também disponibilizou dados de clima e informações do prédio. Para simplificar vamos ficar só com as informações do prédio."},{"metadata":{"trusted":true},"cell_type":"code","source":"df_building = pd.read_csv(filepath_or_buffer=\"/kaggle/input/ashrae-energy-prediction/building_metadata.csv\", \n                       sep=\",\", \n                       encoding=\"utf-8\")\ndf_building.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"Número de linhas:\", df_building.shape[0])\nprint(\"Número de colunas:\", df_building.shape[1])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"Numero de IDs únicos de prédio no dataset de treino:\",df_train[\"building_id\"].nunique())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"Numero de IDs únicos de prédio no dataset de teste:\",df_test[\"building_id\"].nunique())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Parece tudo certo, mas o seguro morreu de velho. Vamos conferir se tudo que está nos treino/teste está incluído neste dataset."},{"metadata":{"trusted":true},"cell_type":"code","source":"porcentagem_do_treino = df_train[\"building_id\"].isin(df_building[\"building_id\"]).mean() * 100\nporcentagem_do_teste = df_test[\"building_id\"].isin(df_building[\"building_id\"]).mean() * 100\n\nprint(\"Treino:\", porcentagem_do_treino, \"%\")\nprint(\"Teste:\", porcentagem_do_teste, \"%\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Duas coisas chamaram minha atenção nos metadados dos edifícios: duas colunas que podem ser utilizadas como variáveis categóricas (`primary_use` e `site_id`) e varios NaNs (Not a Number), que correspondem valores vazios no nosso dataset."},{"metadata":{"trusted":true},"cell_type":"code","source":"df_building.isnull().sum()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Interessante! Tanto o ano de construção do prédio (`year_built`) quanto o número de andares (`floor_count`) tem uma grande quantidade de não preenchidos (informações faltando). Vamos tentar usar isso ao nosso favor. Agora vamos adicionar essas informações ao dataset de treino usando a chave `building_id`.\n\nAntes de fazer essa união, precisamos garantir que essa coluna está preenchida com o mesmo tipo de variável nos diferentes datasets:"},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"Tipo em df_building:\", df_building[\"building_id\"].dtype)\nprint(\"Tipo em df_train:\", df_train[\"building_id\"].dtype)\nprint(\"Tipo em df_test:\", df_test[\"building_id\"].dtype)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Tudo certo! Podemos seguir em frente."},{"metadata":{"trusted":true},"cell_type":"code","source":"df_train_new = df_train.merge(df_building, \n                              on=\"building_id\",       # coluna chave, tambem pode ser uma lista com mais de uma coluna \n                              how=\"left\",             # left, right, outer, inner\n                              validate=\"many_to_one\") # opicional <many ou one>_to_<many ou one>\ndf_train_new.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"O argumento `validate` é muito útil para garantir que linhas adicionais não serão criadas. Devemos adicionar as mesmas linhas ao dataset de teste."},{"metadata":{"trusted":true},"cell_type":"code","source":"df_test_new = df_test.merge(df_building, on=\"building_id\", how=\"left\", validate=\"many_to_one\")\ndf_test_new.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Vou liberar memória deletando os antigos dataframes que não serão mais utilizados."},{"metadata":{"trusted":true},"cell_type":"code","source":"del df_test, df_train, df_building","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## <a id=\"prep\" href=\"#sumario\">5. Preparando os Dados</a>\n\nNesta etapa nós vamos dividir o dataset de treinamento em três partes:\n* A) Ajustar o modelo\n* B) Encontrar os melhores hiperparâmetros\n* C) Estimar o desempenho dos modelos (para os mesmos edifícios)\n\nComo temos disponível uma coluna de tempo, vou fazer o split cronológico, pra isso é útil converter a coluna de tempo no formato datetime. "},{"metadata":{"trusted":true},"cell_type":"code","source":"df_train_new[\"timestamp\"] = pd.to_datetime(df_train_new[\"timestamp\"])\ndf_test_new[\"timestamp\"] = pd.to_datetime(df_test_new[\"timestamp\"])\n\nprint(\"df_train_new começa em\", df_train_new[\"timestamp\"].min(), \"e termina em\", df_train_new[\"timestamp\"].max())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Não temos mais de um ano de dados, essa é uma notícia ruim. Idealmente teriamos mais de um ano para A e pelo menos um ano para B e C, assim controlariamos o fator sazonalidade. Dadas as circunstâncias optei por não preservar a ordem cronológicae sortear aleatoriamente as observações utilizadas. Vamos usar cerca de 70% das observações para A, 15% para B e outros 15% para C."},{"metadata":{"trusted":true},"cell_type":"code","source":"todos_indices = df_train_new.index.tolist()\nprint(\"15% das obs. de df_train_new são cerca de\",len(todos_indices)*.15)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Vamos separar os índices de A, B e C."},{"metadata":{"trusted":true},"cell_type":"code","source":"random.seed(2020)\n# selecionando os indices de b\nindices_b = random.choices(todos_indices, k = 3032415)\n# separando os indices disponiveis\nindices_disponiveis = list( set(todos_indices) - set(indices_b))\n# sorteando os indices de c\nindices_c = random.choices(indices_disponiveis, k = 3032415)\n# separando os indices de a\nindices_a = list( set(indices_disponiveis) - set(indices_c))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Agora vamos montar um encoder! Para o modelo que vou usar de baseline (Florestas Aleatórias), o chamado mean encoder é bastante indicado. Para mais sobre mean encoder consulte:\nhttps://towardsdatascience.com/why-you-should-try-mean-encoding-17057262cd0\n\nPara quais variáveis vamos utilizar o mean encoder?\n\nR: Categoricas (building_id, site_id, primary_use)"},{"metadata":{"trusted":true},"cell_type":"code","source":"colunas_categoricas = [\"building_id\", \"site_id\", \"primary_use\"]\n\nfor coluna_categorica in colunas_categoricas:\n    # separando os valores para o encoder\n    # para criar o enconder vamos restringir as dados\n    # da amostra a usando \"df_train_new.loc[indices_a]\"\n    mean_encoder = df_train_new.loc[indices_a].groupby(coluna_categorica)[\"meter_reading\"].mean()\n    # mapeando para os valores da coluna categorica\n    df_train_new[coluna_categorica] = df_train_new[coluna_categorica].map(mean_encoder)\n    # repetimos o mesmo processo para df_test_new\n    df_test_new[coluna_categorica] = df_test_new[coluna_categorica].map(mean_encoder)    ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Pronto, agora que mapeamos as categoricas cardinais em categoricas ordinais (isso é importante para as florestas aleatórias), podemos prosseguir. Vamos escolher um valor arbitrário para os não preenchidos, mas pra isso vamos ver quais são os valores mínimos observados em cada coluna de ambos datasets."},{"metadata":{"trusted":true},"cell_type":"code","source":"df_train_new.min()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_test_new.min()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Nenhum número negativo. Vou preencher os vazios com -10."},{"metadata":{"trusted":true},"cell_type":"code","source":"# Pequeno truque para reduzir a memoria\ndf_train_new[\"year_built\"] = df_train_new[\"year_built\"] - 1900\ndf_test_new[\"year_built\"] = df_test_new[\"year_built\"] - 1900\n# Prencheendo vazios\ndf_train_new = df_train_new.fillna(-10)\ndf_test_new = df_test_new.fillna(-10)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Para reduzir a memoria ...\n\nKibado de https://www.kaggle.com/caesarlupum/ashrae-start-here-a-gentle-introduction#5.-Reducing-Memory-Size"},{"metadata":{"trusted":true},"cell_type":"code","source":"## Function to reduce the DF size\ndef 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\n\ndf_train_new = reduce_mem_usage(df_train_new)\ndf_test_new = reduce_mem_usage(df_test_new)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Pra salvar mais memória vou remover os timestamp e deixar só uma variável que denota mês como inteiro:"},{"metadata":{"trusted":true},"cell_type":"code","source":"df_train_new[\"month\"] = df_train_new[\"timestamp\"].dt.month.astype(\"uint8\")\ndf_train_new = df_train_new.drop(\"timestamp\", 1)\ndf_test_new[\"month\"] = df_test_new[\"timestamp\"].dt.month.astype(\"uint8\")\ndf_test_new = df_test_new.drop(\"timestamp\", 1)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Tudo pronto para o treino e avaliação!\n\n## <a id=\"train\" href=\"#sumario\">6. Treino e avaliação</a>\n\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"features = [\n    \"building_id\",\n    \"meter\",\n    \"site_id\",\n    \"primary_use\",\n    \"square_feet\",\n    \"year_built\",\n    \"floor_count\",\n]\n\ntarget = \"meter_reading\"\n\nmodelo = RandomForestRegressor(n_estimators=10, max_depth=10, random_state=2020)\nmodelo.fit(df_train_new.loc[indices_a, features], df_train_new.loc[indices_a, target])\n","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}