{"id":20225309,"url":"https://github.com/lubrum/desafio-zap","last_synced_at":"2026-05-06T15:32:10.115Z","repository":{"id":119173710,"uuid":"198107171","full_name":"Lubrum/Desafio-Zap","owner":"Lubrum","description":"An analysis of house/apartment prices using Python (mainly sklearn)","archived":false,"fork":false,"pushed_at":"2021-01-24T17:02:35.000Z","size":1795,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-13T23:27:18.129Z","etag":null,"topics":["json","python36","sklearn"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Lubrum.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-07-21T21:30:06.000Z","updated_at":"2021-01-24T17:02:37.000Z","dependencies_parsed_at":null,"dependency_job_id":"3d8cb905-6fff-4ae3-8ff1-eee3b40838a9","html_url":"https://github.com/Lubrum/Desafio-Zap","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lubrum%2FDesafio-Zap","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lubrum%2FDesafio-Zap/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lubrum%2FDesafio-Zap/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lubrum%2FDesafio-Zap/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Lubrum","download_url":"https://codeload.github.com/Lubrum/Desafio-Zap/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241670090,"owners_count":20000325,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["json","python36","sklearn"],"created_at":"2024-11-14T07:12:14.352Z","updated_at":"2026-05-06T15:32:10.063Z","avatar_url":"https://github.com/Lubrum.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"![GitHub commit activity](https://img.shields.io/github/commit-activity/y/Lubrum/Desafio-Zap) ![GitHub last commit](https://img.shields.io/github/last-commit/Lubrum/Desafio-Zap) ![GitHub repo size](https://img.shields.io/github/repo-size/Lubrum/Desafio-Zap)\n\n# Desafio-Zap\n\n\nOlá. O objetivo deste projeto é realizar análises descritivas, visuais e preditivas sobre dados de locações imobiliárias utilizando exclusivamente a linguagem Python.\n\nApesar da minha experiência anterior ter sido com trabalhos na linguagem R, assumi o desafio de desenvolver uma solução (em uma linguagem que recentemente comecei a trabalhar e aprender, através de cursos online) para o problema proposto pelo Grupo Zap. \n\nO projeto contém o diretório csv, com os arquivos com os resultados das predições.\nO diretório images contém as imagens geradas neste tutorial.\nO diretório py contém o código fonte deste tutorial, com comentários incluídos.\nO diretório json deveria conter os arquivos de treinamento e teste, porém por limitações do guthub não foi possível inserí-los.\n\n# Importação de bibliotecas necessárias e dos conjuntos de dados\n\nPrimeiramente, serão carregados os pacotes/bibliotecas necessárias do Python para a realização das análises. \n@ json para manipulação de dados no formato json;\n@ pandas para manipulação de dataframes\n@ skylearn para criação de modelos de aprendizado de máquina (classificadores, regressores, ...)\n@ matplotlib.pyplot para criação de gráficos\n@ numpy para funções numéricas\n@ seaborn para criação de gráficos com estatísticas\n@ pandas_profiling para gerar relatórios dinâmicos sobre os dados\n@ statistics para geração de estatísticas sobre conjuntos de dados\n@ lightgbm e xgboost para criação de modelos de aprendizado de máquina\n@ plotly para geração de gráficos interativos\n@ category_encoders para codificação de atributos categóricos\n@ cufflinks para permitir geração de gráficos no ambiente Jupyter\n\n\n```python\nimport json\nimport pandas as pd\nimport sklearn as sk\nfrom sklearn import preprocessing, svm\nfrom sklearn import linear_model\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.neural_network import MLPRegressor\nfrom sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score\nfrom sklearn.model_selection import cross_val_score, GridSearchCV, train_test_split\nfrom sklearn.cluster import KMeans\nfrom scipy.stats import uniform, randint\nimport matplotlib.pyplot as plt\nimport matplotlib.colors as colors\nimport matplotlib.cm as cmx\nimport numpy as np\nimport seaborn as sns\nimport pandas_profiling\nimport statistics as stat\nimport lightgbm as lgb\nimport xgboost as xgb\n%matplotlib inline\nimport plotly.figure_factory as ff\nimport plotly.plotly as py\nimport plotly.graph_objs as go\nimport plotly.offline as poff\nimport category_encoders as ce\nfrom plotly.offline import iplot, init_notebook_mode, download_plotlyjs, plot\nimport cufflinks as cf\ncf.go_offline()\ncf.set_config_file(offline=False, world_readable=True)\n```\n\nAgora será realizada a importação dos dados em formato json. Localmente, foi criado um diretório raiz com a pasta json e nela estão contidos os arquivos com os dados de treinamento e teste necessários para as análises. Não foi possível inserir os arquivos no github por limitações de tamanho.\nCabe mencionar que foi necessário utilizar o argumento **encoding** devido à presença de acentos e demais símbolos no arquivo. O argumento **Lines** foi utilizado para poder ler com sucesso arquivos json separados por linhas.\n\n\n```python\ndf = pd.read_json(\"json/source-4-ds-train.json\", encoding = \"utf-8\", lines = True)\ndf_teste = pd.read_json(\"json/source-4-ds-test.json\", encoding = \"utf-8\", lines = True)\n```\n\n# Pré-processamento dos dados \n\nAproveitamos a oportunidade para visualizar o que temos de dados e atributos no dataframe com a função **head** do pandas. Sabemos previamente pela descrição do problema que devemos ter 19 colunas (sendo duas delas compostas de outras informações).\n\n\n```python\nwith pd.option_context('display.max_rows', 100, 'display.max_columns', 10, 'display.max_colwidth', -1):\n    display(df.info())\n```\n\n\n```python\nwith pd.option_context('display.max_rows', 100, 'display.max_columns', 10, 'display.max_colwidth', -1):\n    display(df_teste.info())\n```\n\nFoi observada a necessidade de desaninhar três atributos no formato json: geoLocation (localização geográfica), address (endereço) e pricingInfos (informações de preços). Então foi utilizada a função **concat** para concatenar dois dataframes, o original e um derivado da coluna \"address\" do original. Para transformar a coluna \"address\" do dataframe original do formato dict para dataframe foi utilizada a função do pandas **DataFrame.from_records**, utilizando o argumento **axis** como 1 para concatenar os dataframes pelas colunas. O mesmo procedimento foi realizado para o atributo \"pricingInfos\". Ainda, foram analisados quais registros da coluna **\"geoLocation\"** eram nulos para realizar o preenchimento com um **dict** vazio. Antes, foram concatenados os dataframes com dados de treinamento e teste (0 - 133964 são dados de treinamento e restante de teste).\n\n\n```python\ndf = pd.concat([df, df_teste], axis=0, ignore_index = True) \n```\n\n\n```python\ndf = pd.concat([df, pd.DataFrame.from_records(df['address'])], axis=1) \ndf = pd.concat([df, pd.DataFrame.from_records(df['pricingInfos'])], axis=1)\ndf[\"geoLocation\"][df[\"geoLocation\"].isnull()]\ndf[\"geoLocation\"][53] = {'precision': 'missingvalue', 'location': {'lon': -1, 'lat': -1}}\ndf[\"geoLocation\"][94] = {'precision': 'missingvalue', 'location': {'lon': -1, 'lat': -1}}\ndf[\"geoLocation\"][6581] = {'precision': 'missingvalue', 'location': {'lon': -1, 'lat': -1}}\ndf[\"geoLocation\"][7713] = {'precision': 'missingvalue', 'location': {'lon': -1, 'lat': -1}}\ndf[\"geoLocation\"][8142] = {'precision': 'missingvalue', 'location': {'lon': -1, 'lat': -1}}\ndf[\"geoLocation\"][9425] = {'precision': 'missingvalue', 'location': {'lon': -1, 'lat': -1}}\ndf[\"geoLocation\"][9454] = {'precision': 'missingvalue', 'location': {'lon': -1, 'lat': -1}}\ndf[\"geoLocation\"][11577] = {'precision': 'missingvalue', 'location': {'lon': -1, 'lat': -1}}\ndf[\"geoLocation\"][11778] = {'precision': 'missingvalue', 'location': {'lon': -1, 'lat': -1}}\ndf[\"geoLocation\"][11944] = {'precision': 'missingvalue', 'location': {'lon': -1, 'lat': -1}}\ndf[\"geoLocation\"][14326] = {'precision': 'missingvalue', 'location': {'lon': -1, 'lat': -1}}\ndf[\"geoLocation\"][139729] = {'precision': 'missingvalue', 'location': {'lon': -1, 'lat': -1}}\ndf[\"geoLocation\"][139876] = {'precision': 'missingvalue', 'location': {'lon': -1, 'lat': -1}}\ndf[\"geoLocation\"][145651] = {'precision': 'missingvalue', 'location': {'lon': -1, 'lat': -1}}\n```\n\nAgora, realizamos um desmembramento da coluna de geolocalização para três novas colunas no dataframe original: **precision**, **lat** e **long** (atributos já existentes na coluna **geoLocation**, porem em um formato inadequado).\n\n\n```python\nL = []\nL2 = []\nfor i in range(0,df.shape[0]):\n    L.append(df[\"geoLocation\"][i][\"precision\"])\n    L2.append(df[\"geoLocation\"][i][\"location\"])\nLdf = pd.DataFrame(L, columns=['precision'])\nL2df = pd.DataFrame(L2, columns=['lon','lat'])\ndf = pd.concat([pd.concat([df, Ldf],axis=1),L2df],axis=1)\n```\n\nAgora podemos remover as colunas \"address\", \"pricingInfos\" e \"geoLocation\" com a função **drop**.\n\n\n```python\ndf = df.drop(\"address\", axis=1)\ndf = df.drop(\"pricingInfos\", axis=1)\ndf = df.drop(\"geoLocation\", axis=1)\n```\n\nAgora verificamos os dados após a remoção. \n\n\n```python\ndf.head()\n```\n\nAo verificar as colunas do dataframe com a função **list** e o campo **columns**, foi possível perceber que havia uma coluna adicional, chamada **period**, não mencionada na descrição do problema, que deve tratar da periodicidade do aluguel, quando aplicável.\n\n\n```python\nlist(df.columns) \n```\n\n# Dados faltantes\n\nAgora será contabilizado o percentual de registros faltantes por coluna do dataframe e serão definidas estratégias para contornar cada situação. Para isso, e outros feedbacks interessantes sobre os dados, será utilizada a biblioteca **pandas-profiling**.\nTem-se duas situações distintas a serem contabilizadas de forma automática: os casos dos NaN e das strings vazias ''.\nO código abaixo realiza a contagem e soma por coluna do número de registros com NaN e '', divide-se pelo número total de registros e multiplica-se por 100 para obter o percentual de dados faltantes por coluna.\n\n\n```python\npercentual = ((df.iloc[0:133964].isnull().sum() + (df.iloc[0:133964] == '').sum())/df.iloc[0:133964].shape[0])*100\ndf.iloc[0:133964].profile_report(style={'full_width':True})\n```\n\n## Casos detectáveis automaticamente\n\nAgora são padronizados os dados faltantes para valores -1 (numéricos) e \"missingvalue\" (objetos e strings ou outros)\n\n\n```python\ndf = df.fillna(-1)\ndf = df.replace('', \"missingvalue\")\n```\n\n## Casos não detectáveis de forma automática (Strings)\n\nO caso dos dados numéricos foi resolvido. Infelizmente, ainda tem-se os casos que não são detectados de forma automática nas colunas com outros tipos de dados. Então, após realizar as abordagens automáticas, realizamos a checagem dos registros de todas as colunas para encontrar e corrigir registros faltantes e provavelmente incorretos que foram preenchidos manualmente. Para isso, foi utilizado o código abaixo, repetido para cada coluna não-numérica do dataframe. \nDetalhe: foi criada/utilizada a função **replace_all** para substituir conjuntos de strings \n(Fonte: https://stackoverflow.com/questions/6116978/how-to-replace-multiple-substrings-of-a-string).\n\n\n```python\ndef replace_all(text, dic):\n    for i, j in dic.items():\n        text = text.replace(i, j)\n    return text\n```\n\n\n```python\nmask = (df['description'].str.len() \u003c 10)\ndf['description'][mask]\nd = {'-' : \"missingvalue\" , \".\" : \"missingvalue\" , \"..\" : \"missingvalue\", \"...\" : \"missingvalue\" , \"*\" : \"missingvalue\" , \"0\" : \"missingvalue\" , \"Aoart\" : \"missingvalue\" , \"xx\" : \"missingvalue\" , \"e\" : \"missingvalue\" , \"g\" : \"missingvalue\" , \"MD0943SB\" : \"missingvalue\" , \"....\" : \"missingvalue\" , \". .\" : \"missingvalue\" , \". ..\" : \"missingvalue\" , \"-.\" : \"missingvalue\" , \",..\" : \"missingvalue\" , \"00\" : \"missingvalue\"}\ndf['description'] = replace_all(df['description'], d) #atributo com muitos dados incorretos, agora corrigidos\ndf['listingStatus'].value_counts()\ndf['publicationType'].value_counts()\ndf['title'].value_counts()\nmask = (df['title'].str.len() \u003c 11)\ndf['title'][mask]\ndf['unitTypes'].value_counts()\ndf['city'].value_counts()\ndf['country'].value_counts()\ndf['country'] = df['country'].replace('missingvalue', \"BR\") # a cidade é São Paulo para 99% todos registros, portanto, o país é conhecido.\ndf['district'].value_counts() #atributo irrelevante pela quantidade de registros ausentes\ndf['neighborhood'].value_counts()\nmask = (df['neighborhood'].str.len() \u003c 10)\ndf['neighborhood'][mask].unique()\ndf['state'].value_counts()\ndf['state'] = df['state'].replace('SP', \"São Paulo\")\ndf['state'] = df['state'].replace('missingvalue', \"São Paulo\") # a cidade é São Paulo para 99% todos registros, portanto, o estado é conhecido.\ndf = df[df.state != 'Santa Catarina'] # um único registro de SC é considerado um ruído, já que a base é sobre SP, portanto removemos a amostra\ndf['street'].value_counts()\nmask = (df['street'].str.len() \u003c 11)\ndf['street'][mask].unique()\ndf['streetNumber'].value_counts()\ndf['unitNumber'].value_counts() #atributo irrelevante pela quantidade de registros ausentes\ndf['zipCode'].value_counts()\nmask = (df['zipCode'].str.len() \u003c 8)\ndf['zipCode'][mask].unique()\ndf['zone'].value_counts() #atributo relevante, mas com muitos dados faltantes, e redundante com outras informações de localidade do imóvel\ndf['businessType'].value_counts()\ndf['period'].value_counts()\n```\n\n\n\n\n    -1         120873\n    MONTHLY     29104\n    DAILY          19\n    YEARLY          3\n    Name: period, dtype: int64\n\n\n\n## Correção dos dados faltantes (numéricos)\n\nPara viabilizar o posterior processo de visualização dos dados, agora são checadas e corrigidos todos os atributos numéricos do dataframe. Para checar o percentual de dados faltantes por coluna, basta visualizar a variável **percentual**. Para valores incorretos ou nulos será utilizada a mediana (função **median**, do pacote **statistics**). Não recomendo utilizar essa abordagem quando os dados faltantes representarem um percentual muito grande das amostras, visto que altera a distribuição e variância dos dados, afetando na precisão dos modelos preditivos gerados. A escolha também foi consolidada devido ao espaço de tempo restante para a realização das analises posteriores. \n\nTem-se ainda outras abordagens como:\n1- excluir registros.\n2- ignorar registros.\n3- utilizar a média, mediana ou moda do atributo.\n4- inserção dedutiva.\n5- inserção utilizando análise de regressão linear.\n6- inserção utilizando análise de regressão linear estocástica.\n7- inserção com método hot-deck.\n8- inserção por máxima verossimilhança\n\nMais informações sobre o assunto podem ser verificadas no seguinte paper: \u003chttps://www.ncbi.nlm.nih.gov/pmc/articles/PMC3668100/\u003e\n\nPara os dados categóricos no formato de string, o próprio dado faltante \"missingvalue\" será considerado como uma categoria dos atributos.\n\nPara a parte dos dados de teste, utilizou-se o zero como indicação de dado ausente, visando não modificar os dados reais de teste.\n\n\n```python\ndf.select_dtypes(['float64']).columns\ndf.iloc[0:133964][\"lat\"][df[\"lat\"] \u003e -22] = stat.median(df.iloc[0:133964][\"lat\"][df.iloc[0:133964][\"lat\"] \u003c -22])\ndf.iloc[0:133964][\"lon\"][df[\"lon\"] \u003e -22] = stat.median(df.iloc[0:133964][\"lon\"][df.iloc[0:133964][\"lon\"] \u003c -22])\ndf.iloc[0:133964][\"bathrooms\"][df[\"bathrooms\"] == -1] = stat.median(df.iloc[0:133964][\"bathrooms\"][df.iloc[0:133964][\"bathrooms\"] != -1])\ndf.iloc[0:133964][\"bedrooms\"][df[\"bedrooms\"] == -1] = stat.median(df.iloc[0:133964][\"bedrooms\"][df.iloc[0:133964][\"bedrooms\"] != -1])\ndf.iloc[0:133964][\"parkingSpaces\"][df[\"parkingSpaces\"] == -1] = stat.median(df.iloc[0:133964][\"parkingSpaces\"][df.iloc[0:133964][\"parkingSpaces\"] != -1])\ndf.iloc[0:133964][\"suites\"][df[\"suites\"] == -1] = stat.median(df.iloc[0:133964][\"suites\"][df.iloc[0:133964][\"suites\"] != -1])\ndf.iloc[0:133964][\"totalAreas\"][df[\"totalAreas\"] == -1] = stat.median(df.iloc[0:133964][\"totalAreas\"][df.iloc[0:133964][\"totalAreas\"] != -1])\ndf.iloc[0:133964][\"totalAreas\"][df[\"totalAreas\"] == 0] = stat.median(df.iloc[0:133964][\"totalAreas\"])\ndf.iloc[0:133964][\"usableAreas\"][df[\"usableAreas\"] == -1] = stat.median(df.iloc[0:133964][\"usableAreas\"][df.iloc[0:133964][\"usableAreas\"] != -1])\ndf.iloc[0:133964][\"yearlyIptu\"][df[\"yearlyIptu\"] == -1] = stat.median(df.iloc[0:133964][\"yearlyIptu\"][df.iloc[0:133964][\"yearlyIptu\"] != -1])\n\ndf.iloc[133964:135001][\"bathrooms\"][df[\"bathrooms\"] == -1] = 0\ndf.iloc[133964:135001][\"bedrooms\"][df[\"bedrooms\"] == -1] = 0\ndf.iloc[133964:135001][\"parkingSpaces\"][df[\"parkingSpaces\"] == -1] = 0\ndf.iloc[133964:135001][\"suites\"][df[\"suites\"] == -1] = 0\ndf.iloc[133964:135001][\"totalAreas\"][df[\"totalAreas\"] == -1] = 0\ndf.iloc[133964:135001][\"usableAreas\"][df[\"usableAreas\"] == -1] = 0\ndf.iloc[133964:135001][\"yearlyIptu\"][df[\"yearlyIptu\"] == -1] = 0\n```\n\nAgora, para as variáveis **monthlyCondoFee** e **rentalTotalPrice**, foi decidido atribuir valor 0 para dados ausentes, já que são atributos específicos de determinadas classes de imóveis e modalidade de locação. Não é a melhor alternativa, mas por restrições de tempo optou-se por tal alternativa (obviamente, isso terá impacto no modelo final).\n\n\n```python\n#df.groupby('unitTypes', as_index=False)['bathrooms'].median()\ndf[\"monthlyCondoFee\"][df[\"monthlyCondoFee\"] == -1] = 0\ndf[\"rentalTotalPrice\"][df[\"rentalTotalPrice\"] == -1] = 0\n```\n\nAgora os dados são checados novamente após as correções com o **profile_report**.\n\n\n```python\ndf.iloc[0:133964].profile_report(style={'full_width':True})\n```\n\n# Remoção de outliers (do conjunto treinamento apenas)\n\nSerá adotada tal medida para não só facilitar a visualização de dados, mas também reduzir a variância nos dados por amostras em pequeno número. Apesar da maioria dos dados seguirem uma distribuição assimétrica, optamos por esta opção pela simplicidade e tempo restante para desenvolver o restante deste trabalho. Os dados de teste são mantidos em uma variável separada **df_test**.\n\n\n```python\ndf_train = df.iloc[0:133964]\ndf_test = df.iloc[133964:150001]\nQ1 = df_train.quantile(0.25)\nQ3 = df_train.quantile(0.75)\nIQR = Q3 - Q1\ndf_sem_outlier = df_train[~((df_train \u003c (Q1 - 1.5 * IQR)) |(df_train \u003e (Q3 + 1.5 * IQR))).any(axis=1)]\n```\n\n# Visualização dos dados - relação com o preço\n\nA primeira tentativa foi um gráfico de coordenadas paralelas com atributos numéricos normalizados. \nFoi possível identificar alguns padrões de forma clara, como:\nImóveis com mais banheiros tem tendência de um preço maior;\nImóveis com mais quartos tem tendência de um preço maior;\nImóveis com mais área total e utilizável tem tendência de um preço maior;\nImóveis com mais espaços de estacionamento não aparentam ter relação com o preço;\n\n\n```python\ndf_clone = df_sem_outlier[['price','bathrooms','bedrooms','parkingSpaces','totalAreas','usableAreas']]\n```\n\n```python\ndata = [\n    go.Parcoords(\n        line = dict(color = df_clone['price'], colorscale = [[0,'#cc0000'],[0.5,'#ffff66'],[1,'#00cc00']]),\n        dimensions = list([\n            dict(range = [0,6], label = 'bathrooms', values = df_clone['bathrooms']),\n            dict(range = [0,4], label = 'bedrooms', values = df_clone['bedrooms']),\n            dict(range = [0,6], label = 'parkingSpaces', values = df_clone['parkingSpaces']),\n            dict(range = [0,280], label = 'totalAreas', values = df_clone['totalAreas']),\n            dict(range = [0,410], label = 'usableAreas', values = df_clone['usableAreas']),\n            dict(range = [0,1487500], label = 'price', values = df_clone['price'])\n        ])\n    )\n]\n\nlayout = go.Layout(\n    plot_bgcolor = '#FFFFFF',\n    paper_bgcolor = '#FFFFFF'\n)\n\nfig = go.Figure(data = data, layout = layout)\npoff.iplot(fig, filename = 'parcoords-basic')\n```\n\nComo estão dispostas as casas de acordo com a localidade geográfica?\n\n```python\ncmap = sns.cubehelix_palette(as_cmap=True)\nf, ax = plt.subplots(figsize=(15,15))\npoints = ax.scatter(df_sem_outlier.lat, df_sem_outlier.lon, c=df_sem_outlier.price, s=50, cmap=cmap)\nf.colorbar(points)\n```\n\n![png](images/output_47_1.png)\n\n```python\nkmeans = KMeans(n_clusters=10)\ncluster = pd.DataFrame(pd.concat([df_sem_outlier.lat,df_sem_outlier.lon],axis = 1))\nkmeans.fit(cluster)\ny_km = kmeans.fit_predict(cluster)\n```\n\n\n```python\nplt.figure(figsize=(10,10))\nplt.scatter(cluster[y_km ==0].lat, cluster[y_km == 0].lon, s=100, c='red')\nplt.scatter(cluster[y_km ==1].lat, cluster[y_km == 1].lon, s=100, c='black')\nplt.scatter(cluster[y_km ==2].lat, cluster[y_km == 2].lon, s=100, c='blue')\nplt.scatter(cluster[y_km ==3].lat, cluster[y_km == 3].lon, s=100, c='cyan')\nplt.scatter(cluster[y_km ==4].lat, cluster[y_km == 4].lon, s=100, c='yellow')\nplt.scatter(cluster[y_km ==5].lat, cluster[y_km == 5].lon, s=100, c='green')\nplt.scatter(cluster[y_km ==6].lat, cluster[y_km == 6].lon, s=100, c='royalblue')\nplt.scatter(cluster[y_km ==7].lat, cluster[y_km == 7].lon, s=100, c='darkorange')\nplt.scatter(cluster[y_km ==8].lat, cluster[y_km == 8].lon, s=100, c='greenyellow')\nplt.scatter(cluster[y_km ==9].lat, cluster[y_km == 9].lon, s=100, c='turquoise')\n```\n\n![png](images/output_48_1.png)\n\nComo as variáveis numéricas estão correlacionadas?\n\n\n```python\ng = sns.PairGrid(df_sem_outlier, vars=['bathrooms', 'bedrooms', 'parkingSpaces', 'totalAreas', 'usableAreas', 'price'], palette='RdBu_r')\ng.map(plt.scatter, alpha=0.8)\ng.add_legend();\n```\n![png](images/output_50_1.png)\n\nComo o tipo de imóvel se relaciona com o preço?\n\n\n```python\nsns.catplot(x=\"price\",y=\"unitTypes\",data=df_sem_outlier,  height = 15)\n```\n\n![png](images/output_52_1.png)\n\n```python\nsns.catplot(x=\"price\", y=\"unitTypes\", kind=\"box\", col=\"publicationType\", data=df_sem_outlier, height = 15);\n```\n\n![png](images/output_53_0.png)\n\n```python\nsns.catplot(x=\"price\", y=\"unitTypes\", kind=\"violin\", col=\"publicationType\", data=df_sem_outlier, height = 15);\n```\n\n![png](images/output_54_0.png)\n\n```python\ndf_sem_outlier.iplot(\n    x='totalAreas',\n    y='price',\n    # Specify the category\n    categories='unitTypes',\n    xTitle='Preço',\n    yTitle='Área Total',\n    title='Preço x Área total comparados por tipo de imóvel')\n```\n\n# Remoção de atributos\n\nAgora, serão removidas colunas que não deverão agregar valor informativo ao modelo preditivo que será desenvolvido com o comando **drop**. Além disso, utilizar tais atributos tornam a criação do modelo mais onerosa. Esta análise é subjetiva e depende da interpretação do cientista/engenheiro/desenvolvedor. As colunas selecionadas para remoção são: **\"createdAt\"**, **\"images\"**, **\"title\"**,**\"updatedAt\"**, **\"precision\"** e **\"description\"**. OBS: aqui assume-se que o que exerce influência no preço do imóvel não é o título ou a descrição do anúncio, e sim as características íntrinsecas do imóvel. Porém, em um caso real, a descrição e título podem ser analisados separadamente para verificar quais descrições ou títutos de anúncios exercem maior influência nos preços dos imóveis, e assim, direcionar ações de marketing  mais efetivas para o mercado de imóveis. Além das colunas mencionadas, também serão removidas as colunas **\"district\"**, **zone** e **\"unitNumber\"** pela quantidade de registros faltantes (maior que 85%). Por fim, serão removidas as colunas **city**, **country**, **state** e **listingStatus** por não terem variância nos dados. **Latitude** e **Longitude** são correlacionadas entre si naturalmente devido à localização geográfica, porem, não serão incluídas nesta análise (é possível posteriormente realizar, por exemplo, uma clusterização destes atributos e substituir estes dois atributos por um único, separados em diversos grupos de localidades). A identificação de cada anuncio também não agrega informação relevante ao modelo preditivo. **locationId** também será removido por ser redundante com outras informações de localidade. Detalhe: dados de treino e teste novamente concatenados, sendo das linhas 0 - 68493 os dados de treinamento.\n\n\n```python\ndf_sem_outlier = pd.concat([df_sem_outlier, df_test], axis=0, ignore_index = True) \ndf_sem_outlier = df_sem_outlier.drop(\"createdAt\", axis=1).drop(\"images\", axis=1).drop(\"title\", axis=1).drop(\"updatedAt\", axis=1).drop(\"description\", axis=1).drop(\"district\", axis=1).drop(\"zone\", axis=1).drop(\"unitNumber\", axis=1).drop(\"city\", axis=1).drop(\"country\", axis=1).drop(\"state\", axis=1).drop(\"listingStatus\", axis=1).drop(\"precision\", axis=1).drop(\"id\", axis=1).drop(\"lat\", axis=1).drop(\"lon\", axis=1).drop(\"locationId\", axis=1)\n```\n\nAgora a coluna **\"owner\"** é transformada para o tipo numérico. \n\n\n```python\ndf_sem_outlier[\"owner\"] = df_sem_outlier[\"owner\"]*1\n```\n\n# Dados categóricos do tipo string\n\nAgora é realizada uma transformação das colunas que são do tipo object (textos) para o tipo numérico, de modo que seja possível utilizá-las nos modelos de aprendizado de máquina. Primeiro é realizada a conversão das colunas do tipo **object** para **category**.\n\n\n```python\ndf_sem_outlier[df_sem_outlier.select_dtypes(['object']).columns] = df_sem_outlier.select_dtypes(['object']).apply(lambda x: x.astype('category'))\n```\n\nRealiza-se uma cópia do dataframe e agora trabalha-se com essa cópia, para não modificar o original.\n\n\n```python\ndf_copy = df_sem_outlier.copy()\n```\n\nAgora vamos analisar cada coluna categórica do dataframe cópia. Será utilizado o método **Binary Encoding**. Para realizar tais verificações foi utilizada a biblioteca **pandas-profiling** anteriormente. \nColunas com **Binary Encoding**: businessType, publicationType, neighborhood, publisherId, street, streetNumber, period, locationId, unitTypes e zipCode.\n\n\n```python\nce_bin = ce.BinaryEncoder(cols = ['businessType','publicationType','neighborhood','publisherId','street','streetNumber','period','unitTypes','zipCode'])\ndf_copy = ce_bin.fit_transform(df_copy)\n```\n\n# Criação dos modelos: Treinamento e teste\n\nAgora serão realizados os testes com os algoritmos de regressão. Primeiramente será realizada uma divisão entre base de dados de treinamento e de teste. Será utilizada a divisão 75% treinamento e 25% para teste (com os registros da parte do treinamento), com a função **train_test_split**. Também separados o conjunto final dos dados de teste.\n\n\n```python\nvariables = df_copy.drop(['price'], axis=1)\nX_data = variables.iloc[0:68493]\nX_test_final = variables.iloc[68493:84529]\ny_result = pd.DataFrame(df_copy['price']).iloc[0:68493]\n```\n\n\n```python\nX_train, X_test, y_train, y_test = train_test_split(X_data, y_result, test_size = 0.25)\n```\n\nAqui é realizada uma transformação de normalização/padronização nas variáveis independentes de treinamento e teste. Alguns modelos de ML (Machine Learning) trabalham melhor com os valores transformados desta maneira, reduzindo variações entre colunas distintas do dataset.\n\n\n```python\nscaler = preprocessing.StandardScaler().fit(X_train)\nX_scaled_train = scaler.transform(X_train)\nX_scaled_test = scaler.transform(X_test)\nX_scaled_final_test = scaler.transform(X_test_final)\n```\n\nAgora convertemos as variáveis dependentes para o formato de array e as independentes para dataframe, visando manter o nome das colunas.\n\n\n```python\ny_train = np.ravel(np.array(y_train))\ny_test = np.ravel(np.array(y_test))\nX_train_df = pd.DataFrame(X_scaled_train, columns = X_train.columns)\nX_test_df = pd.DataFrame(X_scaled_test, columns = X_test.columns)\nX_final_test_df = pd.DataFrame(X_scaled_final_test, columns = X_test_final.columns)\n```\n\n# Treinamento/Teste com LGBM (Light Gradient Boosting Machines)\n\nSempre que possível, antes de criar os modelos, serão estimados os hiperparâmetros.\n\n\n```python\nparameters={'learning_rate': 10.0**-np.arange(0,4),\n        'num_iterations' : np.arange(0,10),\n        'num_leaves': ['10','30','50','100'],\n        'max_depth': ['2','5','10'],\n        'min_data_in_leaf': ['300','1000','2000'],\n        'verbose': 1,\n        'random_state':42}\nlgb_grid = GridSearchCV(LGBMRegressor(early_stopping=True), parameters, n_jobs=-1)\n\nlgb_grid.fit(X_train_df,y_train)\n\nprint(\"Best score: %0.4f\" % lgb_grid.best_score_)\nprint(\"Using the following parameters:\")\nprint(lgb_grid.best_params_)\n```\n\n\n```python\nparams={'learning_rate': 0.1,\n        'num_leaves': 31,\n        'verbose': 1,\n        'random_state':42,\n        'bagging_fraction': 0.7,\n        'feature_fraction': 0.7\n       }\nlgb_ = lgb.LGBMRegressor(**params, n_estimators=10000)\nlgb_.fit(X_train_df, y_train)\nplt.scatter(y_test,lgb_.predict(X_test_df, num_iteration=lgb_.best_iteration_))\nplt.grid()\nplt.xlabel('Preço Real')\nplt.ylabel('Preço Predito')\nplt.title('Scatterplot: Preço Real Vs Preço Predito')\nplt.show()\nprint('Mean Squared Error :',mean_squared_error(y_test, lgb_.predict(X_test_df, num_iteration=lgb_.best_iteration_)))\nprint('Mean Absolute Error :',mean_absolute_error(y_test, lgb_.predict(X_test_df, num_iteration=lgb_.best_iteration_)))\nprint('R^2 :', lgb_.score(X_test_df, y_test))\n```\n\n\n![png](images/output_77_0.png)\n\n\n    Mean Squared Error : 10190447972.04698\n    Mean Absolute Error : 64349.79415162858\n    R^2 : 0.8479476269268016\n    \n\n\n```python\n# Plot feature importance\nfeature_importance = lgb_.feature_importances_\nfeature_importance = 100.0 * (feature_importance / feature_importance.max())\nsorted_idx = np.argsort(feature_importance)\nsorted_idx = sorted_idx[len(feature_importance) - 70:]\npos = np.arange(sorted_idx.shape[0]) + .5\nplt.figure(figsize=(12,8))\nplt.barh(pos, feature_importance[sorted_idx], align='center')\nplt.yticks(pos, pd.DataFrame(X_test_df).columns[sorted_idx])\nplt.xlabel('Relative Importance')\nplt.title('Variable Importance')\nplt.show()\n```\n\n\n![png](images/output_78_0.png)\n\n\n# Treinamento/Teste com SVR (Support Vector Regressor)\n\n\n```python\nclf = svm.SVR()\nclf.fit(X_train_df, y_train)\nplt.scatter(y_test,clf.predict(X_test_df))\nplt.grid()\nplt.xlabel('Preço Real')\nplt.ylabel('Preço Predito')\nplt.title('Scatterplot: Preço Real Vs Preço Predito')\nplt.show()\nprint('Mean Squared Error :',mean_squared_error(y_test, clf.predict(X_test_df)))\nprint('Mean Absolute Error :',mean_absolute_error(y_test, clf.predict(X_test_df)))\nprint('R^2 :', r2_score(y_test, clf.predict(X_test_df)))\n```\n\n    Z:\\Programas\\Anaconda\\lib\\site-packages\\sklearn\\svm\\base.py:193: FutureWarning:\n    \n    The default value of gamma will change from 'auto' to 'scale' in version 0.22 to account better for unscaled features. Set gamma explicitly to 'auto' or 'scale' to avoid this warning.\n    \n    \n\n# Treinamento/Teste com MLP (Multi-Layer Perceptron)\n\nInicialmente, vamos estimar os melhores hiperparâmetros para criar o modelo, \n\n\n```python\nparameters = {\n    'hidden_layer_sizes':np.arange(15, 20),\n    'activation': ['tanh', 'relu','identity','logistic'],\n    'solver': ['adam','lbfgs'],\n    'alpha': 10.0**-np.arange(1,7),\n    'learning_rate': ['constant','adaptive']\n}\n\nmlp_grid = GridSearchCV(MLPRegressor(early_stopping=True,batch_size=8192), parameters, n_jobs=-1)\n\nmlp_grid.fit(X_train_df,y_train)\n\nprint(\"Best score: %0.4f\" % mlp_grid.best_score_)\nprint(\"Using the following parameters:\")\nprint(mlp_grid.best_params_)\n```\n\n\n```python\nmlp = MLPRegressor(\n    hidden_layer_sizes=19, \n    activation=\"relu\", \n    solver=\"lbfgs\", \n    alpha=0.1, \n    batch_size=8192, \n    learning_rate=\"constant\", \n    verbose=1) \nmlp.fit(X_train_df, y_train)\nplt.scatter(y_test,mlp.predict(X_test_df))\nplt.grid()\nplt.xlabel('Preço Real')\nplt.ylabel('Preço Predito')\nplt.title('Scatterplot: Preço Real Vs Preço Predito')\nplt.show()\nprint('Mean Squared Error :',mean_squared_error(y_test, mlp.predict(X_test_df)))\nprint('Mean Absolute Error :',mean_absolute_error(y_test, mlp.predict(X_test_df)))\nprint('R^2 :', mlp.score(X_test_df, y_test))\n```\n\n\n![png](images/output_84_0.png)\n\n\n    Mean Squared Error : 15360529593.187231\n    Mean Absolute Error : 84344.46959746198\n    R^2 : 0.7708044844827318\n    \n\n# Treinamento/Teste com XGB (eXtreme Gradient Boosting)\n\n\n```python\nparams={'learning_rate': [0.1],\n        'max_depth': np.arange(10,15),\n        'metric': [\"rmse\"],\n        'num_leaves': np.arange(5,15),\n        'verbose': [1],\n        'colsample_bytree': [0.7],\n        'gamma': [0, 0.5, 1, 1.5, 2, 5],\n        'subsample': [0.6, 0.8, 1.0]\n       }\nxgb_grid = GridSearchCV(xgb.XGBRegressor(early_stopping=True), params, n_jobs=-1)\n\nxgb_grid.fit(X_train_df,y_train)\n\nprint(\"Best score: %0.4f\" % xgb_grid.best_score_)\nprint(\"Using the following parameters:\")\nprint(xgb_grid.best_params_)\n```\n\n\n```python\nxgb_ = xgb.XGBRegressor(\n        learning_rate =  0.1,\n        max_depth = 10,\n        metric = 'rmse',\n        num_leaves = 5,\n        subsample = 1,\n        gamma = 0,\n        colsample_bytree = 0.7,\n        random_state = 42,\n        bagging_fraction = 0.7,\n        feature_fraction = 0.7)\nxgb_.fit(X_train_df, y_train)\nplt.scatter(y_test,xgb_.predict(X_test_df))\nplt.grid()\nplt.xlabel('Preço Real')\nplt.ylabel('Preço Predito')\nplt.title('Scatterplot: Preço Real Vs Preço Predito')\nplt.show()\nprint('Mean Squared Error :',mean_squared_error(y_test, xgb_.predict(X_test_df)))\nprint('Mean Absolute Error :',mean_absolute_error(y_test, xgb_.predict(X_test_df)))\nprint('R^2 :', xgb_.score(X_test_df, y_test))\n```\n\n    [18:47:32] WARNING: src/objective/regression_obj.cu:152: reg:linear is now deprecated in favor of reg:squarederror.\n    \n\n\n![png](images/output_87_1.png)\n\n\n    Mean Squared Error : 11465090474.478434\n    Mean Absolute Error : 68871.29865817894\n    R^2 : 0.828928598730367\n    \n\n\n```python\npd.options.display.float_format = '{:,.2f}'.format\nresults = pd.DataFrame(lgb_.predict(X_final_test_df, num_iteration=lgb_.best_iteration_))\ndf_teste = pd.read_json(\"json/source-4-ds-test.json\", encoding = \"utf-8\", lines = True)\nresults = pd.concat([df_teste['id'], results], axis=1).rename(columns={0:'price'})\nresults.to_csv(r'C:\\Users\\Dell\\Desktop\\Untitled Folder\\csv\\Results.csv', index = False)\nresults.to_excel(\"csv\\Results.xlsx\", index = False)  \n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flubrum%2Fdesafio-zap","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flubrum%2Fdesafio-zap","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flubrum%2Fdesafio-zap/lists"}