Machine Learning do Zero no Python

Recentemente, estive na Python Brasil 2019. Um evento muito legal, falei um pouco sobre ele no Twitter do EstatSite (veja a thread aqui), e, inclusive, estou devendo um post aqui no blog. Acabei participando de um tutorial chamado “Machine Learning do Zero”, dado pelo Tarsis Azevedo – o cara é fera, recomendo que acompanhem o trabalho dele pelo Twitter ou Github, @tarsisazevedo. Aqui, vai o código feito em sala de aula, com algumas alterações e comentários que inclui porque achei relevante – outras porque achei que poderia facilitar para os mais novos

Atualização: Vocês agora podem visitar o post Regressão Linear no Python para aprender mais sobre regressão linear utilizando Python!

Acredito que o código está suficientemente autoexplicativo, então as explicações deste post não serão tão extensas quanto tenho o costume de fazer – por exemplo, não incluirei todas as saídas. Inclusive, recomendo que rode o código por partes, incluindo cada trecho em uma célula diferente do Jupyter Notebook – se você nunca utilizou o Jupyter, visite o post Uma Brevíssima Introdução ao Anaconda e o Jupyter Notebook.

Aqui, você vai aprender a localizar linhas e colunas com valores nulos/missing, excluir linhas ou colunas, obter estatísticas descritivas, fazer análise exploratória (gráfico de dispersão, histograma, mapa de calor), dentre outras coisas importantes no dia a dia de um cientista de dados.

# CARREGA BIBLIOTECA
import pandas
import numpy
import jupyter
import seaborn
import pytest
import sklearn

from matplotlib import pyplot

# CARREGA DATASET
# Primeiro, cria nomes pra usa-los nas colunas qdo carregar o dataset
columns = ['crim',
'zn',
'indus',
'chas',
'nox',
'rm',
'age',
'dis',
'rad',
'tax',
'ptratic',
'b',
'lstat',
'medv']

# importa dataset utilizando a funcao read_csv do PANDAS
boston_df = pandas.read_csv('https://raw.githubusercontent.com/tarsisazevedo/pybr-tutorial/master/housing-unclean.csv', names=columns)

# Alternativa: boston = pd.read_csv("housing-unclean.csv", names="crim zn undus chas nox rm age dis rad tax ptratio b lstat mdev".split())

# verifica as primeiras linhas
boston_df.head()

# Se quiser entender mais sobre alguma funcao, use o interrogacao
pandas.read_csv?

# Ou digite pandas.read_csv e aperte shift+tab

# Tipo de cada variavel
boston_df.dtypes

# Num de linhas e colunas
boston_df.shape

# Verifica se a linha toda esta duplicada
boston_df.duplicated()

# Acessa a linha 605
boston_df.iloc[605]

# Calcula qtas linhas estao duplicadas. Essa soma funciona pq o booleano TRUE eh igual a 1
boston_df.duplicated().sum()

# retira linhas duplicadas
boston_df.drop_duplicates()

# Repare: O jupyter nao alterou o nosso dataset com o comando anterior
boston_df.shape

Conforme dito nos comentários, o Jupyter não está alterando nossa base quando digitamos alguma função do tipo boston_df.drop_duplicates(). Ele apenas nos mostra como ficaria sem as duplicidades, mas não aplica a função na base existente. Para que isso seja feito, você deve sempre incluir o argumento inplace como sendo igual a True. Caso você não queira alterar a base original – eu, particularmente, não gosto disso e prefiro criar várias versões para acompanhar as alterações – você pode criar um dataframe novo aplicando a função conforme comentário no trecho abaixo:

# agora sim, vai alterar usando inplace
boston_df.drop_duplicates(inplace=True)

# se quisesse criar uma nova versao:
# boston_df_v2 = boston_df.drop_duplicates()
# conta quantos nulos tem em cada coluna
boston_df.isnull().sum()

Note que temos alguns valores nulos em nosso conjunto de dados. Algumas das possibilidades para lidar com campos missing:

  • Preencher o missing com a média ou mediana da coluna;
  • Utilizar algum valor que sirva como uma nova classe;
  • Utilizar valores preditos com base em algum modelo.

Dito isto, no nosso caso, os valores foram inseridos pelo Tarsis somente para fins didáticos. Podemos excluí-los sem perda de informação relevante:

# No nosso caso, podemos soh excluir os nulos
boston_df.dropna(inplace=True)

# verificamos nossa base
boston_df.shape

Agora sim, nosso conjunto de dados foi alterado. Feitas essas pequenas manipulações, você vai querer observar as estatísticas descritivas dessa base. Para isso, aplicamos a função describe():

# analise descritiva
pandas.set_option('display.width',100)
pandas.set_option('precision', 3)
boston_df.describe() #aqui, variaveis categoricas estao sendo tratadas como numericas (tenha em mente que eh errado)

Temos então o número de indivíduos (count) para cada variável, a média, o desvio padrão, o valor mínimo, os percentis mais utilizados e o máximo. Caso desconheça alguma dessas variáveis, visite os posts Percentil – Conceito e Código SAS e Estatística Descritiva

E, agora, partimos para as visualizações, a famosa análise exploratória:

# boxplot da coluna crim
%matplotlib inline
boston_df.crim.plot("box")

# distribuicao das variaveis
plot_index=1
max_plots=4
for column in boston_df.columns:
axes = pyplot.subplot(4, max_plots, plot_index)
axes.set_title('Distribution of '+ column)
axes.figure.set_figheight(15)
axes.figure.set_figwidth(15)
axes.figure.set_tight_layout(False)
pyplot.hist(boston_df[column],bins=20)
plot_index=plot_index+1

# a feature que vamos prever
%matplotlib inline
pyplot.figure(figsize=(20, 5))
seaborn.distplot(boston_df['medv'], bins=30)

# boxplot 'tombado'
pyplot.figure(figsize=(20, 5))
seaborn.boxplot(boston_df['medv'])

# Grafico de dispersao das variaveis vs medv
plot_index=1
max_plots=4
for column in boston_df.columns:
if column != "medv":
axes = pyplot.subplot(4, max_plots, plot_index)
axes.set_title('Distribution of '+ column + " v/s medv")
axes.figure.set_figheight(15)
axes.figure.set_figwidth(15)
axes.figure.set_tight_layout(False)
pyplot.scatter(boston_df[column], boston_df["medv"], marker='o')
plot_index=plot_index+1

# Heatmap
pyplot.figure(figsize=(20, 20))
correlation_matrix = boston_df.corr().round(2)
# annot = True to print the values inside the square
seaborn.heatmap(data=correlation_matrix, annot=True)

Agora, um breve parênteses a respeito da função reshape que usaremos mais para frente. Reshape serve para alterar as dimensoes do vetor. Quando temos reshape(2,3) queremos que o vetor seja reorganizado com duas linhas e tres colunas. Se tivermos um vetor com 5 elementos, reshape(2,3) nao vai funcionar, pois ficaria um espaco sobrando (2*3 = 6). Precisamos que o reshape seja exato. Ao utilizarmos -1 como argumento do reshape, entao o python vai determinar qual a quantidade de linhas ou colunas que deve utilizar. Por exemplo, se tivermos um vetor com 6 elementos e utilizarmos o reshape(-1,2), entao o python sabe que voce quer duas colunas, e que o numero de linhas deve ser de acordo com o que “couber” – no caso, serao 3. Se voce tiver 5 elementos e utilizar reshape(3,-1) entao o python tentara encontrar o numero de colunas necessarias para encaixar 5 elementos. Como isso nao eh possivel, ja que precisamos ter o numero exato de posicoes no reshape, ele retornara um erro.
Veja os exemplos abaixo:

print("Ex. 1: numpy.arange(6)")
print(numpy.arange(6))
print("")

print("Ex. 2: numpy.arange(6).reshape(2,3)")
print(numpy.arange(6).reshape(2,3))
print("")

print("Ex. 3: numpy.arange(6).reshape(-1,1)")
print(numpy.arange(6).reshape(-1,1))
print("")

print("Ex. 4: numpy.arange(6).reshape(2,-1)")
print(numpy.arange(6).reshape(2,-1))
print("")

print("Ex. 5: numpy.arange(5).reshape(3,-1)")
print(numpy.arange(5).reshape(3,-1))
print("")

Agora que você entendeu o reshape(), voltamos ao tutorial rodando uma regressão linear:

from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression

# cria um vetor com uma coluna da variavel rm (sera a variavel independente)

rm = numpy.array(boston_df["rm"]).reshape(-1,1)

# cria um vetor com uma coluna da variavel medv (sera a variavel dependente)

medv = numpy.array(boston_df["medv"]).reshape(-1,1)

# separa em treino e teste

X_train, X_test, y_train, y_test = train_test_split(rm, medv, test_size=0.2, random_state=42)

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

print('linear model coeff (w): {}'.format(linreg.coef_))

print(list(map('linear model intercept (b): {:.3f}'.format,linreg.intercept_))) # tive que mudar por erro na formatacao

print('R-squared score (training): {:.3f}'.format(linreg.score(X_train, y_train)))

print('R-squared score (test): {:.3f}'.format(linreg.score(X_test, y_test)))

Note que para apresentar o valor do intercepto tivemos que fazer uma mudança na função. Isso porque há uma diferença na formatação do valor que impediu o uso do script tradicional. Via de regra, utilizar print(‘linear model intercept (b): {:.3f}’.format(linreg.intercept_)) funciona!

Podemos traçar o gráfico dessa regressão:

import matplotlib.pyplot as plt
plt.figure(figsize=(5,5))
plt.scatter(X_train, y_train, marker= 'o', s=50, alpha=0.8)
plt.plot(X_train, linreg.coef_ * X_train + linreg.intercept_, 'r-')
plt.title('Least-squares linear regression')
plt.xlabel('Feature value (x)')
plt.ylabel('Target value (y)')
plt.show()

Como alternativa, você pode usar o statsmodels ao invés do sklearn:

import pandas as pd
import numpy as np
from sklearn import datasets, linear_model
from sklearn.linear_model import LinearRegression
import statsmodels.api as sm
from scipy import stats

X2 = sm.add_constant(X_train)
est = sm.OLS(y_train, X2)
est2 = est.fit()
print(est2.summary())

Por hoje, ficamos aqui. Esse tutorial, por mais breve que tenha sido, engloba muito do dia à dia de um cientista de dados, pode acreditar. Claro, há modelos mais robustos, há bases mais bagunçadas. No entanto, saiba que com esse conteúdo aqui você já pode avançar bem nos estudos e, claro, no trabalho. Ponha em prática esses conhecimentos!

Caso tenha dúvidas, me envie um e-mail, comente o post ou mande uma DM em alguma rede social. Caso queira ver o Jupyter Notebook (recomendadíssimo!), acesse https://github.com/yukioandre/Python/blob/master/ml-do-zero.ipynb. Recomendo também que acesse outros posts sobre tratamento de bases, como o Tutorial: Limpeza e Análise de Dados com Python na Prática.

Gostou do conteúdo? Se inscreva para receber as novidades! Deixe seu e-mail em INSCREVA-SE na barra à direita, logo abaixo de pesquisar. E, por favor, não deixe de comentar, dar seu feedback e compartilhar com seus amigos. De verdade, isso faz toda a diferença. Você também pode acompanhar mais do meu trabalho seguindo a conta de Twitter @EstatSite ou por alguma das redes que você encontra em Sobre o Estatsite / Contato, como meu canal de Youtube Canal do Yukio.

Bons estudos!

3 comentários em “Machine Learning do Zero no Python”

  1. Sou iniciante em análise de dados em jupyter notebook, Estou gostando do seus post’s, Continue postando o que você acha que os iniciantes de análise devem saber sobre esse universo dos dados, Deus Nos Dê inteligência.

    1. Que bom que esta gostando Julio. Nao sei se vc tem Twitter, mas la rola bastante troca de ideia no @EstatSite tambem. Em breve vou postar outros modelos de Python aqui tambem. Bons estudos e pode chamar para o que precisar!

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *