Calculando o preço de apartamentos na cidade de São Paulo

O objetivo deste projeto é construir um modelo preditivo capaz de calcular o preço de um imóvel no bairro Vila Nova Conceição na cidade de São Paulo.

Para completar este projeto cada aluno deverá seguir as orientações que estão neste notebook e preencher as células vazias.

Aquisição, pré-processamento e análise descritiva


In [1]:
import pandas as pd
import io
import requests
url="https://media.githubusercontent.com/media/fbarth/ml-espm/master/data/20140917_imoveis_filtrados_final.csv_shaped.csv"
s=requests.get(url).content
df = pd.read_csv(io.StringIO(s.decode('utf-8')), sep=",")

In [2]:
df.head()


Out[2]:
bairro preco area suites dormitorios banheiros vagas
0 vila-nova-conceicao 490000.0 32.0 1.0 1.0 1.0 1.0
1 vila-nova-conceicao 3180000.0 157.0 2.0 2.0 2.0 2.0
2 vila-nova-conceicao 1900000.0 205.0 2.0 3.0 3.0 3.0
3 vila-nova-conceicao 3565000.0 193.0 3.0 3.0 3.0 3.0
4 vila-nova-conceicao 1605000.0 116.0 1.0 3.0 2.0 2.0

In [3]:
%matplotlib inline
import seaborn as sns
import matplotlib.pyplot as plt
plt.subplots(figsize=(10, 8))
cmap = sns.cubehelix_palette(rot=-.2, as_cmap=True)
ax = sns.scatterplot(x="area", y="preco",
                     hue="suites", size="vagas",
                     palette=cmap, sizes=(10, 200),
                     data=df)



In [4]:
df['bairro'].value_counts()


Out[4]:
vila-mariana           1714
paraiso                 809
vila-nova-conceicao     794
vila-sonia              314
perto_ibirapuera        129
Name: bairro, dtype: int64
  • Considere apenas os imóveis do bairo vila-nova-conceicao

In [5]:
# @hidden_cell
df = df[df['bairro'] == 'vila-nova-conceicao']
  • Depois de considerar apenas os imóveis do bairro vila-nova-conceicao o tamanho do dataset precisa ser exatamente igual a:

In [6]:
df.shape


Out[6]:
(794, 7)

In [7]:
%matplotlib inline
import seaborn as sns
import matplotlib.pyplot as plt
plt.subplots(figsize=(10, 8))
cmap = sns.cubehelix_palette(rot=-.2, as_cmap=True)
ax = sns.scatterplot(x="area", y="preco",
                     hue="suites", size="vagas",
                     palette=cmap, sizes=(10, 200),
                     data=df)



In [8]:
df.head()


Out[8]:
bairro preco area suites dormitorios banheiros vagas
0 vila-nova-conceicao 490000.0 32.0 1.0 1.0 1.0 1.0
1 vila-nova-conceicao 3180000.0 157.0 2.0 2.0 2.0 2.0
2 vila-nova-conceicao 1900000.0 205.0 2.0 3.0 3.0 3.0
3 vila-nova-conceicao 3565000.0 193.0 3.0 3.0 3.0 3.0
4 vila-nova-conceicao 1605000.0 116.0 1.0 3.0 2.0 2.0
  • A quantidade suites não pode ser maior que a quantidade de dormitórios e a quantidade de suites também não pode ser maior que a quantidade de banheiros. Exclua todos os exemplos que não satisfazem esta restrição. Estes exemplos provavelmente são erros de coleta de dados.

In [9]:
# @hidden_cell
df = df[df['suites'] <= df['dormitorios']]
df = df[df['suites'] <= df['banheiros']]
  • Depois deste filtro o dataset precisa ter o tamanho abaixo:

In [10]:
df.shape


Out[10]:
(772, 7)

In [11]:
%matplotlib inline
import seaborn as sns
import matplotlib.pyplot as plt
plt.subplots(figsize=(10, 8))
cmap = sns.cubehelix_palette(rot=-.2, as_cmap=True)
ax = sns.scatterplot(x="area", y="preco",
                     hue="suites", size="vagas",
                     palette=cmap, sizes=(10, 200),
                     data=df)


  • Visto que agora a variável bairro tem apenas um valor, você deve remover a mesma

In [12]:
# @hidden_cell
df = df.drop(columns=['bairro'])
  • Depois deste filtro o dataset precisa ter a seguinte estrutura:

In [13]:
df.head()


Out[13]:
preco area suites dormitorios banheiros vagas
0 490000.0 32.0 1.0 1.0 1.0 1.0
1 3180000.0 157.0 2.0 2.0 2.0 2.0
2 1900000.0 205.0 2.0 3.0 3.0 3.0
3 3565000.0 193.0 3.0 3.0 3.0 3.0
4 1605000.0 116.0 1.0 3.0 2.0 2.0
  • Qual é o valor mínimo e máximo dos preços dos imóveis?

In [14]:
# @hidden_cell
x = df['preco'].describe()
x


Out[14]:
count    7.720000e+02
mean     2.479498e+06
std      1.507490e+06
min      4.115000e+05
25%      1.300000e+06
50%      2.000000e+06
75%      3.550000e+06
max      8.000000e+06
Name: preco, dtype: float64

Divisão dos datasets


In [15]:
df.head()


Out[15]:
preco area suites dormitorios banheiros vagas
0 490000.0 32.0 1.0 1.0 1.0 1.0
1 3180000.0 157.0 2.0 2.0 2.0 2.0
2 1900000.0 205.0 2.0 3.0 3.0 3.0
3 3565000.0 193.0 3.0 3.0 3.0 3.0
4 1605000.0 116.0 1.0 3.0 2.0 2.0

In [16]:
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(df.iloc[:,1:12], df['preco'], test_size=0.2, random_state=4)

In [17]:
x_train.head()


Out[17]:
area suites dormitorios banheiros vagas
197 245.0 3.0 3.0 5.0 4.0
546 32.0 1.0 1.0 1.0 1.0
499 278.0 2.0 3.0 6.0 4.0
780 233.0 3.0 4.0 4.0 3.0
749 161.0 3.0 3.0 5.0 3.0

Criação e avaliação do modelo preditivo

Crie um modelo de regressão utilizando os algoritmos e transformações nos atributos que você considera mais adequados.

Valide o modelo desenvolvido considerando os datasets X_test e y_test. Espera-se que o erro médio absoluto seja inferior a duzentos mil reais (R$ 200.000,00).

Descreva nas células abaixo todas as etapas necessárias para o desenvolvimento e validação do modelo.

Regressão Linear sem engenharia de atributos


In [20]:
print(x_train.shape)
print(y_train.shape)
print(x_test.shape)


(617, 5)
(617,)
(155, 5)

In [21]:
from sklearn.linear_model import LinearRegression
model = LinearRegression().fit(x_train, y_train)

Validação do modelo


In [22]:
predicted = model.predict(x_test)

In [23]:
from sklearn.metrics import mean_squared_error, r2_score, mean_absolute_error
rmse = mean_squared_error(y_test, predicted)
r2 = r2_score(y_test, predicted)
mae = mean_absolute_error(y_test, predicted)
print('rmse = %', rmse)
print('r2 = %', r2)
print('mae = %', mae)


rmse = % 359876747022.1244
r2 = % 0.8386671604006455
mae = % 419616.52465791063

Random Forest sem modificação adicional de atributos e modificando apenas a quantidade de árvores


In [24]:
from sklearn.ensemble import RandomForestRegressor
results = pd.DataFrame(columns=['estimators','r2'])
for i in range(100, 2000, 100):
    clf = RandomForestRegressor(n_estimators=i, max_depth=None, random_state=4, oob_score=True)
    clf.fit(x_train, y_train)
    results = results.append({'estimators':i, 'r2': clf.oob_score_}, ignore_index=True)

In [25]:
results.plot(x='estimators', y='r2')


Out[25]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f9f48d72f28>

In [26]:
clf = RandomForestRegressor(n_estimators=400, max_depth=None, random_state=4, oob_score=True)
clf.fit(x_train, y_train)


Out[26]:
RandomForestRegressor(bootstrap=True, criterion='mse', max_depth=None,
           max_features='auto', max_leaf_nodes=None,
           min_impurity_decrease=0.0, min_impurity_split=None,
           min_samples_leaf=1, min_samples_split=2,
           min_weight_fraction_leaf=0.0, n_estimators=400, n_jobs=None,
           oob_score=True, random_state=4, verbose=0, warm_start=False)

In [27]:
print(clf.oob_score_)
print(clf.feature_importances_)
print(x_train.columns)


0.8805338634748813
[0.76958528 0.01501288 0.01893293 0.01746236 0.17900656]
Index(['area', 'suites', 'dormitorios', 'banheiros', 'vagas'], dtype='object')

Validação do modelo


In [28]:
predicted = clf.predict(x_test)

In [29]:
from sklearn.metrics import mean_squared_error, r2_score, mean_absolute_error
rmse = mean_squared_error(y_test, predicted)
r2 = r2_score(y_test, predicted)
mae = mean_absolute_error(y_test, predicted)
print('rmse = %', rmse)
print('r2 = %', r2)
print('mae = %', mae)


rmse = % 231840900499.54626
r2 = % 0.8960656638074931
mae = % 306780.1359702765

Regressão Linear com transformação polinomial


In [30]:
#Generate a new feature matrix consisting of all polynomial combinations 
#of the features with degree less than or equal to the specified degree. 
#For example, if an input sample is two dimensional and of the form [a, b], 
#the degree-2 polynomial features are [1, a, b, a^2, ab, b^2].

from sklearn.preprocessing import PolynomialFeatures
transformer = PolynomialFeatures(degree=2, include_bias=False)
transformer.fit(x_train)
train_ = transformer.transform(x_train)

In [31]:
print(x_train.shape)
print(train_.shape)


(617, 5)
(617, 20)

In [32]:
from sklearn.linear_model import LinearRegression
model = LinearRegression().fit(train_, y_train)

Validação do modelo


In [33]:
transformer.fit(x_test)
test_ = transformer.transform(x_test)
predicted = model.predict(test_)

In [34]:
from sklearn.metrics import mean_squared_error, r2_score, mean_absolute_error
rmse = mean_squared_error(y_test, predicted)
r2 = r2_score(y_test, predicted)
mae = mean_absolute_error(y_test, predicted)
print('rmse = %', rmse)
print('r2 = %', r2)
print('mae = %', mae)


rmse = % 286295461976.87317
r2 = % 0.8716536696873655
mae = % 380993.63389069453

Random Forest com transformacao polinomial


In [35]:
from sklearn.model_selection import GridSearchCV
param_grid = { 
    'n_estimators': [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000],
    'max_features': ['sqrt', 'log2', 3, 4, 5, 6],
    'max_depth' : [2, 10, 20, 100, None]
}
rfc=RandomForestRegressor(random_state=4)
CV_rfc = GridSearchCV(estimator=rfc, param_grid=param_grid, cv= 3, verbose=1, n_jobs=-1)
CV_rfc.fit(train_, y_train)
CV_rfc.best_params_


Fitting 3 folds for each of 300 candidates, totalling 900 fits
[Parallel(n_jobs=-1)]: Using backend LokyBackend with 2 concurrent workers.
[Parallel(n_jobs=-1)]: Done  46 tasks      | elapsed:   11.0s
[Parallel(n_jobs=-1)]: Done 196 tasks      | elapsed:   48.4s
[Parallel(n_jobs=-1)]: Done 446 tasks      | elapsed:  2.4min
[Parallel(n_jobs=-1)]: Done 796 tasks      | elapsed:  4.7min
[Parallel(n_jobs=-1)]: Done 900 out of 900 | elapsed:  5.5min finished
Out[35]:
{'max_depth': 20, 'max_features': 6, 'n_estimators': 100}

In [36]:
clf3 = RandomForestRegressor(n_estimators=100, max_features = 6, max_depth=20, random_state=4, oob_score=True)
clf3.fit(train_, y_train)


Out[36]:
RandomForestRegressor(bootstrap=True, criterion='mse', max_depth=20,
           max_features=6, max_leaf_nodes=None, min_impurity_decrease=0.0,
           min_impurity_split=None, min_samples_leaf=1,
           min_samples_split=2, min_weight_fraction_leaf=0.0,
           n_estimators=100, n_jobs=None, oob_score=True, random_state=4,
           verbose=0, warm_start=False)

In [37]:
print(clf3.oob_score_)


0.8725711092903806

Validação do modelo


In [38]:
predicted = clf3.predict(test_)
from sklearn.metrics import mean_squared_error, r2_score, mean_absolute_error
rmse = mean_squared_error(y_test, predicted)
r2 = r2_score(y_test, predicted)
mae = mean_absolute_error(y_test, predicted)
print('rmse = %', rmse)
print('r2 = %', r2)
print('mae = %', mae)


rmse = % 218977651215.03406
r2 = % 0.9018322618183869
mae = % 303975.838114764

Análise dos resultados


In [40]:
def diff(x, y):
    return (x-y)
    
resultados = pd.DataFrame({'real': y_test,'rf_grid_polinomial': predicted})
resultados['rf_grid_polinomial_dif'] = resultados.apply(lambda x: diff(x['real'], x['rf_grid_polinomial']), axis=1)
resultados = resultados.join(x_test)
resultados.head()


Out[40]:
real rf_grid_polinomial rf_grid_polinomial_dif area suites dormitorios banheiros vagas
69 8000000.0 7.292156e+06 707844.285714 287.0 4.0 4.0 5.0 5.0
651 3400000.0 4.127360e+06 -727360.000000 295.0 2.0 3.0 4.0 3.0
142 3700000.0 4.066640e+06 -366639.772727 265.0 3.0 3.0 5.0 3.0
221 1915000.0 1.920860e+06 -5860.000000 160.0 1.0 4.0 2.0 2.0
65 4250000.0 4.247762e+06 2238.095238 222.0 3.0 3.0 5.0 4.0

In [41]:
ax = resultados.plot(x='area', y='rf_grid_polinomial_dif', kind='scatter')
ax.set_title('Análise dos erros')


Out[41]:
Text(0.5, 1.0, 'Análise dos erros')