Blog

Postado em em 5 de junho de 2023

House Prices do Kaggle – Projeto real para seu portfólio de Ciência de Dados

Você já possui uma análise de dados no seu portfólio do Github? Que tal construirmos juntos o desafio da House Prices do Kaggle!

Caso prefira esse conteúdo no formato de vídeo-aula, assista ao vídeo abaixo ou acesse o nosso canal do YouTube!

Para receber por e-mail o(s) arquivo(s) utilizados na aula, preencha:

Fala Impressionadores! Na aula de hoje eu quero te mostrar mais um Projeto Real para Portfólio de Ciência de Dados!

Isso mesmo, é mais um projeto que você pode acompanhar o passo a passo e colocar no seu portfólio de projetos.

A ideia é que com esse projeto você seja capaz de mostrar os conhecimentos que tem e como fez para solucionar o problema.

Você vai notar que utilizamos diversos recursos que já vimos em outras aulas e você ainda pode ir além melhorar ainda mais seus resultados.

Mas a ideia é que você tenha um projeto real de ciência de dados para apresentar no seu portfólio de projetos.

Então mesmo que você não tenha uma experiência no mercado de trabalho de fato, você ainda consegue mostrar os conhecimentos que tem através desse tipo de projeto!

E com isso pode mostrar tudo o que sabe para os recrutadores e conseguir o seu primeiro emprego na área!

Este projeto é um desafio real do Kaggle, portanto estou partindo do pressuposto de que vocês já viram as primeiras aulas aqui no canal.

Caso não tenham visto vou deixar aqui algumas aulas de introdução a Ciências de dados para você iniciar seus estudos.

House Prices do Kaggle – Advanced Regression Techniques

(Preços de casas – Técnicas Avançadas de Regressão)

House Prices do Kaggle
house prices

Vamos utilizar o dataset disponível no Kaggle. Este é um dataset de competição. Temos uma base com a descrição de cada uma das colunas (data_description.txt)

Este dataset possui dados reais, vamos importar essa base, fazer as precisões, postar no Kaggle e receber uma pontuação.

Vamos começar? – House Prices do Kaggle

# Importando o pandas

import pandas as pd

# Importando o dataset de treino

base = pd.read_csv('train.csv')

# Visualizando essa base

base.head(3)
visualização
visualização

Podemos ver que temos nesta base muitos valores nulos, valores zerados e valores em escalas muito diferentes.

Vamos verificar também o Shape da base.

# Retornando o shape da base

base.shape

(1460, 81)

Observe que a nossa base não tem um número tão grande de linhas, mas 81 é um número muito grande de colunas para esta base, pode ser que ocorra overfitting.

# E as informações

base.info()

<class 'pandas.core.frame.DataFrame'>

RangeIndex: 1460 entries, 0 to 1459

Data columns (total 81 columns):

 #   Column         Non-Null Count  Dtype 

---  ------         --------------  ----- 

 0   Id             1460 non-null   int64 

 1   MSSubClass     1460 non-null   int64 

 2   MSZoning       1460 non-null   object

 3   LotFrontage    1201 non-null   float64

 4   LotArea        1460 non-null   int64 

 5   Street         1460 non-null   object

 6   Alley          91 non-null     object

 7   LotShape       1460 non-null   object

 8   LandContour    1460 non-null   object

 9   Utilities      1460 non-null   object

 10  LotConfig      1460 non-null   object

 11  LandSlope      1460 non-null   object

 12  Neighborhood   1460 non-null   object

 13  Condition1     1460 non-null   object

 14  Condition2     1460 non-null   object

 15  BldgType       1460 non-null   object

 16  HouseStyle     1460 non-null   object

 17  OverallQual    1460 non-null   int64 

 18  OverallCond    1460 non-null   int64 

 19  YearBuilt      1460 non-null   int64 

 20  YearRemodAdd   1460 non-null   int64  

 21  RoofStyle      1460 non-null   object

 22  RoofMatl       1460 non-null   object

 23  Exterior1st    1460 non-null   object

 24  Exterior2nd    1460 non-null   object

 25  MasVnrType     1452 non-null   object

 26  MasVnrArea     1452 non-null   float64

 27  ExterQual      1460 non-null   object

 28  ExterCond      1460 non-null   object

 29  Foundation     1460 non-null   object

 30  BsmtQual       1423 non-null   object

 31  BsmtCond       1423 non-null   object

 32  BsmtExposure   1422 non-null   object

 33  BsmtFinType1   1423 non-null   object

 34  BsmtFinSF1     1460 non-null   int64 

 35  BsmtFinType2   1422 non-null   object

 36  BsmtFinSF2     1460 non-null   int64 

 37  BsmtUnfSF      1460 non-null   int64 

 38  TotalBsmtSF    1460 non-null   int64 

 39  Heating        1460 non-null   object

 40  HeatingQC      1460 non-null   object

 41  CentralAir     1460 non-null   object

 42  Electrical     1459 non-null   object

 43  1stFlrSF       1460 non-null   int64 

 44  2ndFlrSF       1460 non-null   int64 

 45  LowQualFinSF   1460 non-null   int64 

 46  GrLivArea      1460 non-null   int64 

 47  BsmtFullBath   1460 non-null   int64 

 48  BsmtHalfBath   1460 non-null   int64  

 49  FullBath       1460 non-null   int64 

 50  HalfBath       1460 non-null   int64 

 51  BedroomAbvGr   1460 non-null   int64 

 52  KitchenAbvGr   1460 non-null   int64 

 53  KitchenQual    1460 non-null   object

 54  TotRmsAbvGrd   1460 non-null   int64 

 55  Functional     1460 non-null   object

 56  Fireplaces     1460 non-null   int64 

 57  FireplaceQu    770 non-null    object

 58  GarageType     1379 non-null   object

 59  GarageYrBlt    1379 non-null   float64

 60  GarageFinish   1379 non-null   object

 61  GarageCars     1460 non-null   int64 

 62  GarageArea     1460 non-null   int64 

 63  GarageQual     1379 non-null   object

 64  GarageCond     1379 non-null   object

 65  PavedDrive     1460 non-null   object

 66  WoodDeckSF     1460 non-null   int64 

 67  OpenPorchSF    1460 non-null   int64 

 68  EnclosedPorch  1460 non-null   int64 

 69  3SsnPorch      1460 non-null   int64 

 70  ScreenPorch    1460 non-null   int64 

 71  PoolArea       1460 non-null   int64 

 72  PoolQC         7 non-null      object

 73  Fence          281 non-null    object

 74  MiscFeature    54 non-null     object

 75  MiscVal        1460 non-null   int64 

 76  MoSold         1460 non-null   int64 

 77  YrSold         1460 non-null   int64 

 78  SaleType       1460 non-null   object

 79  SaleCondition  1460 non-null   object

 80  SalePrice      1460 non-null   int64 

dtypes: float64(3), int64(35), object(43)

memory usage: 924.0+ KB

Como já importamos os dados e já vimos a quantidade de valores vazios vamos explorar mais essa questão.

Começando a explorar os dados

# Visualizando quantidade de valores vazios

(base.isnull().sum()/base.shape[0]).sort_values(ascending=False).head(20)

PoolQC          0.995205

MiscFeature     0.963014

Alley           0.937671

Fence           0.807534

FireplaceQu     0.472603

LotFrontage     0.177397

GarageYrBlt     0.055479

GarageCond      0.055479

GarageType      0.055479

GarageFinish    0.055479

GarageQual      0.055479

BsmtFinType2    0.026027

BsmtExposure    0.026027

BsmtQual        0.025342

BsmtCond        0.025342

BsmtFinType1    0.025342

MasVnrArea      0.005479

MasVnrType      0.005479

Electrical      0.000685

Id              0.000000

dtype: float64

Estamos verificando a quantidade de valores vazios por porcentagem em cada coluna, podemos definir aqui que uma coluna com mais de 10% de valores vazios não é interessante para o nosso modelo, por exemplo:

# Podemos eliminar as colunas com mais de 10% de valores vazios

eliminar = base.columns[(base.isnull().sum()/base.shape[0]) > 0.1]

eliminar

Index(['LotFrontage', 'Alley', 'FireplaceQu', 'PoolQC', 'Fence',

       'MiscFeature'],

      dtype='object')

# Eliminando essas colunas

base = base.drop(eliminar,axis=1)

Agora todas as colunas com mais de 10% de valores zerados foram eliminadas.

Ainda podemos fazer diversos tratamentos na base, mas vamos fazer alguns testes somente agora que retiramos valores vazios para ver se o erro já diminuiu significativamente ou não.

Após verificar qual o erro atual você pode traçar uma meta do resultado que você espera chegar após os demais tratamentos.

Queremos criar primeiro um modelo para verificar o quanto estamos errando e depois planejar como melhorar. Para isso:

  • Vamos eliminar as colunas de texto
  • Precisamos tratar os valores vazios
  • Vamos escolher alguns algoritmos para testar e um método de avaliação de erro
# Selecionando apenas as colunas numéricas

colunas = base.columns[base.dtypes != 'object']

colunas

Index(['Id', 'MSSubClass', 'LotArea', 'OverallQual', 'OverallCond',

       'YearBuilt', 'YearRemodAdd', 'MasVnrArea', 'BsmtFinSF1', 'BsmtFinSF2',

       'BsmtUnfSF', 'TotalBsmtSF', '1stFlrSF', '2ndFlrSF', 'LowQualFinSF',

       'GrLivArea', 'BsmtFullBath', 'BsmtHalfBath', 'FullBath', 'HalfBath',

       'BedroomAbvGr', 'KitchenAbvGr', 'TotRmsAbvGrd', 'Fireplaces',

       'GarageYrBlt', 'GarageCars', 'GarageArea', 'WoodDeckSF', 'OpenPorchSF',

       'EnclosedPorch', '3SsnPorch', 'ScreenPorch', 'PoolArea', 'MiscVal',

       'MoSold', 'YrSold', 'SalePrice'],

      dtype='object')

Conseguimos retirar as colunas que não eram numéricas e ficar com as colunas que podemos usar no modelo.

Então podemos dizer que a nossa base 2 é a base inicial, porem, filtrada com apenas essas colunas que acabamos de separar:

# E criar uma base com esses valores

base2 = base.loc[:,colunas]

base2.head(3)
visualização
visualização

Observe que não temos mais nenhuma coluna com texto, mas ainda temos diversas colunas zeradas e este modelo não trabalha com valores vazios.

Vamos então verificar novamente quais colunas possuem valores vazios utilizando o ascending=False como fizemos acima.

# Verificando os valores vazios

base2.isnull().sum().sort_values(ascending=False).head(3)

GarageYrBlt    81

MasVnrArea      8

Id              0

dtype: int64

Note que só temos duas colunas que possuem valores vazios. Podemos então substituir todos esses valores nulos por -1.

Neste caso não estamos criando um valor apenas se trata de uma boa prática para eliminar valores que já não existiam colocando-os como -1:

# Substituindo os valores vazios por -1

base2 = base2.fillna(-1)

Feito isso temos a nossa base inicial para começar!

Criando nosso modelo -> Vamos separar em treino e teste

https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html

# Selecionando X e y

X = base2.drop('SalePrice',axis=1)

y = base2.SalePrice

# Importando o train_test_split

from sklearn.model_selection import train_test_split

# Separando essa base em treino e teste

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42)

O próximo passo é selecionar os algoritmos que vamos utilizar. Não é preciso utilizar algoritmos robustos nessa faze, lembre-se de começar pelo simples e ir escalando a medida que for avançando em experiência e conhecimento, afinal todo esse processo será contado como experiência.

Podemos começar com os algoritmos mais simples como:

Regressão Linear

https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LinearRegression.html

Árvore de Regressão

https://scikit-learn.org/stable/modules/tree.html#regression

KNeighborsRegressor

https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighborsRegressor.html#sklearn.neighbors.KNeighborsRegressor

Mas, por que criar 3 modelos diferentes?

Estamos criando 3 modelos justamente para mostrar para o recrutador que vai ver seu portfólio que você sabe criar modelos e principalmente que você sabe fazer comparações entre eles!

# Importando a regressão linear

from sklearn.linear_model import LinearRegression

# Criando o regressor e fazendo o fit com os dados de treino

reg_rl = LinearRegression().fit(X_train, y_train)

# Fazendo a previsão pros dados de teste

y_rl = reg_rl.predict(X_test)

# Importando a árvore de regressão

from sklearn import tree

# Criando o regressor e fazendo o fit com os dados de treino

reg_ar = tree.DecisionTreeRegressor(random_state=42).fit(X_train, y_train)

# Fazendo a previsão

y_ar = reg_ar.predict(X_test)

# Importando o KNN

from sklearn.neighbors import KNeighborsRegressor

# Criando o regressor e fazendo o fit com os dados de treino

reg_knn = KNeighborsRegressor(n_neighbors=2).fit(X_train, y_train)

# Fazendo a previsão

y_knn = reg_knn.predict(X_test)

Para avaliar, vamos utilizar o erro médio absoluto e o erro médio quadrático

Erro médio absoluto

https://scikit-learn.org/stable/modules/generated/sklearn.metrics.mean_absolute_error.html

Erro quadrático médio

https://scikit-learn.org/stable/modules/generated/sklearn.metrics.mean_squared_error.html

# Importando o erro médio absoluto

from sklearn.metrics import mean_absolute_error

# E o erro quadrático médio

from sklearn.metrics import mean_squared_error

# Avaliando o erro da regressão

print(mean_absolute_error(y_test,y_rl))

print(mean_squared_error(y_test,y_rl))

23763.187393065116

1533982883.4448853

Agora temos nosso erro médio absoluto e quadrático.

É necessário converter esses valores ou fazer alguma transformação?

Não, não é obrigatório fazer esses ajustes, o principal ponto aqui é poder comparar os resultados.

# da árvore de decisão

print(mean_absolute_error(y_test,y_ar))

print(mean_squared_error(y_test,y_ar))

27580.78838174274

2530245114.701245

# e do knn

print(mean_absolute_error(y_test,y_knn))

print(mean_squared_error(y_test,y_knn))

33273.08298755187

2733937586.841286

Outra coisa importante que você pode pesquisar no kaggle é o erro que será utilizado para avaliar o seu resultado, neste caso será utilizado o erro quadrático.

Então se for para escolher é melhor considerar o resultado médio quadrático porque é com ele que o Kaggle vai avaliar o teste.

Podemos plotar visualmente a relação do y_test com as previsões feitas

Para isso vamos utilizar o matplotlib

https://matplotlib.org/

# Importando o matplotlib

import matplotlib.pyplot as plt

# Criando esse gráfico

fig, ax = plt.subplots(ncols=3,figsize=(15,5))

ax[0].scatter(y_test/100000,y_rl/100000)

ax[0].plot([0,700000],[0,700000],'--r')

ax[1].scatter(y_test/100000,y_ar/100000)

ax[1].plot([0,700000],[0,700000],'--r')

ax[2].scatter(y_test/100000,y_knn/100000)

ax[2].plot([0,700000],[0,700000],'--r')

ax[0].set(xlim=(0, 7),ylim=(0, 7))

ax[0].set_xlabel('Real')

ax[0].set_ylabel('Previsão')

ax[1].set(xlim=(0, 7),ylim=(0, 7))

ax[1].set_xlabel('Real')

ax[1].set_ylabel('Previsão')

ax[2].set(xlim=(0, 7),ylim=(0, 7))

ax[2].set_xlabel('Real')

ax[2].set_ylabel('Previsão')

plt.show()
House Prices do Kaggle
visualização gráfica

Vamos utilizar a Regressão Linear por ser o algoritmo com menor erro quadrático médio, a mesma métrica avaliada pelo Kaggle na hora de classificar os modelos.

Fazendo a previsão para a base de teste da competição

Agora vamos usar a base de teste da competição, ou seja, vamos basicamente repetir todo o processo que fizemos até agora, vamos visualizar a base, tratar os valores, visualizar graficamente e submeter o teste.

Obs: não podemos excluir linhas

# Importando a base de teste

teste = pd.read_csv('test.csv')

# Visualizando a base

teste.head(3)
visualização
visualização
# Eliminando as mesmas colunas da base de treino

teste = teste.drop(eliminar,axis=1)

# Verificando as colunas numéricas

colunas2 = teste.columns[teste.dtypes != 'object']

colunas2

Index(['Id', 'MSSubClass', 'LotArea', 'OverallQual', 'OverallCond',

       'YearBuilt', 'YearRemodAdd', 'MasVnrArea', 'BsmtFinSF1', 'BsmtFinSF2',

       'BsmtUnfSF', 'TotalBsmtSF', '1stFlrSF', '2ndFlrSF', 'LowQualFinSF',

       'GrLivArea', 'BsmtFullBath', 'BsmtHalfBath', 'FullBath', 'HalfBath',

       'BedroomAbvGr', 'KitchenAbvGr', 'TotRmsAbvGrd', 'Fireplaces',

       'GarageYrBlt', 'GarageCars', 'GarageArea', 'WoodDeckSF', 'OpenPorchSF',

       'EnclosedPorch', '3SsnPorch', 'ScreenPorch', 'PoolArea', 'MiscVal',

       'MoSold', 'YrSold'],

      dtype='object')

# Mantendo também apenas as colunas numéricas

teste = teste.loc[:,colunas2]

# Verificando a base restante

teste.info()

<class 'pandas.core.frame.DataFrame'>

RangeIndex: 1459 entries, 0 to 1458

Data columns (total 36 columns):

 #   Column         Non-Null Count  Dtype 

---  ------         --------------  ----- 

 0   Id             1459 non-null   int64 

 1   MSSubClass     1459 non-null   int64 

 2   LotArea        1459 non-null   int64 

 3   OverallQual    1459 non-null   int64 

 4   OverallCond    1459 non-null   int64 

 5   YearBuilt      1459 non-null   int64 

 6   YearRemodAdd   1459 non-null   int64 

 7   MasVnrArea     1444 non-null   float64

 8   BsmtFinSF1     1458 non-null   float64

 9   BsmtFinSF2     1458 non-null   float64

 10  BsmtUnfSF      1458 non-null   float64

 11  TotalBsmtSF    1458 non-null   float64

 12  1stFlrSF       1459 non-null   int64 

 13  2ndFlrSF       1459 non-null   int64 

 14  LowQualFinSF   1459 non-null   int64 

 15  GrLivArea      1459 non-null   int64 

 16  BsmtFullBath   1457 non-null   float64

 17  BsmtHalfBath   1457 non-null   float64

 18  FullBath       1459 non-null   int64 

 19  HalfBath       1459 non-null   int64 

 20  BedroomAbvGr   1459 non-null   int64 

 21  KitchenAbvGr   1459 non-null   int64 

 22  TotRmsAbvGrd   1459 non-null   int64 

 23  Fireplaces     1459 non-null   int64 

 24  GarageYrBlt    1381 non-null   float64

 25  GarageCars     1458 non-null   float64

 26  GarageArea     1458 non-null   float64

 27  WoodDeckSF     1459 non-null   int64 

 28  OpenPorchSF    1459 non-null   int64 

 29  EnclosedPorch  1459 non-null   int64 

 30  3SsnPorch      1459 non-null   int64 

 31  ScreenPorch    1459 non-null   int64 

 32  PoolArea       1459 non-null   int64 

 33  MiscVal        1459 non-null   int64 

 34  MoSold         1459 non-null   int64 

 35  YrSold         1459 non-null   int64 

dtypes: float64(10), int64(26)

memory usage: 410.5 KB

# Visualizando quantidade de valores vazios

teste.isnull().sum().sort_values(ascending=False).head(10)

GarageYrBlt     78

MasVnrArea      15

BsmtHalfBath     2

BsmtFullBath     2

BsmtUnfSF        1

GarageCars       1

GarageArea       1

BsmtFinSF1       1

BsmtFinSF2       1

TotalBsmtSF      1

dtype: int64

Vamos precisar retirar os valores vazios, pois a regressão linear não vai conseguir trabalhar com valores vazio

Podemos apenas substituir por -1 como fizemos acima

# Substituindo os valores vazios por -1

teste = teste.fillna(-1)

Agora podemos usar nosso modelo e ajustar os dados para usarmos no Kaggle

# Vamos usar a regressão linear para fazer a previsão

y_pred = reg_rl.predict(teste)

# Podemos adicionar essa coluna de previsão na nossa base

teste['SalePrice'] = y_pred

# E extrair somente o Id e o SalePrice

resultado = teste[['Id','SalePrice']]

resultado.head(3)

House Prices do Kaggle – Primeiro resultado

primeiro resultado
primeiro resultado
# Podemos então exportar essa base

resultado.to_csv('resultado.csv',index=False)

Agora podemos submeter nossa previsão no teste do Kaggle, clique em Submit to Competition, escreva um breve resumo sobre o seu projeto e arraste o arquivo para anexar no Kaggle.

Pronto, feito isso podemos submeter o teste e já verificar o resultado, neste teste o resultado chegou em 0,25, você pode ainda colocar uma meta sobre este valor e estudar formas de diminuir ainda mais o erro.

Não precisa estipular uma meta muito próximo a zero porque chegar nela será muito mais complexo ou pode ser que surja um algoritmo que possibilite mais rápido esse resultado, que outros participantes do desafio conheçam e você ainda não conheça.

Por este motivo, a ideia é ir testando seus conhecimentos e evoluindo a cada teste aos poucos sempre estipulando uma meta sobre SEU último resultado, isso vai trazer uma evolução mais clara e tangível.

Como colocar este projeto no seu GitHub?

  • O primeiro passo é criar um repositório
  • Nomeie este repositório, você pode chamar de HousePrices, por exemplo.
  • Coloque a descrição do projeto
  • Selecione para ficar público e adicione um Readme.

Feito isso vamos exportar o arquivo do teste:

  • Vamos exportar como Notebook (.ipynb)
  • Subir o arquivo no GitHub clicando em Add file -> Upload files.
  • Coloque o documento no espaço para adicionar arquivos e salve

Agora, tudo está disponível no seu portfólio.

Você também pode usar o Readme como uma ata onde você vai registrar toda a evolução do projeto e suas próximas metas, pode ser somente escrito ou com imagens.

Você pode tentar melhorar a limpeza dos seus dados

Limpeza de Dados em um Dataset Real – Dados do Titanic

Depois é possível fazer a engenharia de recursos

Feature Engineering em Python para Seus Projetos de Ciência de Dados

A padronização / normalização dos dados

Padronização e Normalização de Dados para Ciência de Dados

E até a seleção de variáveis

Seleção de Recursos (Feature Selection) em Projetos de Ciência de Dados

Conclusão – House Prices do Kaggle

Trouxe para esta aula um desafio real do Kaggle, o objetivo foi mostrar como fazer o desafio usando as bibliotecas, explorando datasets mais simples e fazer comparações entre resultados.

É importante que esse tipo de estudo seja realizado para que você tenha algo para mostrar no seu Github, este trabalho vai fazer justamente isso, mostrar suas habilidades de organização, uso das ferramentas e capacidade de comparação, além de te dar um desafio de sempre tentar melhorar o resultado.

Eu fico por aqui! Até a próxima,

Hashtag Treinamentos

Para acessar outras publicações de Ciência de Dados, clique aqui!


Quer aprender mais sobre Python com um minicurso básico gratuito?

Quer sair do zero no Python e virar uma referência na sua empresa? Inscreva-se agora mesmo no Python Impressionador