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)
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)
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:
# 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)
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
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
# 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()
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)
# 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
# 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?
Feito isso vamos exportar o arquivo do teste:
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
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,
Para acessar outras publicações de Ciência de Dados, clique aqui!
Expert em conteúdos da Hashtag Treinamentos. Auxilia na criação de conteúdos de variados temas voltados para aqueles que acompanham nossos canais.