Logo Passei Direto
Buscar
Material
páginas com resultados encontrados.
páginas com resultados encontrados.
left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

Prévia do material em texto

MINERAÇÃO DE DADOS EM PYTHON
ICMC-USP
Victor Alexandre Padilha
André Carlos Ponce de Leon Ferreira de Carvalho
Instituto de Ciências Matemáticas e de Computação
Universidade de São Paulo
9 de agosto de 2017
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
Capítulo
1
Instalação do Python e bibliotecas
A linguagem de programação Python possui bibliotecas que ajudam a escrever
códigos voltados para aplicações em diferentes áreas de conhecimento. Uma dessas áreas
é a mineração de dados. Esta apostila tem por objetivo ilustrar como bibliotecas da lin-
guagem Python podem ser utilizadas para a realização de experimentos de mineração de
dados.
Todos os exemplos a serem apresentados neste material utilizarão a linguagem
de programação Python na versão 3 e algumas de suas principais bibliotecas desenvolvi-
das para dar suporte a análise de dados, aprendizado de máquina e mineração de dados.
Portanto, esta primeira parte apresenta um breve tutorial sobre como instalar essas ferra-
mentas em diferentes sistemas operacionais, Linux e Windows.
1.1. Instalação em Linux
Diversas distribuições atuais do sistema operacional Linux disponibilizam, como parte de
seu conjunto padrão de pacotes, um ambiente para programação na linguagem Python 3.
Para conferir, basta digitar os seguintes comandos no terminal:
$ which python3
e
$ which pip3
os quais devem retornar algo como /usr/bin/python3 e /usr/bin/pip3, respec-
tivamente. Caso algum erro seja informado, deverão ser instalados os pacotes apropriados
para que seja possível utilizar a linguagem. Para isso, será utilizado o gerenciador de pa-
cotes disponível na distribuição usada. Os dois gerenciadores mais comumente utilizados
são o apt-get (Debian, Ubuntu e derivados) e o yum (RedHat, CentOS e derivados). Por-
tanto, para a instalação em sistemas derivados deles, basta digitar algum dos seguintes
comandos no terminal:
$ sudo apt-get install python3 python3-dev python3-pip
ou
2
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
$ sudo yum install python3 python3-dev python3-pip
Após digitar esses comandos, toda vez que você quiser executar algum script,
basta digitar o comando python3 script.py.
Adicionalmente, nos exemplos apresentados neste material e para os trabalhos
aplicados distribuídos no decorrer da disciplina, serão necessárias quatro bibliotecas am-
plamente utilizadas para relizar experimentos de mineração e ciência de dados:
• NumPy1,
• SciPy2
• scikit-learn3
• matplotlib4
Para a instalação dessas bibliotecas, basta digitar no terminal o comando a seguir:
$ pip3 install numpy scipy sklearn matplotlib pandas --user
1.2. Instalação em Windows
Como primeiro passo, a versão mais atualizada e estável do Python 3 deve ser baixada
em https://www.python.org/downloads/windows/. Para a instalação do
gerenciador de pacotes pip, o script get-pip.py5 deve ser baixado e executado no termi-
nal do Windows como python3 get-pip.py. Finalmente, utilizaremos o seguinte
comando para a instalação das bibliotecas NumPy, SciPy, scikit-learn e matplotlib6:
$ pip3 install numpy scipy sklearn matplotlib pandas
1.3. Referências e tutoriais
Esta primeira parte deste material teve como objetivo descrever os passos necessários
para a instalação das ferramentas que serão necessárias no decorrer da disciplina de Mi-
neração de Dados Biológicos. Nos próximos capítulos, diversos exemplos de códigos
serão apresentados para a exploração de conjunto de dados, visualização de dados, pré-
processamento de bases de dados, construção de modelos preditivos, construção de mode-
los descritivos, além de outros temas que serão cobertos na disciplina. Não será necessário
qualquer conhecimento prévio acerca das bibliotecas citadas nas seções anteriores. Entre-
tanto, alguns bons tutoriais introdutórios estão disponíveis nos websites de cada biblioteca
para que o leitor possa tanto consultar suas funcionalidades como aprender a usar melhor
os seus recursos.
1http://www.numpy.org/
2http://www.scipy.org/
3http://http://scikit-learn.org/stable/
4http://matplotlib.org/
5https://bootstrap.pypa.io/get-pip.py
6Para o ambiente Windows, se os comandos python3 e pip3 não funcionarem, deve-se testar com os
comandos python e pip, respectivamente.
3
https://www.python.org/downloads/windows/
http://www.numpy.org/
http://www.scipy.org/
http://http://scikit-learn.org/stable/
http://matplotlib.org/
https://bootstrap.pypa.io/get-pip.py
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
Capítulo
2
Iniciando o estudo e exploração de dados
Esta parte do material vai ilustrar como a linguagem Python pode ser utilizada
para uma análise exploratória de um conjunto de dados, de forma a encontrar padrões
nos dados e extrair conhecimento desses dados. Existem duas maneiras de explorar um
conjunto de dados: por meio de técnicas estatísticas, mais especificamente da estatística
descritiva, e de técnicas de visualização.
Nesta parte do material, serão apresentadas algumas das principais medidas esta-
tísticas utilizadas para descrever um conjunto de dados, bem como suas funções corres-
pondentes nas bibliotecas da linguagem Python consideradas, além de técnicas simples
capazes de ilustrar graficamente padrões presentes em um conjunto de dados.
Inicialmente, na Seção 2.1, será apresentada uma breve introdução às operações
matemáticas básicas para a manipulação de dados com a biblioteca NumPy. Na Seção
2.2, será discutido como as bases de dados consideradas no presente material estão nor-
malmente estruturadas, e serão apresentadas algumas medidas estatísticas para análise
exploratória de dados. Na Seção 2.3, será discutida a ocorrência de dados multivariados,
que condizem com os cenários estudados em mineração de dados. Na Seção 2.4, serão
demonstrados alguns exemplos simples de gráficos que podem auxiliar em uma análise
inicial dos dados. Por fim, na Seção 2.5, serão propostos alguns exercicios com a finali-
dade de praticar os conceitos apresentados.
2.1. Introdução à NumPy
NumPy é um pacote da linguagem Python que foi desenvolvido para computação cientí-
fica, área que utiliza computação para resolver problemas complexos. Esse pacote, que
será muito utilizado para as análises e experimentos destas notas, possui várias funções
que permitem manipular e descrever dados. Este capítulo irá inicialmente mostrar como
funções desse pacote podem ser aplicadas a arrays, que consistem no tipo básico definido
pelo pacote.
2.1.1. Arrays
O principal tipo de dados disponibilizado pela biblioteca NumPy e que será mais utilizado
no decorrer deste material é conhecido como numpy.array. Um array pode ser ins-
tanciado por meio da chamada numpy.array(lista), na qual lista é um objeto
do tipo list contendo apenas valores numéricos. Por exemplo:
3
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
import numpy
lista = [1, 2, 3, 4, 5]
x = numpy.array(lista)
print(x)
O tipo array fornece facilidades para a realização de operações matemáticas com
escalares. Para fins de ilustração, execute o código abaixo no terminal do Python:
import numpy
x = numpy.array([1, 2, 3, 4, 5])
y = 2
print(x + y)
print(x - y)
print(y - x)
print(x * y)
print(x / y)
print(y / x)
Ao inspecionar os resultados do código anterior, é possível observar que nas operações
de soma, subtração, multiplicação e divisão em que um dos operandos é um escalar e o
outro é um array, cada operação ocorre entre o escalar e cada elemento do array.
A NumPy também possui funções para realizar operações utilizando dois arrays
de mesmo tamanho1. Como exemplo, execute o código abaixo no terminal do Python:
import numpy
x = numpy.array([1, 2, 3, 4, 5])
y = numpy.array([6, 7, 8, 9, 10])
print(x + y)
print(x - y)
print(y - x)
print(x * y)
1Na verdade, diversas operações podem ser feitas também com arrays de tamanhos e dimensões distin-
tas. Entretanto, o comportamento da NumPy em tais cenários pode não ser tão intuitivo. Para o leitor inte-
ressado nestetópico, recomenda-se a leitura de: https://scipy.github.io/old-wiki/pages/
EricsBroadcastingDoc.
4
https://scipy.github.io/old-wiki/pages/EricsBroadcastingDoc
https://scipy.github.io/old-wiki/pages/EricsBroadcastingDoc
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
print(x / y)
print(y / x)
Ao analisar as saídas do código anterior, pode-se perceber que, quando os operandos são
dois arrays do mesmo tamanho, as operações de soma, subtração, multiplicação e divi-
são ocorrem elemento a elemento. Embora os arrays utilizados nos exemplos anteriores
tenham apenas uma dimensão, as operações podem ser aplicadas a arrays e matrizes com
qualquer número de dimensões.
Por fim, a NumPy fornece ainda várias funções pré-definidas para explorar diver-
sas propriedades de um array qualquer x. Dentre as funções que serão utilizadas ao longo
deste material, pode-se mencionar:
• numpy.sum(x): retorna a soma de todos os elementos de x.
• numpy.max(x): retorna o valor máximo contido em x.
• numpy.min(x): retorna o valor mínimo contido em x.
2.2. Exploração de dados
Tipicamente, para boa parte dos problemas em que técnicas mineração de dados e apren-
dizado de máquina são aplicadas, o conjunto de dados coletados, também conhecido como
base de dados, será representado por meio de uma matriz ou tabela, denotada por Xn×m,
em que n indica o número de objetos observados e m indica o número de características
(ou atributos) que foram coletadas para cada objeto.
Como exemplo a ser utilizado no decorrer deste capítulo, considere a base de
dados Iris, frequentemente utilizada em livros de mineração de dados e de aprendizado
de máquina, originalmente proposta em [Fisher 1936]. Nesta base de dados constam as
informações de 150 exemplos observados de plantas provenientes de três diferentes espé-
cies do gênero Iris: Iris setosa, Iris versicolor e Iris virginica. As características coletadas
para cada exemplo foram: largura da sépala (cm), comprimento da sépala (cm), largura
da pétala (cm) e comprimento da pétala (cm). A Tabela 2.1 apresenta duas observações
(exemplos) de cada espécie (classe).
Tabela 2.1: Dois exemplos de cada classe da base de dados Iris.
Largura sépala Comprimento sépala Largura pétala Comprimento pétala Espécie (classe)
5,1 3,5 1,4 0,2 Iris setosa
4,9 3,0 1,4 0,2 Iris setosa
7,0 3,2 4,7 1,4 Iris versicolor
6,4 3,2 4,5 1,5 Iris versicolor
6,3 3,3 6,0 2,5 Iris virginica
5,8 2,7 5,1 1,9 Iris virginica
A seguir, serão discutidas algumas das principais medidas estatísticas que podem
ser utilizadas para uma exploração inicial dos dados com a finalidade de extrair informa-
ções relevantes de uma base de dados tal como a Iris.
5
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
2.2.1. Dados univariados
Dados univariados são valores de apenas uma variável. No caso de Mineração de Dados
(MD), seriam os valores de um único atributo.
2.2.1.1. Medidas de posição ou localidade
2.2.1.1.1 Média
Considerando a existência de n observações diferentes para uma variável qualquer x, a
média é definida como:
x̄ =
1
n
·
n
∑
i=1
xi (1)
Na biblioteca NumPy, assumindo que um conjunto de n observações seja repre-
sentado por um array de tamanho n, a média pode ser calculada conforme o exemplo a
seguir:
import numpy
# gerando um array com 100 valores aleatorios
# sorteados entre 0 e 1000
x = numpy.random.randint(low=0, high=100, size=100)
# calculando a media utilizando os metodos
# numpy.sum e len
media = numpy.sum(x) / len(x)
print(media)
# mais facilmente, podemos utilizar o metodo numpy.mean
media = numpy.mean(x)
print(media)
2.2.1.1.2 Mediana
Considerando a existência de n observações para uma variável qualquer x, a mediana pode
ser definida como o valor central das observações ordenadas quando n for ímpar. Para os
casos em que n for par, a mediana é definida como valor médio entre as duas observações
centrais ordenadas [Faceli et al. 2011]. Em Python, a mediana pode ser calculada tal
como apresentado no exemplo abaixo:
mediana = numpy.median(x)
print(mediana)
6
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
2.2.1.1.3 Percentis
Um percentil ou quantil, denotado por py%, representa, dentre m observações, o va-
lor tal que y% das observações são menores do que ele [Magalhães e de Lima 2000,
Faceli et al. 2011]. Esta medida trata-se de uma generalização da mediana (que corres-
ponde a p50%) e do primeiro e terceiro quartis (que correspondem a p25% e p75%, respec-
tivamente). Para o seu cálculo, a biblioteca NumPy dispõe do seguinte método:
# para calcular p15%
percentil_15 = numpy.percentile(x, 15)
print(percentil_15)
2.2.1.2. Medidas de dispersão ou espalhamento
2.2.1.2.1 Intervalo ou amplitude
O intervalo (também conhecido como amplitude) consiste na diferença entre o valor má-
ximo e mínimo de um conjunto de observações. Em Python, o mesmo pode ser calculado
como:
valor_maximo = numpy.max(x)
valor_minimo = numpy.min(x)
intervalo = valor_maximo - valor_minimo
print(intervalo)
2.2.1.2.2 Variância e desvio-padrão
A variância de um conjunto de observações, denotada por s2, é definida como:
s2 =
1
n−1
·
n
∑
i=1
(xi− x̄)2 (2)
em que x̄ consiste na média das observações (Equação 1). Para manter a mesma unidade
dos dados originais, o desvio-padrão é definido como [Magalhães e de Lima 2000]:
s =
√
s2. (3)
O cálculo dessas duas medidas pode ser implementado como:
media = numpy.mean(x)
n = len(x)
diferencas = x - media
variancia = numpy.sum(diferencas * diferencas) / (n - 1)
desvio_padrao = numpy.sqrt(variancia)
# de maneira mais facil, podemos utilizar as
7
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
# seguintes funcoes
variancia = numpy.var(x, ddof=1)
desvio_padrao = numpy.std(x, ddof=1)
em que o parâmetro ddof indica o número de graus de liberdade utilizados. Para o
cálculo na Equação (2), as funções numpy.var e numpy.std utilizam como divisor
a fórmula n− ddof. Em todos os cenários estudados neste material será utilizada a
variância amostral e o desvio-padrão amostral. Portanto, o valor considerado para ddof
sempre será 12.
2.2.1.3. Medidas de distribuição
2.2.1.3.1 Obliquidade
A obliquidade (ou skewness, em inglês) trata-se de uma medida de assimetria de uma
distribuição de probabilidade em torno de sua média. Ela pode assumir valores negativos,
positivos ou próximos de 0. No primeiro caso, a cauda da distribuição é mais alongada
à esquerda e, por consequência, a distribuição dos dados concentra-se mais à direita no
seu respectivo gráfico. No segundo caso, a cauda da distribuição é mais alongada para
a esquerda, o que aponta uma maior concentração dos dados à direita do seu respectivo
gráfico. Por fim, no terceiro caso, a distribuição possui caudas aproximadamente balan-
ceadas e, como resultado, ela terá uma maior simetria. Na Figura 2.1 são apresentados
exemplos para as duas primeiras situações, quando a variável assume valores contínuos.
Figura 2.1: Ilustração de obliquidade negativa e positiva (Fonte: [Hermans 2008]3).
O cálculo da obliquidade é definido por:
obliquidade =
∑
n
i=1(xi− x̄)3
(n−1) · s3 (4)
sendo x̄ e s definidos tal como nas equações (1) e (3), nessa ordem.
Em Python, a obliquidade dos dados contidos em um array pode ser calculada por
meio da biblioteca SciPy da seguinte maneira:
2https://en.wikipedia.org/wiki/Degrees_of_freedom_(statistics)
3A imagem original está disponível sob a licença CC-BY-SA 3.0 (https://creativecommons.
org/licenses/by-sa/3.0/).
8
https://en.wikipedia.org/wiki/Degrees_of_freedom_(statistics)
https://creativecommons.org/licenses/by-sa/3.0/
https://creativecommons.org/licenses/by-sa/3.0/
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
import numpy
import scipy.stats
# gerando uma amostra com 10000 observacoes a partir de
# uma distribuicao normal com media zero e desvio-padrao
# unitario
dados = numpy.random.normal(loc=0.0, scale=1.0, size=10000)
# como os dados foram gerados segundo uma distribuicao# normal, que eh simetrica, a obliquidade devera resultar
# em algum valor proximo de 0
obliquidade = scipy.stats.skew(dados)
print(obliquidade)
2.2.1.3.2 Curtose
A curtose é uma medida que caracteriza o achatamento da distribuição dos dados. Assim
como a obliquidade, os seus valores podem ser negativos, positivos ou próximos de 0.
No primeiro caso, a distribuição é mais achatada e apresenta picos mais baixos e caudas
mais leves4 quando comparada à distribuição normal. No segundo caso, a distribuição dos
dados apresenta picos mais elevados e caudas mais pesadas5 ao se comparar à distribuição
normal. Por fim, no último caso, a distribuição dos dados apresenta achatamento e caudas
próximas ao que ocorre com a distribuição normal.
A equação para o cálculo da curtose é definida como:
curtose =
∑
n
i=1(xi− x̄)4
(n−1) · s4 (5)
sendo x̄ e s definidos, respectivamente, pelas Equações (1) e (3).
Tal como a obliquidade, a curtose pode ser calculada por meio da biblioteca SciPy:
import numpy
import scipy.stats
# gerando uma amostra com 10000 observacoes a partir de
# uma distribuicao normal com media zero e desvio-padrao
# unitario
dados = numpy.random.normal(loc=0.0, scale=1.0, size=10000)
# como os dados foram gerados segundo uma distribuicao
# normal, que eh simetrica, a curtose devera resultar
# em algum valor proximo de 0
4Isto é, valores extremos (outliers) tendem a ser pouco frequentes. Como um exemplo de extrema
curtose, pode-se citar a distribuição uniforme, a qual não apresenta a ocorrência de outliers.
5Ou seja, outliers tendem a ser mais frequentes.
9
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
curtose = scipy.stats.kurtosis(dados)
print(curtose)
2.3. Dados multivariados
Dados multivariados podem ser definidos como conjuntos de dados em que cada obser-
vação consiste em um vetor de características e não apenas um único valor, como nos
exemplos apresentados na seção anterior. No caso de MD, cada elemento do vetor corres-
ponde a um atributo do conjunto de dados. Em outras palavras, cada observação i pode
ser definida como um vetor xi = [xi1 xi2 · · · xim]
T , em que m indica o número de
características coletadas para cada observação. Portanto, um conjunto de observações.
X =

xT
1
xT
2
...
xT
n
 (6)
corresponderá a um conjunto de dados Xn×m (Seção 2.2).
Adotando essa definição, todos os conceitos discutidos na seção anterior podem
ser redefinidos. Desse modo, cada medida de posição ou dispersão pode ser reformulada
para calcular como resultado um vetor de comprimento m em que cada posição corres-
ponde ao valor de tal medida para cada atributo [Faceli et al. 2011].
2.3.1. Exemplo: Iris
Nesta seção será brevemente apresentado como algumas das medidas discutidas na Seção
2.2.1 podem ser calculadas para dados multivariados na linguagem Python. Para isso,
será utilizado como exemplo o conjunto de dados Iris6, introduzido na Seção 2.2.
Para carregar o arquivo CSV para o conjunto de dados Iris, recomenda-se a utili-
zação da biblioteca Pandas, a qual dispõe do tipo de dados DataFrame, que tem uma
estrutura tabular, similar àquela apresentada na Tabela 2.1. Assim, pode-se proceder da
seguinte maneira:
import pandas
# carregando iris.csv
dados = pandas.read_csv('iris.csv')
# imprimindo os dez primeiros exemplos da base de dados
print(data.head(10))
Conforme já mencionado, para dados multivariados, as medidas apresentadas na
seção anterior serão calculadas sobre cada atributo do problema a ser tratado. Portanto,
como exemplo, considere o seguinte código para a Iris:
6Disponível em: https://raw.githubusercontent.com/pandas-dev/pandas/
master/pandas/tests/data/iris.csv.
10
https://raw.githubusercontent.com/pandas-dev/pandas/master/pandas/tests/data/iris.csv
https://raw.githubusercontent.com/pandas-dev/pandas/master/pandas/tests/data/iris.csv
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
# calculando a media para todos os atributos
print(dados.mean())
# calculando a mediana para todos os atributos
print(dados.median())
# calculando percentil 15% para todos os atributos
print(dados.quantile(0.15))
# calculando o intervalo para todos os atributos
print(dados.max(numeric_only=True) - dados.min(numeric_only=True))
# calculando a variancia para todos os atributos
print(data.var())
# calculando o desvio-padrao para todos os atributos
# (na biblioteca pandas, ddof=1 por padrao)
print(data.std())
# calculando a obliquidade para todos os atributos
print(data.skew())
# calculando a curtose para todos os atributos
print(data.kurtosis())
2.4. Visualização de dados
2.4.1. Histogramas
Uma das formas mais simples de ilustrar a distribuição de um conjunto de valores de
uma variável é o uso de histogramas. Neste tipo de gráfico tem-se, no eixo horizontal, o
conjunto (ou intervalos) de valores observados, enquanto que no eixo vertical, apresenta-
se a frequência de ocorrência de cada valor (ou valores dentre de um intervalo) presente
na amostra analisada. O pacote NumPy fornece uma função para calcular o histograma,
que pode ser vista abaixo. Nessa função, bins corresponde ao número de barras verti-
cais. Quando o valor de bins é definido como ’auto’, o número de barras é definido
automaticamente. Como exemplo, considere o código a seguir:
# calculando o histograma para uma variável
import numpy
from matplotlib import pyplot
# Notas na primeira prova
notas = numpy.array([2, 5, 7, 3, 5, 6, 5, 6, 6, 5, 5, 3])
pyplot.hist(notas, bins='auto')
pyplot.title('Histograma')
11
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
pyplot.ylabel('Frequencia')
pyplot.xlabel('Nota')
pyplot.show()
O histograma gerado utilizando os dados do código anterior é ilustrado pela Figura
2.2. As cestas sem barras são os intervalos para os quais não havia nenhum valor.
Figura 2.2: Exemplo de histograma para os valores de uma variável.
2.4.2. Scatter plots
Um scatter plot consiste em um tipo de gráfico comumente utilizado para observar o
comportamento entre duas variáveis de uma base de dados. Na Figura 2.3 é apresentado
um exemplo de scatter plot para a base de dados Iris, em que cada cor indica uma classe
diferente.
Figura 2.3: Exemplo de scatter plot para dois atributos da base de dados Iris.
12
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
Um scatter plot pode ser gerado por meio do seguinte código:
import pandas
from matplotlib import pyplot
# carregando iris.csv
dados = pandas.read_csv('iris.csv')
# criando um dicionario para mapear cada classe para uma cor
classe_cor = {'Iris-setosa' : 'red',
'Iris-virginica' : 'blue',
'Iris-versicolor' : 'green'}
# criando uma lista com as cores de cada exemplo
cores = [classe_cor[nome] for nome in dados.Name]
# gerando scatter plot
# no eixo x sera plotado o tamanho da sepala
# no eixo y sera plotado o comprimento da sepala
dados.plot(kind='scatter', x='SepalLength', y='SepalWidth',
c=cores)
pyplot.show()
Alternativamente, pode-se também gerar uma matriz de scatter plots, que irá con-
ter os scatter plots para todos os pares possíveis de atributos. Ademais, na diagonal de
tal matriz, pode-se apresentar informações a respeito de cada atributo (por exemplo, o seu
histograma). Na Figura 2.4 é apresentada a matriz de scatter plots para a base Iris, com
os histogramas dos atributos contidos na diagonal. Tal matriz pode ser gerada por meio
do seguinte código:
import pandas
from pandas.plotting import scatter_matrix
from matplotlib import pyplot
# carregando iris.csv
dados = pandas.read_csv('iris.csv')
# criando um dicionario para mapear cada classe para uma cor
classe_cor = {'Iris-setosa' : 'red',
'Iris-virginica' : 'blue',
'Iris-versicolor' : 'green'}
# criando uma lista com as cores de cada exemplo
cores = [classe_cor[nome] for nome in dados.Name]
13
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
# gerando matriz de scatter plots
scatter_matrix(dados, color=cores)
pyplot.show()
Figura 2.4: Matrizde scatter plots para a base de dados Iris.
2.4.3. Box plots
Um box plot é um gráfico apresentado em formato de caixa, em que a aresta inferior
da caixa representa o primeiro quartil (Q1), a aresta superior representa o terceiro quartil
(Q3) e um traço interno à caixa representa a mediana (Q2) de uma amostra. Ademais, uma
linha tracejada delimita o limite entre o terceiro quartil e o maior valor das observações
que é menor ou igual a Q3 +1,5 · (Q3−Q1) e o limite entre o primeiro quartil e o menor
valor na amostra que é maior ou igual a Q1−1,5 · (Q3−Q1). Valores abaixo ou acima de
tal linha tracejada são representados como círculos, e indicam valores extremos (outliers).
Na Figura 2.5 é apresentado um exemplo de box plot para os atributos da base Iris, o qual
pode ser gerado por meio do exemplo de código apresentado a seguir:
import pandas
from pandas.plotting import scatter_matrix
from matplotlib import pyplot
14
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
# carregando iris.csv
dados = pandas.read_csv('iris.csv')
# gerando bloxplot para os atributos da Iris
dados.plot(kind='box')
pyplot.show()
Figura 2.5: Box plot para os atributos da base de dados Iris.
2.5. Exercícios
1. Pesquise e explique com as suas palavras sobre o que extraem as medidas de co-
variância e correlação. Qual a utilidade delas? Aplique-as para a base de dados
Iris, apresente e interprete os resultados (dica: use as funções DataFrame.cov7
e DataFrame.corr8).
2. Considere a matriz de scatter plots da Figura 2.4. A partir dos plots, o que pode ser
observado? Existe alguma classe mais separada das demais? Existem classes sobre-
postas? Qual a sua opinião sobre as possíveis implicações das classes sobrepostas
para uma tarefa de mineração de dados?
3. Calcule e apresente, para cada atributo da base Iris, sua obliquidade e sua curtose.
Compare os resultados obtidos com o box plot da Figura 2.5. Ademais, gere os
7https://pandas.pydata.org/pandas-docs/stable/generated/pandas.
DataFrame.cov.html
8https://pandas.pydata.org/pandas-docs/stable/generated/pandas.
DataFrame.corr.html
15
https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.cov.html
https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.cov.html
https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.corr.html
https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.corr.html
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
histogramas para cada atributo da base (dica: pesquise sobre os parâmetros neces-
sários para gerar histogramas com a função DataFrame.plot). Que atributos
possuem distribuições mais simétricas? Há a presença de valores extremos (outli-
ers) para algum atributo? Se sim, qual? O que você acha que pode ser a razão para
a ocorrência de tais valores?
Referências
[Faceli et al. 2011] Faceli, K., Lorena, A. C., Gama, J., e Carvalho, A. C. P. L. F. (2011).
Inteligência artificial: Uma abordagem de aprendizado de máquina. Rio de Janeiro:
LTC, 2:192.
[Fisher 1936] Fisher, R. A. (1936). The use of multiple measurements in taxonomic
problems. Annals of human genetics, 7(2):179–188.
[Hermans 2008] Hermans, R. (2008). Negative and positive skew diagrams
(English). https://commons.wikimedia.org/wiki/File:Negative_
and_positive_skew_diagrams_(English).svg. Acesso em: 04 ago.
2017.
[Magalhães e de Lima 2000] Magalhães, M. N. e de Lima, A. C. P. (2000). Noções de
probabilidade e estatística. IME-USP São Paulo:.
16
https://commons.wikimedia.org/wiki/File:Negative_and_positive_skew_diagrams_(English).svg
https://commons.wikimedia.org/wiki/File:Negative_and_positive_skew_diagrams_(English).svg
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
Capítulo
3
Pré-processamento
Considerando o grande volume de dados disponível em diversas aplicações, com
frequência os conjuntos de dados não possuirão uma qualidade boa o suficiente para a ex-
tração de conhecimento novo, útil e relevante por algoritmos de aprendizado de máquina
(AM). As principais causas de baixa qualidade de dados incluem a ocorrência de atributos
irrelevantes, valores ausentes ou redundantes. Neste capítulo serão apresentadas algumas
simples abordagens para melhorar a qualidade dos dados, que aumentam as chances de
um bom modelo ser induzido por um algoritmo de AM. Na Seção 3.1 será descrito o
conjunto de dados que será utilizado para ilustrar o uso de técnicas de pré-processamento.
Na Seção 3.2 será apresentado como alguns atributos podem ser removidos manualmente.
Na Seção 3.3 serão apresentadas algumas das técnicas de amostragem de dados mais co-
muns. Na Seção 3.4 será explicado como dados ausentes podem ser tratados. Na Seção
3.5 será abordada a ocorrência de objetos ou atributos redundantes. Por fim, na Seção 3.9
serão propostos alguns exercícios para fixar os conceitos apresentados.
3.1. Conjunto de dados
Para os exemplos apresentados no presente capítulo, serão utilizados dois conjuntos de
dados: Breast Cancer Wisconsin1 [Street et al. 1992, Mangasarian et al. 1995] e Contra-
ceptive Method Choice2 [Lim et al. 2000].
No conjunto Breast Cancer Wisconsin cada objeto consiste em um tecido de massa
mamária e os atributos correspondem a características, extraídas a partir de imagens digi-
talizadas, dos núcleos celulares (raio, textura, perímetro etc.) contidos em cada tecido. As
classes associadas a cada tecido informam o diagnóstico do tecido (maligno ou benigno).
O conjunto Contraceptive Method Choice consiste em amostras de uma pesquisa
realizada na Indonésia em 1987 sobre métodos contraceptivos. Os objetos consistem
em mulheres que não estavam grávidas ou que não sabiam da gravidez ao participarem
da pesquisa. Os atributos consistem em características socio-econômicas. O problema
deste conjunto de dados consiste em classificar o método contraceptivo utilizado em: 1
(nenhum método utilizado), 2 (método de longa duração) ou 3 (método de curta duração).
Todos os atributos são dados qualitativos nominais ou ordinais. Uma descrição completa
1http://archive.ics.uci.edu/ml/datasets/Breast+Cancer+Wisconsin+
%28Diagnostic%29
2http://archive.ics.uci.edu/ml/datasets/Contraceptive+Method+Choice
18
http://archive.ics.uci.edu/ml/datasets/Breast+Cancer+Wisconsin+%28Diagnostic%29
http://archive.ics.uci.edu/ml/datasets/Breast+Cancer+Wisconsin+%28Diagnostic%29
http://archive.ics.uci.edu/ml/datasets/Contraceptive+Method+Choice
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
de cada atributo e seus possíveis valores está disponível em http://archive.ics.
uci.edu/ml/machine-learning-databases/cmc/cmc.names.
Os exemplos apresentados a seguir utilizarão os arquivos breast_cancer.csv
e cmc.csv, fornecidos como material suplementar a este texto.
3.2. Eliminação manual de atributos
Diversos conjuntos de dados do mundo real podem ter atributos que, por serem claramente
irrelevantes, não apresentam qualquer benefício para uma tarefa de classificação ou de
extração de conhecimento. Por exemplo, o conjunto de dados Breast Cancer contém o
atributo sample_id, um valor numérico que identifica o tecido analisado. Como tal
valor será único para cada objeto e o mesmo não possui qualquer sentido comparativo,
ele pode ser eliminado. Em Python, isso pode ser realizado por meio do código a seguir.
import pandas
dados = pandas.read_csv('breast_cancer.csv')
# removendo a coluna sample_id, a qual nao eh necessaria
# para realizar o diagnostico
del dados['sample_id']
3.3. Amostragem
Diversos algoritmos de AM, sejam pela suas complexidades computacionais ou pelas
quantidades de memória exigida, apresentam dificuldades em tratar conjuntos de dados
de tamanho elevado. Uma quantidade de dados muito grande pode tornar o processo de
treinamento demorado. Um meio de contornar essa dificuldade é a utilização de amostras
do conjuntos de dados original. A utilização de um subconjunto menor de objetos, em
geral tornará mais simples e rápida a geração deum modelo.
Entretanto, um ponto importante a ser levado em consideração está relacionado
ao nível ao qual a amostra representa a distribuição original dos dados. Normalmente,
em casos nos quais a mesma não seja representativa, o modelo de AM induzido não será
capaz de atingir uma eficiência aceitável para o problema.
Um modo para tratar o ponto supracitado, resume-se na utilização de técnicas
de amostragem estatística, as quais aumentam as chances de que as amostras extraídas
da base de dados original possam ser informativas e representativas. Duas das técnicas
mais utilizadas são a amostragem simples e a amostragem estratificada. Ambas serão
introduzidas a seguir.
3.3.1. Amostragem simples
A amostragem simples consiste basicamente em extrair objetos, com ou sem reposição,
do conjunto de dados original com igual probabilidade. No primeiro caso, quando há a
reposição, um objeto pode ocorrer mais de uma vez em uma amostra e a probabilidade
de ocorrência de cada objeto mantém-se constante durante todo o processo. No segundo
19
http://archive.ics.uci.edu/ml/machine-learning-databases/cmc/cmc.names
http://archive.ics.uci.edu/ml/machine-learning-databases/cmc/cmc.names
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
caso, quando não existe reposição, cada objeto poderá acontecer apenas uma vez em uma
amostra. Assim, a probabilidade de ocorrência de cada objeto é alterada a cada passo do
processo. O código abaixo apresenta um exemplo para cada caso.
import pandas
dados = pandas.read_csv('breast_cancer.csv')
# gerando uma amostra aleatoria simples de 100 elementos
# da base breast cancer, sem reposicao
dados.sample(100)
# gerando uma amostra aleatoria simples de 100 elementos
# da base breast cancer, com reposicao
dados.sample(100, replace=True)
3.3.2. Amostragem estratificada
A amostragem estratificada é tipicamente utilizada quando há o desbalanceamento na
quantidade de exemplos de cada classe em um conjunto de dados. Ou seja, neste cenário,
normalmente uma ou outra classe possuirá uma quantidade de objetos consideravelmente
maior do que as demais. A fim de gerar um classificador que não seja tendencioso para
uma ou mais classes majoritárias ou, de suavizar a dificuldade de modelar alguma das
classes de um problema, a amostragem estratificada pode ser utilizada de duas maneiras.
Na primeira abordagem para amostragem estratificada, o subconjunto a ser ex-
traído contém a mesma quantidade de objetos para cada classe. O código a seguir ilustra,
de maneira simplificada, esse tratamento.
import pandas
dados = pandas.read_csv('breast_cancer.csv')
# tamanho da amostra estratificada
tamanho_amostra = 100
# obtendo as classes da base de dados
classes = dados['diagnosis'].unique()
# calculando a quantidade de amostras por classe
# neste exemplo, serao amostradas as mesmas
# quantidades para cada classe
qtde_por_classe = round(tamanho_amostra / len(classes))
# nesta lista armazenaremos, para cada classe, um
# pandas.DataFrame com suas amostras
amostras_por_classe = []
20
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
for c in classes:
# obtendo os indices do DataFrame
# cujas instancias pertencem a classe c
indices_c = dados['diagnosis'] == c
# extraindo do DataFrame original as observacoes da
# classe c (obs_c sera um DataFrame tambem)
obs_c = dados[indices_c]
# extraindo a amostra da classe c
# caso deseje-se realizar amostragem com reposicao
# ou caso len(obs_c) < qtde_por_classe, pode-se
# informar o parametro replace=True
amostra_c = obs_c.sample(qtde_por_classe)
# armazenando a amostra_c na lista de amostras
amostras_por_classe.append(amostra_c)
# concatenando as amostras de cada classe em
# um único DataFrame
amostra_estratificada = pd.concat(amostras_por_classe)
Na segunda abordagem, a amostra gerada do conjunto de dados original mantém
as mesmas proporções de objetos da base de dados original em cada classe. O exemplo
de código a seguir demonstra como isso pode ser feito.
import pandas
dados = pandas.read_csv('breast_cancer.csv')
# tamanho da amostra estratificada
tamanho_amostra = 100
# obtendo as classes da base de dados
classes = dados['diagnosis'].unique()
# nesta lista armazenaremos, para cada classe, um
# pandas.DataFrame com suas amostras
amostras_por_classe = []
for c in classes:
# obtendo os indices do DataFrame
# cujas instancias pertencem a classe c
indices_c = dados['diagnosis'] == c
21
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
# extraindo do DataFrame original as observacoes da
# classe c (obs_c sera um DataFrame tambem)
obs_c = dados[indices_c]
# calculando a proporcao de elementos da classe c
# no DataFrame original
proporcao_c = len(obs_c) / len(dados)
# calculando a quantidade de elementos da classe
# c que estarao contidos na amostra estratificada
qtde_c = round(proporcao_c * tamanho_amostra)
# extraindo a amostra da classe c
# caso deseje-se realizar amostra com reposicao ou,
# caso len(obs_c) < qtde_c, pode-se
# informar o parametro replace=True
amostra_c = obs_c.sample(qtde_c)
# armazenando a amostra_c na lista de amostras
amostras_por_classe.append(amostra_c)
# concatenando as amostras de cada classe em
# um único DataFrame
amostra_estratificada = pd.concat(amostras_por_classe)
3.4. Dados ausentes
Dados ausentes podem ocorrer por uma série de motivos em cenários de aplicação reais
(veja, por exemplo, a discussão no Capítulo 3 do livro de [Faceli et al. 2011]). Uma alter-
nativa que pode ser utilizada para a solução de tal problema é a remoção de objetos que
contenham valores ausentes. Entretanto, deve-se perceber que uma grande quantidade de
informações relevantes podem acabar sendo descartadas (por exemplo, se muitos objetos
possuírem valores ausentes, tem-se uma redução significativa da base de dados). Por-
tanto, uma alternativa bastante comum é a inserção automática de tais valores, utilizando
alguma medida capaz de estimá-los. Comumente, tal medida consiste na média, mediana
ou moda do respectivo atributo. Em Python, a inserção automática pode ser realizada por
meio do código abaixo.
import pandas
# carrega uma versao do conjunto de dados contendo valores
# ausentes
dados = pandas.read_csv('breast_cancer_missing.csv')
# cada valor ausente eh indicado por um
# 'Not a Number' (numpy.nan)
22
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
# isso pode ser verificado conforme abaixo
# onde 'valores_ausentes' sera um DataFrame em que
# cada posicao indica se o valor esta ausente
# (True) ou nao (False)
valores_ausentes = dados.isnull()
# calculando as medias dos atributos
# 'medias' resulta em uma variavel do tipo 'Series',
# o qual consiste em uma implementacao da Pandas
# de um numpy.array com cada elemento contendo
# um rotulo (ou nome)
medias = dados.mean()
# por fim, a insercao de valores sera realizada pela
# funcao fillna, onde o parametro inplace=True indica
# para os valores serem inseridos no próprio DataFrame
# (caso seja falso, uma copia do DataFrame original sera
# retornada contendo os valores preenchidos)
dados.fillna(medias, inplace=True)
3.5. Dados redundantes
Dados redundantes podem ocorrer tanto para os objetos quanto para os atributos de um
conjunto de dados [Faceli et al. 2011]. Quando há a redundância de objetos, dois ou mais
deles possuem valores muito similares (ou até mesmo iguais) para todos os seus atributos.
Isso pode ser um problema pois, ao aplicar um algoritmo de AM, tais objetos irão inserir
uma ponderação artificial aos dados. Objetos redundantes podem ser removidos por meio
do código a seguir.
import pandas
# carregando uma versao do conjunto de dados contendo
# objetos redundantes (repetidos)
dados = pandas.read_csv('breast_cancer_red.csv')
# removendo exemplos redundantes
# observe que o resultado sera a base original
# o parametro inplace determina se a alteracao deve
# ocorrer no proprio DataFrame
# portanto, caso inplace=False, a funcao drop_duplicates
# retorna uma copia de breast_cancer_redcom os exemplos
# redundantes removidos
dados.drop_duplicates(inplace=True)
Para o caso de redundância de atributos, tem-se que um deles pode estar forte-
mente correlacionado com outro, o que indica um comportamento similar de suas varia-
ções. Algumas vezes, em tais casos, o valor de um atributo pode ser calculado ou obtido
23
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
por meio de um ou mais dentre os atributos remanescentes. A remoção de atributos redun-
dantes é proposta como exercício na Seção 3.9. Portanto, o respectivo exemplo prático
foi omitido neste texto.
3.6. Ruídos
Ruídos normalmente consistem em valores que aparentemente não pertencem à distribui-
ção que gerou o conjunto de dados à disposição [Faceli et al. 2011]. Vários podem ser
os motivos para a ocorrência dos mesmos, desde erros na digitação ao tabular os dados,
problemas de medição em instrumentos utilizados para coletar os dados ou, até mesmo,
valores pouco comuns mas reais. Portanto, ao descartar objetos que apresentem valores
ruidosos para um ou mais atributos, pode-se perder informações relevantes para o pro-
blema estudado.
A fim de reduzir a influência de ruídos nos atributos do conjunto de dados estu-
dado, diversas técnicas podem ser aplicadas. Dentre elas, uma das mais simples consiste
em dividir os valores de cada atributo em faixas, de modo que cada faixa contenha apro-
ximadamente a mesma quantidade de valores. Em seguida, os valores contidos em cada
faixa são substituídos por alguma medida que os sumarize como, por exemplo, a média.
Um exemplo desta técnica é demonstrado a seguir.
import pandas
dados = pandas.read_csv('breast_cancer.csv')
# dividindo o atributo 'mean_radius' em 10 faixas
bins = pandas.qcut(dados['mean_radius'], 10)
# a quantidade de valores aproximadamente igual em
# cada faixa pode ser comprovada pelo metodo
# value_counts
bins.value_counts()
# O metodo groupby permite que valores contidos na
# coluna de um DataFrame sejam agrupados segundo
# algum criterio.
# Neste exemplo, a coluna 'mean_radius' sera agrupada
# pelas faixas definidas pelo metodo qcut.
grupos = dados['mean_radius'].groupby(bins)
# obtendo a media de cada faixa
medias = grupos.mean()
# Obtendo a nova coluna.
# O metodo apply recebe como parametro uma funcao,
# aplica tal funcao a todos os seus elements e retorna
# um pandas.Series contendo os resultados.
24
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
# Neste caso, cada elemento de bins consiste
# no intervalo que o respectivo valor de 'mean_radius'
# pertence e, assim, a funcao informada em apply
# retornara a respectiva media de cada intervalo.
novo_mean_radius = bins.apply(lambda x : medias[x])
# por fim, a coluna 'mean_radius' do DataFrame original
# eh atualizada
dados['mean_radius'] = novo_mean_radius
3.7. Transformação de dados
3.7.1. Transformação de dados simbólicos para dados numéricos
Diversas técnicas de AM exigem que os dados de entrada consistam apenas em valores
numéricos. Entretanto, diversos conjuntos reais podem apresentar atributos qualitativos
nominais ou ordinais, tornando assim necessário o emprego de técnicas que permitam a
conversão de tais atributos.
No primeiro caso, quando houver a existência de atributos nominais, a inexistência
de qualquer ordem deve persistir na conversão do atributo. Uma abordagem bastante
conhecida e utililizada para isso consiste na codificação 1-de-c [Faceli et al. 2011]. Nela,
considerando que um atributo possua c valores possíveis, são criados c novos atributos
binários, onde cada posição indica um possível valor do atributo nominal original. Desse
modo, apenas uma posição da nova sequência binária de cada objeto poderá ser igual
a 1, indicando qual é o valor correspondente de um determinado objeto para o atributo
original. Em Python, tal conversão pode ser facilmente realizada por meio do método
pandas.get_dummies, conforme demonstrado pelo exemplo a seguir.
import pandas
dados = pandas.read_csv('cmc.csv')
# O atributo husband_occupation consiste em um atributo
# nominal com 4 valores possiveis.
# A conversao do mesmo para a codificacao 1-de-c eh
# feita como:
occ1dec = pandas.get_dummies(dados['husband_occupation'])
Quando houverem atributos qualitativos ordinais, pode-se também optar por repre-
sentações binárias. Entretanto, as mesmas deverão ser diferentes da representação 1-de-c,
uma vez que as distâncias entre os possíveis valores de um atributo não serão mais iguais
para todos os casos. Para isso, pode-se utilizar o código cinza ou o código termômetro
[Faceli et al. 2011].
O código cinza consiste na transformação dos valores inteiros para as suas respec-
tivas representações binárias. Em Python, tal transformação pode ser realizada conforme
segue.
25
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
import pandas
dados = pandas.read_csv('cmc.csv')
# O atributo wife_education consiste em um atributo
# qualitativo ordinal com 4 valores possiveis.
# A conversao do mesmo para o codigo cinza pode ser
# feita pela aplicacao do metodo 'bin' na respectiva
# coluna do DataFrame. O metodo 'bin' recebe como entrada
# um valor inteiro e retorna uma string com a representacao
# binaria do mesmo.
wife_ed_binario = dados['wife_education'].apply(bin)
O código termômetro realiza a transformação do respectivo atributo qualitativo
ordinal em um vetor de c posições, onde c indica a quantidade de valores possíveis. Desse
modo, cada valor ordinal corresponde a um vetor binário preenchido com uma quantidade
de valores iguais a 1, acumulados sequencialmente da esquerda para a direita ou vice-
versa, equivalente à sua posição na ordem dos valores possíveis do atributo original. Esta
transformação é proposta como exercício na Seção 3.9. Portanto, o respectivo código foi
omitido.
3.7.2. Transformação de dados numéricos para dados simbólicos
Várias de técnicas de AM como, por exemplo, alguns algoritmos de árvore de decisão,
exigem que os dados de entrada assumam valores qualitativos. Portanto, em cenários nos
quais há a presença de atributos com valores contínuos, torna-se importante a aplicação
de técnicas adequadas de discretização.
Existe uma grande quantidade de técnicas de discretização disponíveis na litera-
tura (os estudos de [Kotsiantis e Kanellopoulos 2006, Garcia et al. 2013] sumarizam vá-
rias delas), as quais são baseadas em diferentes suposições e procedimentos. Dentre elas,
as mais simples e intuitivas consistem na divisão de um intervalo original de valores por
faixas, podendo estas terem a mesma largura ou frequências de valores similares. O có-
digo a seguir ilustra ambas.
import pandas
dados = pandas.read_csv('breast_cancer.csv')
# Obtendo a coluna 'mean_radius'.
mean_radius = dados['mean_radius']
# Discretizando a coluna 'mean_radius'.
# O parametro 'bins' define em quantos intervalos
# sera discretizado.
# O parametro labels define o rotulo de cada
# intervalo.
26
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
# Neste caso, como labels eh igual a range(10),
# os intervalos serao discretizados com valores
# inteiros entre 0 e 9.
# O metodo pandas.cut discretiza em intervalos
# de larguras iguais. Caso deseje-se discretizar
# com frequencias iguais, deve-se utilizar o
# metodo pandas.qcut.
mean_radius_disc = pandas.cut(dados, bins=10,
labels=range(10))
# Atualizando a respectiva coluna no DataFrame
# original.
dados['mean_radius'] = mean_radius_disc
3.7.3. Normalização de atributos numéricos
Muitos conjuntos de dados reais apresentam atributos contínuos cujos valores espalham-
se por distintas faixas de valores ou que possuem diferentes variações de valores, devido
às suas naturezas ou escalas em que foram medidas. Em alguns problemas, tais diferenças
podem ser importantes e devem ser levadas em conta [Faceli et al. 2011]. Entretanto, em
outras situações pode ser necessária uma normalização dos valores de cada atributo, a fim
de que se evite que algum predomine sobre outroou que inclua qualquer tipo de ponde-
ração indesejada ao induzir um modelo de AM. Os tipos mais comuns de normalização
consistem em reescala ou padronização.
A normalização por reescala define, através de um valor mínimo e um valor má-
ximo, um novo intervalo onde os valores de um atributo estarão contidos. Tipicamente,
tal intervalo é definido como [0,1]. Portanto, para este caso, a normalização por reescala
de um atributo j de um objeto xi pode ser calculada como:
xi j =
xi j −min j
max j−min j
(1)
sendo min j e max j, nessa ordem, os valores mínimo e máximo do atributo j para o con-
junto de dados considerado.
Na normalização por padronização, os diferentes atributos contínuos poderão abran-
ger diferentes intervalos, mas deverão possuir os mesmos valores para alguma medida de
posição e de espalhamento/variação [Faceli et al. 2011]. Tipicamente, tais medidas irão
consistir na média e no desvio-padrão. Neste caso, o valor normalizado de um atributo j
em um objeto i é dado por:
xi j =
xi j − x̄· j
s j
(2)
onde x̄· j e s j representam a média do atributo j e o seu desvio-padrão, respectivamente.
Desse modo, cada cada atributo j terá média zero e desvio-padrão unitário.
Os dois tipos de normalização supramencionados podem ser executados conforme
o código em seguida.
27
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
import pandas
from sklearn.preprocessing import minmax_scale
from sklearn.preprocessing import scale
dados = pandas.read_csv('breast_cancer.csv')
# Obtendo os nomes das colunas do DataFrame
# como uma lista.
cols = list(dados.columns)
# Removendo da lista 'cols' os nomes
# 'sample_id' e 'diagnosis' que sao
# colunas que nao serao normalizadas
cols.remove('sample_id')
cols.remove('diagnosis')
# Copiando os dados e aplicando a normalizacao
# por reescala nas colunas do DataFrame que contem
# valores continuos.
# Por padrao, o metodo minmax_scale reescala
# com min=0 e max=1.
dados_amp = dados.copy()
dados_amp[cols] = dados[cols].apply(minmax_scale)
# Copiando os dados e aplicando a normalizacao
# por padronização a todas as colunas do DataFrame.
# Por padrao, o metodo scale subtrai a media e
# divide pelo desvio-padrao.
dados_dist = dados.copy()
dados_dist[cols] = dados[cols].apply(scale)
3.8. Redução de dimensionalidade
Muitos dos conjuntos de dados tratados em mineração de dados possuem como caracte-
rística um elevado número de atributos. Uma aplicação biológica clássica em que esse
tipo de situação ocorre é na análise de dados de expressão gênica, onde milhares de ge-
nes são tipicamente aferidos em um número consideravelmente menor e mais limitado de
amostras (normalmente de dezenas a algumas centenas).
São poucas as técnicas de AM que são capazes de lidar com uma grande quan-
tidade de atributos de maneira eficiente (tanto do ponto de vista computacional quanto
do ponto de vista de acurácia). Portanto, a utilização de técnicas que sejam capazes de
reduzir a dimensionalidade dos dados, por meio de agregação ou seleção de atributos,
pode auxiliar na indução de um modelo de AM. Nesta seção, serão abordados dois exem-
plos para redução de dimensionalidade: Principal Component Analysis, para agregação
de atributos; e filtragem por um limiar de variância pré-estabelecido, para seleção de atri-
butos.
28
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
3.8.1. Principal Component Analysis (PCA)
O PCA consiste em uma técnica estatística que utiliza uma transformação linear para
reexpressar um conjunto de atributos em um conjunto menor de atributos não correlaci-
onados linearmente, mantendo boa parte das informações contidas nos dados originais
[Dunteman 1989]. Em suma os objetivos do PCA consistem em [Abdi e Williams 2010]:
• Extrair apenas as informações mais relevantes de um conjunto de dados;
• Comprimir o tamanho do conjunto de dados original, simplificando assim sua des-
crição; e
• Permitir a análise da estrutura dos objetos e dos atributos de um conjunto de dados.
Um simples e intuitivo tutorial sobre o PCA está disponível em https://arxiv.
org/abs/1404.1100. Abaixo é apresentado um exemplo da aplicação do PCA no
conjunto Breast Cancer.
import pandas
from sklearn.decomposition import PCA
dados = pd.read_csv('breast_cancer.csv')
# Obtendo os nomes das colunas.
cols = list(dados.columns)
# Removendo colunas que nao serao inclusas
# na reducao de dimensionalidade.
cols.remove('sample_id')
cols.remove('diagnosis')
# Instanciando um PCA. O parametro n_components
# indica a quantidade de dimensoes que a base
# original sera reduzida.
pca = PCA(n_components=2, whiten=True)
# Aplicando o pca na base breast_cancer.
# O atributo 'values' retorna um numpy.array
# de duas dimensões (matriz) contendo apenas
# os valores numericos do DataFrame.
dados_pca = pca.fit_transform(dados[cols].values)
# O metodo fit_transform retorna outro numpy.array
# de dimensao numero_objetos x n_components.
# Apos isso, instancia-se um novo DataFrame contendo
# a base de dados original com dimensionalidade
# reduzida.
29
https://arxiv.org/abs/1404.1100
https://arxiv.org/abs/1404.1100
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
dados_pca = pd.DataFrame(dados_pca,
columns=['comp1', 'comp2'])
3.8.2. Filtragem por limiar de variância
A filtragem por limiar de variância consiste basicamente em manter apenas os atributos
da base de dados original que possuam variância acima de um valor pré-estabelecido. O
código a seguir ilustra o funcionamento da mesma em Python.
import pandas
dados = pd.read_csv('breast_cancer.csv')
# Obtendo os nomes das colunas.
cols = list(dados.columns)
# Removendo colunas que nao serao inclusas
# na reducao de dimensionalidade.
cols.remove('sample_id')
cols.remove('diagnosis')
# Instanciando um VarianceThreshold. Esta
# classe recebe apenas um parâmetro real,
# que indica o valor de limiar desejado.
var_thr = VarianceThreshold(1.0)
# Selecionando apenas os atributos com
# variância maior ou igual a 1.
# 'dados_var_thr' sera um numpy.array com
# duas dimensoes.
dados_var_thr = var_thr.fit_transform(dados[cols].values)
Outros métodos mais sofisticados para seleção de atributos estão disponíveis na
biblioteca scikit-learn por meio do módulo sklearn.feature_selection3.
3.9. Exercícios
1. Observe que os códigos apresentados na Seção 3.3.2 nem sempre retornam uma
amostra com o tamanho desejado exato (dica: teste ambos os códigos com a base
Iris, por exemplo). Por que isso acontece? Escreva uma função que receba como
entrada um DataFrame, um tamanho de amostra desejado, o tipo de amostragem
estratificada a ser feita e retorne uma amostra estratificada com o tamanho exato
desejado. Quais as implicações desta nova função no resultado da amostragem?
2. Repare que o código apresentado na Seção 3.4 considera os exemplos de todas
as classes para o cálculo dos valores a serem inseridos no DataFrame. Escreva
3http://scikit-learn.org/stable/modules/feature_selection.html
30
http://scikit-learn.org/stable/modules/feature_selection.html
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
uma função que calcule e insira tais valores separadamente para cada classe. Ade-
mais, tal função deverá receber como parâmetro a medida que será calculada (média
ou mediana). Considerando a base de dados breast_cancer_missing.csv,
crie um arquivo CSV com o novo DataFrame gerado para cada caso.
3. Escreva uma função que receba como entrada um DataFrame, calcule a correla-
ção entre todos os seus atributos (através da função DataFrame.corr) e retorne
um novo DataFrame mantendo apenas um atributo dentre aqueles que possuem
correlação acima (ou abaixo, caso a correlação seja negativa) de um limiar infor-
mado como parâmetro.
4. Implemente a representação pelo código termômetro descrita na Seção 3.7.1 e apli-
que a mesma para todos os atributos qualitativos ordinais do conjunto de dados
Contraceptive Method Choice.
5. Repare que no código apresentado na Seção 3.8 o novo DataFramegerado possui
duas colunas nomeadas ’comp1’ e ’comp2’. Pesquise sobre o PCA e descreva
com as suas palavras o que esses dois novos atributos representam.
6. Aplique o PCA no conjunto de dados Iris, introduzido no capítulo anterior, com
n_components=2. Gere um scatter plot com diferentes cores para cada classe.
O que pode ser observado? Como o PCA pode auxiliar como ferramenta de visua-
lização?
Referências
[Abdi e Williams 2010] Abdi, H. e Williams, L. J. (2010). Principal component analysis.
Wiley interdisciplinary reviews: computational statistics, 2(4):433–459.
[Dunteman 1989] Dunteman, G. H. (1989). Principal components analysis. Number 69.
Sage.
[Faceli et al. 2011] Faceli, K., Lorena, A. C., Gama, J., e Carvalho, A. C. P. L. F. (2011).
Inteligência artificial: Uma abordagem de aprendizado de máquina. Rio de Janeiro:
LTC, 2:192.
[Garcia et al. 2013] Garcia, S., Luengo, J., Sáez, J. A., Lopez, V., e Herrera, F. (2013).
A survey of discretization techniques: Taxonomy and empirical analysis in supervised
learning. IEEE Transactions on Knowledge and Data Engineering, 25(4):734–750.
[Kotsiantis e Kanellopoulos 2006] Kotsiantis, S. e Kanellopoulos, D. (2006). Discreti-
zation techniques: A recent survey. GESTS International Transactions on Computer
Science and Engineering, 32(1):47–58.
[Lim et al. 2000] Lim, T.-S., Loh, W.-Y., e Shih, Y.-S. (2000). A comparison of predic-
tion accuracy, complexity, and training time of thirty-three old and new classification
algorithms. Machine learning, 40(3):203–228.
31
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
[Mangasarian et al. 1995] Mangasarian, O. L., Street, W. N., e Wolberg, W. H. (1995).
Breast cancer diagnosis and prognosis via linear programming. Operations Research,
43(4):570–577.
[Street et al. 1992] Street, W. N., Wolberg, W. H., e Mangasarian, O. L. (1992). Nuclear
feature extraction for breast tumor diagnosis.
32
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
Capítulo
4
Análise de agrupamento de dados
A análise de agrupamento de dados é um dos principais problemas exploratórios
investigados em mineração de dados. Ao longo das últimas décadas, diversos algoritmos
e formulações foram propostos para os mais variados tipos de aplicação. Neste capítulo
são apresentadas e discutidas alguns dos principais algoritmos da área, bem como alguns
dos critérios para validação das soluções encontradas por eles. Portanto, esta parte está
organizada conforme segue. Na Seção 4.1 será descrito o problema de agrupamento de
dados. Na Seção 4.2 será discutido ... Na Seção 4.3 serão introduzidos os três algoritmos
de agrupamento hierárquico mais conhecidos. Na Seção 4.4 será apresentado o algoritmo
k-means. Na Seção 4.5 será explicado o algoritmo DBSCAN. Por fim, na Seção 4.7, serão
propostos alguns exercícios.
4.1. Aprendizado de máquina não supervisionado
O problema de AM não supervisionado consiste em trabalhar sobre um conjunto de da-
dos, sem utilzar rótulos ou qualquer tipo de informação (ou supervisão) sobre como as
instâncias devem ser manipuladas [Zhu e Goldberg 2009]. Em outras palavras, o con-
junto de dados tratado consiste apenas de instâncias sem qualquer informação de ró-
tulo (ou classe) conhecida a priori. Algumas das principais tarefas neste cenário são
[Zhu e Goldberg 2009]: detecção de novidades, redução de dimensionalidade e agrupa-
mento de dados, sendo esta última de interesse do presente capítulo.
Em suma, considerando um conjunto de dados composto por n objetos descritos
por m características, o problema de agrupamento de dados consiste em segmentar esse
conjunto em k grupos, com k << n, de modo que objetos contidos em um mesmo grupo
sejam similares entre si e dissimilares de objetos contidos nos demais grupos, segundo
alguma medida de (dis)similaridade que leva em conta as m características disponíveis
[Jain e Dubes 1988, Tan et al. 2006].
É importante salientar que não há uma definição universal para o que constitui
um grupo [Everitt et al. 2001, Faceli et al. 2011], sendo esta dependente do algoritmo e
aplicação estudados. Ademais, diversos algoritmos de agrupamento foram propostos ao
longo das últimas décadas, baseadas em diferentes suposições. Por exemplo, o algo-
ritmo k-means, amplamente conhecido e utilizado, foi proposto há mais de cinco décadas
[Jain 2010].
Portanto, o presente capítulo possui um caráter totalmente introdutório, sem o ob-
33
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
jetivo de cobrir de maneira ampla o campo de análise de agrupamento de dados. Assim,
apenas os algoritmos clássicos que buscam por partições rígidas nos dados serão discuti-
dos. Formalmente, dado um conjunto X = {x1, · · · ,xn}, uma partição rígida consiste em
uma coleção de subconjuntos C = {C1, · · · ,Ck}, satisfazendo as seguintes propriedades
[Xu e Wunsch 2005]:
• C1∪C2∪·· ·∪Ck = X ;
• Ci 6= /0, ∀i ∈ [1,k]; e
• Ci∩C j = /0, ∀i, j ∈ [1,k] e i 6= j.
Exercício 4.1. Uma partição rígida, conforme previamente definida, é comumente refe-
renciada na literatura como agrupamento particional exclusivo. Outros tipos de partições
encontradas na literatura são: probabilístico, fuzzy ou particional não exclusivo. No pri-
meiro caso, cada objeto possuirá uma probabilidade de pertencer a cada grupo com a
restrição de que a soma de suas probabilidades é igual a 1. No segundo caso, cada ob-
jeto tem um grau de pertinência no intervalo [0,1] para cada grupo. Por fim, no terceiro
caso, cada objeto pode ser incluído em mais de um grupo. Escreva, para cada cenário,
as respectivas propriedades, de modo similar ao apresentado anteriormente para o caso
particional exclusivo.
4.2. Medidas de (dis)similaridade
A definição de uma medida de (dis)similaridade para um problema de agrupamento de
dados é de grande importância, uma vez que ela será uma das principais responsáveis
por definir a estrutura de grupos produzida. A escolha de uma medida é uma decisão
importante e deve levar em conta diversos fatores. Dentre eles, um dos mais importantes
envolve os tipos de atributos à disposição (quantitativo, qualitativo nominal, qualitativo
ordinal, misto etc.).
Boa parte das medidas de dissimilaridade para objetos com atributos quantitativos
são baseadas na distância de Minkowski. Considerando dois objetos xi e x j, essa distância
é definida como:
d(xi,x j) =
( m
∑
l=1
|xil− x jl|p
) 1
p
. (1)
A escolha do valor de p define variações para essa medida. Dentre elas, os três casos mais
conhecidos são [Faceli et al. 2011]:
• Distância Manhattan (p = 1):
d(xi,x j) =
m
∑
l=1
|xil− x jl|; (2)
34
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
• Distância euclidiana (p = 2):
d(xi,x j) =
√
m
∑
l=1
(xil− x jl)2; (3)
• Distância de Chebyshev (p = ∞):
d(xi,x j) = max
1≤l≤m
|xil− x jl|. (4)
Por sua vez, dentre as principais medidas de similaridade, pode-se citar a correla-
ção de Pearson e o cosseno entre dois vetores [Faceli et al. 2011].
Para atributos qualitativos, uma das medidas de dissimilaridade mais conhecidas
é a distância de Hamming, definida como:
d(xi,x j) =
m
∑
l=1
I(xil = x jl), (5)
sendo I(·) a função indicadora, a qual retorna valor igual a um quando a condição passada
a ela é verdadeira e zero, caso contrário.
Em Python, diversas medidas de distância podem ser calculadas para um conjunto
de dados por meio da função pdist do módulo scipy.spatial.distance1. Um
exemplo de código é apresentado a seguir.
from scipy.spatial.distance import pdist, squareform
import pandas
# carregando iris.csv sem a coluna que contem
# os rotulos das classes
dados = pandas.read_csv('iris.csv', usecols=[0, 1, 2, 3])
# O metodo 'pdist' recebe como entrada um numpy.array.
# O atributo 'values' de um pandas.DataFrame retorna
# seus valores em tal formato.
dados = dados.values
# 'pdist' calcula as distancias entre todos os pares
# possiveis de objetos. O parametro 'metric' define
# qual medida de (dis)similaridade sera calculada.
distancias= pdist(dados, metric='euclidean')
# O metodo 'pdist' retorna um numpy.array contendo
# n * (n - 1) / 2 elementos. Para transforma-lo em
1https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.
distance.pdist.html
35
https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.pdist.html
https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.pdist.html
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
# um numpy.array de dimensao n x n pode-se utilizar
# o metodo 'squareform'.
distancias = squareform(distancias)
Exercício 4.2. Considerando os três casos da distância de Minkowski apresentados
nesta seção (p = 1, p = 2 e p = ∞), comente, para cada um, qual o formato da super-
fície gerada por todos os possíveis objetos equidistantes a um objeto central.
4.3. Algoritmos hierárquicos
Enquanto algoritmos particionais geram apenas uma partição, algoritmos hierárquicos
produzem uma sequência de partições rígidas aninhadas, cada uma contendo uma quanti-
dade diferente de grupos. Esses algoritmos podem seguir duas abordagens [Faceli et al. 2011]:
1. Abordagem aglomerativa: o algoritmo inicia com n grupos, cada um formado por
um objeto diferente do conjunto de dados. Em cada passo, os dois grupos mais
próximos, segundo algum critério pré-estabelecido, são unidos. Desse modo, o
procedimento é repetido até que reste apenas um único grupo contendo todos os
objetos.
2. Abordagem divisiva: o algoritmo inicia com apenas um grupo contendo todos os
objetos. Em cada passo, algum dos grupos é dividido em dois novos grupos, se-
gundo algum critério pré-estabelecido. Todo o procedimento é realizado até que
sejam formados n grupos, cada um contendo apenas um objeto do conjunto de da-
dos original.
Os algoritmos hierárquicos clássicos da literatura são baseados na abordagem
aglomerativa e podem ser sumarizados pelo Algoritmo 1.
Algoritmo 1: agrupamento aglomerativo hierárquico.
Entrada: matriz de dissimilaridade S ∈ Rn×n
Saída : agrupamento hierárquico de S
1 enquanto todos os objetos não estiverem em um único grupo faça
2 atualizar as distâncias entre todos os pares possíveis de grupos;
3 encontrar os dois grupos Ci e C j mais próximos;
4 unir Ci e C j em um novo grupo;
5 fim
À execução do passo 3 do algoritmo acima dá-se o nome de linkage (ligação).
A principal diferença entre vários dos algoritmos aglomerativos está no modo como a
atualização das distâncias entre os grupos é feita (passo 2). Os métodos mais conhecidos
são:
• Single-linkage: a distância d(Ci,C j) entre dois grupos é dada pela distância mínima
36
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
entre os seus objetos. Ou seja:
d(Ci,C j) = min
xa∈Ci
xb∈C j
d(xa,xb). (6)
• Complete-linkage: a distância d(Ci,C j) entre dois grupos é dada pela distância má-
xima entre os seus objetos. Ou seja:
d(Ci,C j) = max
xa∈Ci
xb∈C j
d(xa,xb). (7)
• Average-linkage: a distância d(Ci,C j) entre dois grupos é dada pela distância média
entre os objetos de diferentes grupos. Ou seja:
d(Ci,C j) =
1
|Ci||C j| ∑
xa∈Ci
xb∈C j
d(xa,xb). (8)
Por fim, uma das principais vantagens de algoritmos de agrupamento hierárquicos
advém da representação dos seus resultados por meio de dendrogramas. Um dendrograma
é uma representação gráfica em formato de árvore que apresenta a hierarquia de partições
obtidas. Na Figura 4.1 é apresentado um exemplo de dendrograma, utilizando o método
complete-linkage, para o conjunto de dados artificial blobs.csv, fornecido como ma-
terial suplementar, que contém 20 instâncias separadas em três grupos correspondentes
a três distribuições normais multivariadas. No eixo horizontal é apresentado o índice de
cada objeto. No eixo vertical é apresentado o valor de distância quando cada par de ob-
jetos foi unido. As possíveis partições são definidas por meio de cortes no dendrograma.
Um exemplo de corte corresponde à linha horizontal pontilhada2.
Em Python, a biblioteca SciPy fornece as implementações dos três algoritmos de
agrupamento hierárquico citados anteriormente, além de uma função para a plotagem de
dendrogramas. Um exemplo de código, utilizando o conjunto blobs.csv é apresentado
a seguir.
from scipy.cluster.hierarchy import linkage
from scipy.cluster.hierarchy import fcluster
from scipy.cluster.hierarchy import dendrogram
from matplotlib import pyplot
import pandas
# carregando blobs.csv sem a coluna 'label'
dados = pandas.read_csv('blobs.csv', usecols=[0, 1])
# O metodo 'linkage' recebe como entrada um numpy.array.
2Neste exemplo, o corte sugerido segmenta os três grupos corretamente.
37
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
Figura 4.1: Exemplo de dendrograma para um conjunto de dados gerado a partir de três
distribuições normais multivariadas.
# O atributo 'values' de um pandas.DataFrame retorna
# seus valores em tal formato.
dados = dados.values
# Aplicando o agrupamento hierarquico aos dados.
# O parametro 'method' define qual algoritmo sera
# utilizado.
# Varios metodos de agrupamento estao disponiveis. Para
# os exemplos deste material sera utilizado
# method='average', method='complete' ou
# method='single'.
# O parametro 'metric' define a medida de
# (dis)similaridade a ser utilizada. Para uma lista
# de medidas disponiveis recomenda-se a documentacao
# do metodo scipy.spatial.distance.pdist.
h = linkage(dados, method='complete', metric='euclidean')
# Para plotar o dendrograma utiliza-se o metodo
# 'dendrogram'.
dendrogram(h)
pyplot.show()
38
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
# O metodo fcluster recebe como entrada uma
# hierarquia gerada pelo metodo linkage e extrai
# grupos da mesma segundo algum criterio.
# Abaixo serao extraidos os grupos por meio de um limiar
# de distancia. Segundo o dendrograma da Figura
# 4.1, o qual foi gerado atraves do metodo
# complete-linkage, um limiar de 7.5 parece ser
# adequado.
# A variavel 'rotulos' sera um numpy.array
# onde o valor contido em rotulos[i] indica o rotulo
# do objeto i.
rotulos_dist = fcluster(h, t=7.5, criterion='distance')
# Caso o criterio escolhido seja o numero de
# grupos, o metodo fcluster estima sozinho um valor
# de distancia de modo que t grupos sejam formados.
# Por exemplo, para extrair 3 grupos:
rotulos_k = fcluster(h, t=3, criterion='maxclust')
Exercício 4.3. Considerando um conjunto de dados X = {x1, · · · ,x5} e a matriz de dis-
tâncias 
0 3 10 1 4
3 0 2 2 3
10 2 0 8 8
1 2 8 0 8
4 3 8 8 0

Nessa matriz, cada elemento ai j indica a distância entre xi e x j. Aplique à matriz
os três algoritmos hierárquicos descritos nesta seção e descreva, de maneira detalhada, os
cálculos realizados por cada um deles em cada passo.
4.4. Algoritmo k-means
O algoritmo k-means busca por uma partição que minimize a soma dos erros quadráticos
(SSE, do inglês, sum of squared errors) entre os objetos de um conjunto de dados e o
centróide dos seus respectivos grupos [Jain 2010]. A medida SSE é definida como:
SSE =
k
∑
i=1
∑
x j∈Ci
d(x j, x̄Ci)
2, (9)
sendo d(·, ·) a distância euclidiana e x̄Ci o centróide de um grupo Ci, calculado como:
x̄Ci =
1
|Ci| ∑
x j∈Ci
x j. (10)
Em [Drineas et al. 2004] é provado que o problema de minimização da SSE é NP-
difícil, inclusive quando k = 2. Portanto, o algoritmo k-means é um procedimento de
39
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
otimização com inicialização aleatória que garante a convergência para um mínimo local
da Equação (9). O mesmo é apresentado no Algoritmo 2.
Algoritmo 2: k-means.
Entrada: conjunto de dados X ∈ Rn×m e o número de grupos k
Saída : agrupamento particional de X em k grupos
1 gerar k centróides aleatoriamente;
2 repita
3 calcular a distância entre cada objeto x j e cada centróide x̄Ci;
4 atribuir cada objeto x j ao grupo Ci com centróide mais próximo;
5 recalcular o centróide de cada grupo conforme a Equação (10);
6 até que um critério pré-definido seja atingido ouque os objetos não mudem
de grupo;
Um exemplo da execução do k-means para o conjunto blobs.csv é apresentado
abaixo.
from sklearn.cluster import k_means
import pandas
# carregando blobs.csv sem a coluna 'label'
dados = pandas.read_csv('blobs.csv', usecols=[0, 1])
# O metodo 'k_means' recebe como entrada um numpy.array.
# O atributo 'values' de um pandas.DataFrame retorna
# seus valores em tal formato.
dados = dados.values
# Executa o algoritmo k-means.
# 'n_clusters' indica o numero de grupos buscados.
# 'init' indica o tipo de inicializacao.
# 'n_init' indica a quantidade de vezes que o algoritmo
# sera executado. Dentre todas as 'n_init' execucoes,
# eh retornada aquela com o menor valor de sse.
# O metodo retorna tres valores: o primeiro, um
# um numpy.array com k linhas e m colunas,
# contendo os centroides finais; o segundo, um
# numpy.array contendo os rotulos de cada objeto; e,
# por fim, o valor de sse da solucao retornada.
centroides, rotulos, sse = k_means(dados,
n_clusters=3,
init='random',
n_init=100)
Exercício 4.4. Considerando a função objetivo do k-means, apresentada na Equação (9):
40
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
a Discuta quais serão as características dos grupos encontrados por esse algoritmo.
b Descreva em quais cenários o k-means não é capaz de produzir agrupamentos de
boa qualidade.
c Um dos maiores problemas do algoritmo k-means decorre da influência de outliers
em sua performance. Em tais casos, o valor de SSE sofrerá um grande incremento
e os centróides finais não serão tão representativos quanto seriam com a ausência
dos outliers [Tan et al. 2006]. Uma alternativa para isso seria utilizar o algoritmo
k-medians, o qual substitui a média pela mediana no cálculo de um centróide3.
Embora resultados mais robustos podem ser encontrados, o k-medians possui uma
grande desvantagem em relação ao k-means. Enuncie e discuta tal desvantagem.
4.5. Algoritmo DBSCAN
O algoritmo DBSCAN (Density-Based Spatial Clustering of Applications with Noise)
busca por grupos definidos como regiões com alta densidade de objetos, separados por
regiões de baixa densidade [Tan et al. 2006]. Uma das principais vantagens desse algo-
ritmo advém do fato de não ser necessário informar previamente o número desejado de
grupos. Para isso, ele se baseia na classificação de cada objeto do conjunto de dados em
uma dentre 3 categorias [Ester et al. 1996]:
• Objeto central: todo objeto xi contendo uma quantidade de objetos vizinhos, con-
tando ele próprio, maior ou igual a um parâmetro MinPts. Um vizinho é determi-
nado como todo objeto separado por, no máximo, uma distância ε de xi.
• Objeto de borda: todo objeto que não satisfaz as condições para objeto central,
mas que pertence à vizinhança de um objeto central.
• Ruído: todo objeto que não pertence a nenhuma das duas categorias anteriores.
A Figura 4.2 ilustra um exemplo da classificação de um conjunto de objetos con-
siderando MinPts = 3. Nela, os objetos vermelhos são centrais, os amarelos de borda e o
azul é ruído. Os círculos em torno de cada objeto denotam o raio ε que define as vizinhan-
ças. O Algoritmo 3 apresenta o pseudocódigo do algoritmo DBSCAN [Tan et al. 2006].
3É provado que este novo algoritmo minimiza a distância L1, também conhecida como distância de
Manhattan.
41
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
Figura 4.2: Exemplo de classificação de objetos feita pelo DBSCAN com MinPts = 3
(Fonte: [Chire 2011]4).
Algoritmo 3: DBSCAN.
Entrada: conjunto de dados X ∈ Rn×m, um raio ε e a quantidade mínima de
vizinhos para um objeto ser considerado central MinPts
Saída : agrupamento particional de X
1 rotular cada objeto como central, borda ou ruído;
2 remover objetos rotulados como ruído;
3 ligar todos os objetos rotulados como centrais que estejam dentro de um raio
ε uns dos outros;
4 definir cada componente de objetos centrais conectados como um grupo;
5 inserir cada objeto rotulado como borda ao grupo de algum dos seus objetos
centrais associados, resolvendo empates que venham a ocorrer;
Por fim, um exemplo em Python da aplicação do algoritmo DBSCAN a um con-
junto de dados é apresentado no código a seguir.
from sklearn.cluster import dbscan
import pandas
# carregando blobs.csv sem a coluna 'label'
dados = pandas.read_csv('blobs.csv', usecols=[0, 1])
# O metodo 'dbscan' recebe como entrada um numpy.array.
# O atributo 'values' de um pandas.DataFrame retorna
# seus valores em tal formato.
dados = dados.values
# Executa o algoritmo DBSCAN.
# 'eps' eh o parametro que define o raio de cada objeto.
# 'min_samples' indica a quantidade minima de objetos
# para considerar um objeto como central.
4A imagem original está disponível sob a licença CC-BY-SA 3.0 (https://creativecommons.
org/licenses/by-sa/3.0/).
42
https://creativecommons.org/licenses/by-sa/3.0/
https://creativecommons.org/licenses/by-sa/3.0/
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
# 'metric' define a medida de distancia a ser utilizada
# e, seu valor padrao consiste na distancia euclidiana.
# O metodo retorna dois valores:
# o primeiro eh um numpy.array contendo os indices
# dos objetos classificados como centrais;
# o segundo eh um numpy.array contendo o rotulo de
# grupo de cada objeto
objetos_centrais, rotulos = dbscan(dados,
eps=1.5,
min_samples=3)
Exercício 4.5. Uma das principais desvantagens do DBSCAN decorre de o mesmo não
ser capaz de identificar corretamente um agrupamento quando as densidades variam am-
plamente de grupo para grupo [Tan et al. 2006]. Por que isso acontece? Por que não é
possível contornar tal problema ao aumentar o valor de ε ou alterar o valor de MinPts?
4.6. Validação de agrupamentos
Para avaliar quão bons são os agrupamentos encontrados, uma medida de avaliação ou
validação deve ser utilizada. As medidas ou critérios de validação podem ser internos,
externos ou relativos. A seguir são apresentadas os principais critérios de validação utili-
zados para avaliar agrupamentos gerados por algoritmos de agrupamento de dados.
4.6.1. Critérios de validação relativa
"Qual dentre um conjunto de soluções de agrupamento melhor representa os dados?".
Esta é a pergunta que deve ser respondida por um critério de validação relativa de uma
maneira quantitativa [Jain e Dubes 1988]. Entretanto, é importante observar, conforme
será apresentado a seguir, que, assim como algoritmos de agrupamento, diferentes índi-
ces de validação relativa possuem diferentes suposições e viéses. Desse modo, cabe aos
envolvidos no processo de mineração de dados optarem por aqueles mais adequados na
etapa de validação.
4.6.1.1. Índice de Dunn (ID)
O ID é um critério de validação relativa baseado na ideia de compactação intra-grupo
e separação inter-grupos [Vendramin et al. 2010]. Em outras palavras, ele é apropriado
para identificar agrupamentos que contém grupos cujos objetos estão próximos entre si
e distantes de objetos contidos em outros grupos. O ID pode ser formalmente definido
como:
Dunn(C) = min
1≤ i, j ≤ k
i 6= j
{
d(Ci,C j)
max
1≤ l ≤ k
{D(Cl)}
}
, (11)
sendo d(Ci,C j) e D(Cl) definidos, respectivamente:
d(Ci,C j) = min
xa∈Ci
xb∈C j
d(xa,xb), (12)
43
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
D(Cl) = max
xa,xb∈Cl
d(xa,xb). (13)
Portanto, valores altos dessa medida indicam soluções que obedecem seu viés. Ademais,
ao generalizar os cálculos de d(Ci,C j) e D(Cl), variantes dessa medida podem ser geradas
[Vendramin et al. 2010]. Dentre elas, 17 são descritas em [Bezdek e Pal 1998].
A implementação do ID não está disponível nas bibliotecas utilizadas neste mate-
rial. Ela será exigida como exercício na Seção 4.7.
4.6.1.2. Largura de silhueta (LS)
Assim como o ID, a LS baseia-se nos conceitos de compactação intra-grupo e separação
inter-grupos. A silhueta de um objeto individual xi pertencente a um grupo Cl (S(xi)) é
definida por:
S(xi) =
b(xi)−a(xi)
max{a(xi),b(xi)}
(14)
sendo a(xi) e b(xi) calculadoscomo:
a(xi) =
1
|Cl|−1 ∑
x j ∈Cl
d(xi,x j), (15)
b(xi) = min
1≤ h≤ k
h 6= l
{
1
|Ch| ∑
x j ∈Ch
d(xi,x j)
}
. (16)
Com isso, a LS é definida como a silhueta média entre todos os objetos do conjunto de
dados. Ou seja:
LS(C) =
1
n
n
∑
i=1
S(xi). (17)
A LS assume valores no intervalo [−1,1]. A melhor partição possível segundo
esse critério é aquela que atinge LS(C) = 1.
Um exemplo da LS em Python é apresentado a seguir.
from sklearn.cluster import k_means
from sklearn.metrics import silhouette_score
import pandas
# carregando blobs.csv sem a coluna 'label'
dados = pandas.read_csv('blobs.csv', usecols=[0, 1])
dados = dados.values
# Executando o algoritmo k-means.
centroides, rotulos, sse = k_means(dados,
44
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
n_clusters=3,
init='random',
n_init=100)
# Calculando a largura de silhueta.
# O primeiro parametro eh o conjunto de dados estudado.
# O segundo engloba os rotulos encontrados por um algoritmo.
# O parametro 'metric' indica a medida de distancia
# utilizada. No caso do algoritmo k-means, sera informado
# 'sqeuclidean' que eh a distancia euclidiana ao
# quadrado.
s = silhouette_score(dados, rotulos, metric='sqeuclidean')
Exercício 4.6.1. Os dois índices relativos apresentados nesta parte não são adequados
para validar o algoritmo DBSCAN. Por que?
Para auxiliar na resposta, aplique o algoritmo DBSCAN no conjunto de dados
moons.csv, fornecido como material suplementar (observe que pode ser necessário
ajustar o valor do parâmetro ε para se obter um bom resultado). Gere um scatter plot para
a base de dados com cores para seus rótulos originais e um scatter plot com cores para
os rótulos encontrados pelo DBSCAN. Calcule o valor o ID e o LS para a solução gerada
pelo DBSCAN. Compare os scatter plots com os valores dos índices.
4.6.2. Critérios de validação interna
Índices de validação interna medem o grau em que uma solução de agrupamento é justi-
ficada com base apenas no conjunto de dados original ou em uma matriz de similaridades
ou dissimilaridades calculadas a partir do mesmo [Jain e Dubes 1988]. Assim, um índice
de validação interna pode ser visto como o grau de concordância entre um agrupamento
encontrado por um algoritmo de agrupamento e o próprio conjunto de dados.
Muitas vezes, índices de validação interna são utilizados como função objetivo a
ser otimizada por algoritmos de agrupamento. Como exemplo, tem-se a relação entre o
SSE (Equação 9) e o k-means.
Exercício 4.6.2. Em muitas aplicações, o SSE é utilizado como critério de qualidade na
seleção de uma dentre várias soluções de agrupamento disponíveis. Desse modo, aquela
com valor mínimo de SSE é normalmente escolhida. Com base nisso, responda:
a Para quais formatos geométricos de grupos existentes em um conjunto de dados
esse critério é adequado?
b O SSE normalmente não é adequado para escolher uma dentre várias soluções com
diferentes números de grupos. Por que? (Dica: aplique o k-means em diversos
conjuntos de dados, por exemplo blobs.csv e Iris, considerando vários valores
para o números de grupos e observe o comportamento do SSE).
45
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
4.6.3. Critérios de validação externa
Critérios de validação externa avaliam o grau de concordância entre duas soluções de
agrupamento de dados [Jain e Dubes 1988]. Em várias aplicações práticas, uma das par-
tições comparadas consistirá em uma solução obtida por algum algoritmo, denotada por
Cob = {Cob
1 , · · · ,Cob
k }, enquanto que a partição restante representará uma solução de refe-
rência para o conjunto de dados estudado, denotada por Cre f = {Cre f
1 , · · · ,Cre f
q }.
Vários dos critérios de similaridade para a comparação de partições baseiam-se
na contagem de pares de objetos. Assim, nesse tipo de abordagem, as seguintes variáveis
são definidas:
• a11: quantidade de pares de objetos em um mesmo grupo em Cob e Cre f ;
• a10: quantidade de pares de objetos em um mesmo grupo em Cob mas em grupos
diferentes em Cre f ;
• a01: quantidade de pares de objetos em grupos diferentes em Cob mas em um
mesmo grupo em Cre f ;
• a00: quantidade de pares de objetos pertencentes a grupos diferentes tanto em Cob
quanto em Cre f .
A seguir, cinco dos índices de validação externa mais conhecidos para avaliação
de agrupamentos particionais exclusivos serão apresentados. O respectivo código será
apresentado ao final desta seção.
4.6.3.1. Índice Rand (IR)
O critério IR calcula a proporção de acordos no agrupamento de pares de objetos entre
duas partições (a11 e a00) em relação ao total de pares possíveis de objetos. Ou seja:
IR(Cob,Cre f ) =
a11 +a00
a11 +a10 +a01 +a11
=
a11 +a00(n
2
) . (18)
Esta medida retorna valores no intervalo [0,1], com valores mais altos indicando
uma maior similaridade entre duas partições.
4.6.3.2. Índice Jaccard (IJ)
O critério IJ calcula a proporção de pares de objetos agrupados conjuntamente em Cob
e Cre f em relação à quantidade de pares de objetos em um mesmo grupo em Cob ou em
Cre f . Ou seja:
IJ(Cob,Cre f ) =
a11
a11 +a01 +a10
. (19)
46
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
Assim como o IR, o IJ está contido no intervalo [0,1] com valores mais altos
apontando uma maior concordância entre Cob e Cre f .
4.6.3.3. Índice Rand Ajustado (IRA)
Um dos problemas do IR tradicional advém da dificuldade em determinar "quão bom"ou
"quão alto"é um valor obtido na comparação de duas partições. Essa dificuldade existe
pois, ao comparar duas partições geradas aleatoriamente, valores inesperadamente altos
podem ocorrer. Em tais situações, torna-se necessário ajustar o índice para aleatoriadade.
Desse modo, a versão ajustada do IR obedece a definição a seguir:
IRA(Cob,Cre f ) =
IR(Cob,Cre f )−E[IR(Cob,Cre f )]
max{IR}−E[IR(Cob,Cre f )]
, (20)
onde E[IR(Cob,Cre f )] indica o valor esperado do IR ao comparar as partições Cob e Cre f
e max{IR} indica o valor máximo atingido por essa medida (ou seja, max{IR}= 1).
Critérios de validação externa, quando ajustados para aleatoriedade, assumem va-
lores no intervalo (−∞,1]. Assim, valores positivos indicam que a similaridade entre Cob
e Cre f é maior do que o valor esperado ao comparar agrupamentos gerados aleatoriamente
[Horta e Campello 2015]. Em [Hubert e Arabie 1985], o IRA é proposto assumindo uma
distribuição hipergeométrica como modelo nulo. O cálculo do IRA é dado por:
IRA(Cob,Cre f ) =
a− (a+c)(a+b)
a+b+c+d
(a+c)+(a+b)
2 − (a+c)(a+b)
a+b+c+d
. (21)
4.6.3.4. Informação Mútua (IM)
A medida IM mede a informação compartilhada entre Cob e Cre f . Em outras palavras,
essa medida quantifica a quantidade de informação sobre Cre f obtida através de Cob e
vice-versa. A IM é calculada como:
IM(Cob,Cre f ) =
|Cob|
∑
i=1
|Cre f |
∑
j=1
P(i, j) log
(
P(i, j)
Pob(i) Pre f ( j)
)
(22)
sendo P(i, j), Pob(i) e Pre f ( j) definidos, respectivamente, por:
P(i, j) =
|Cob
i ∩Cre f
j |
n
, (23)
Pob(i) =
|Cob
i |
n
, (24)
Pre f ( j) =
|Cre f
j |
n
. (25)
47
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
O valor mínimo para a IM é igual a zero, sendo que valores mais altos indicam uma
melhor concordância entre as partições comparadas. Uma das principais desvatangens
dessa medida é a ausência de um limitante superior. Para fins comparativos, uma versão
normalizada da mesma para o intervalo [0,1] é mais adequada [Strehl e Ghosh 2002].
Uma das variações mais conhecidas (IMN) é apresentada na próxima subseção.
4.6.3.5. Informação Mútua Normalizada (IMN)
Uma das normalizações mais conhecidas da IM pode ser definida como:
IMN(Cob,Cre f ) =
IM(Cob,Cre f )√
H(Cob) H(Cre f )
, (26)
onde H(·) representa a entropia de um agrupamento, calculada por meio da fórmula a
seguir:
H(C) =−
|C|
∑
i=1
P(i) log(P(i)). (27)
Desse modo, a IMN está contida no intervalo [0,1], com valores mais altos apontando
uma maior similaridade entre as partições comparadas.
4.6.3.6. Exemplos
A seguir é apresentadoum exemplo de código em Python para o cálculo do IRA, IM e
IMN. Os critérios IR e o IJ não estão disponíveis nas bibliotecas utilizadas neste material.
Portanto, as respectivas implementações serão exigidas como exercício na Seção 4.7.
from sklearn.cluster import k_means
from sklearn.metrics import adjusted_rand_score
from sklearn.metrics import mutual_info_score
from sklearn.metrics import normalized_mutual_info_score
import pandas
# carregando blobs.csv sem a coluna 'label'
dados = pandas.read_csv('blobs.csv', usecols=[0, 1])
dados = dados.values
# Executando o algoritmo k-means.
centroides, rotulos_kmeans, sse = k_means(dados,
n_clusters=3,
init='random',
n_init=100)
# Calculando o IRA.
ira = adjusted_rand_score(rotulos_dados, rotulos_kmeans)
48
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
# Calculando o IM.
im = mutual_info_score(rotulos_dados, rotulos_kmeans)
# Calculando o IMN.
imn = normalized_mutual_info_score(rotulos_dados,
rotulos_kmeans)
Exercício 4.6.3. O IR possui um forte viés para a comparação de partições contendo
maiores números de grupos. Para tal caso, um dos termos irá dominar o valor calculado
para a medida. Responda:
a Qual é o termo que domina o valor do IR? Por que isso acontece?
b Em qual cenário o valor de IR será igual a zero?
4.7. Exercícios
1. Uma extensão direta do algoritmo k-means, capaz de gerar, divisivamente, uma
hierarquia de grupos, é conhecida por bisecting k-means. Tal extensão é descrita na
Seção 8.2.3 do Capítulo 8 do livro de [Tan et al. 2006]5. Implemente o bisecting
k-means, de modo que retorne a hierarquia de grupos produzida, e aplique-o ao
conjunto de dados Iris.
2. Implemente o ID, o IR e o IJ. Aplique o k-means para os conjuntos blobs.csv,
moons.csv e Iris. Calcule os valores dos índices para os resultados do k-means.
Referências
[Bezdek e Pal 1998] Bezdek, J. C. e Pal, N. R. (1998). Some new indexes of cluster
validity. IEEE Transactions on Systems, Man, and Cybernetics, Part B (Cybernetics),
28(3):301–315.
[Chire 2011] Chire (2011). DBSCAN Illustration. https://commons.
wikimedia.org/wiki/File:DBSCAN-Illustration.svg. Acesso em:
05 set. 2017.
[Drineas et al. 2004] Drineas, P., Frieze, A., Kannan, R., Vempala, S., e Vinay, V. (2004).
Clustering large graphs via the singular value decomposition. Machine learning,
56(1):9–33.
[Ester et al. 1996] Ester, M., Kriegel, H.-P., Sander, J., Xu, X., et al. (1996). A density-
based algorithm for discovering clusters in large spatial databases with noise. In Kdd,
volume 96, pages 226–231.
5Este capítulo está disponível gratuitamente em http://www-users.cs.umn.edu/~kumar/
dmbook/ch8.pdf.
49
https://commons.wikimedia.org/wiki/File:DBSCAN-Illustration.svg
https://commons.wikimedia.org/wiki/File:DBSCAN-Illustration.svg
http://www-users.cs.umn.edu/~kumar/dmbook/ch8.pdf
http://www-users.cs.umn.edu/~kumar/dmbook/ch8.pdf
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
[Everitt et al. 2001] Everitt, B., Landau, S., Leese, M., e Stahl, D. (2001). Cluster analy-
sis. 2001. Arnold, London.
[Faceli et al. 2011] Faceli, K., Lorena, A. C., Gama, J., e Carvalho, A. C. P. L. F. (2011).
Inteligência artificial: Uma abordagem de aprendizado de máquina. Rio de Janeiro:
LTC, 2:192.
[Horta e Campello 2015] Horta, D. e Campello, R. J. G. B. (2015). Comparing hard and
overlapping clusterings. Journal of Machine Learning Research, 16:2949–2997.
[Hubert e Arabie 1985] Hubert, L. e Arabie, P. (1985). Comparing partitions. Journal of
classification, 2(1):193–218.
[Jain 2010] Jain, A. K. (2010). Data clustering: 50 years beyond k-means. Pattern
recognition letters, 31(8):651–666.
[Jain e Dubes 1988] Jain, A. K. e Dubes, R. C. (1988). Algorithms for clustering data.
Prentice-Hall, Inc.
[Strehl e Ghosh 2002] Strehl, A. e Ghosh, J. (2002). Cluster ensembles—a knowledge
reuse framework for combining multiple partitions. Journal of machine learning rese-
arch, 3(Dec):583–617.
[Tan et al. 2006] Tan, P.-N. et al. (2006). Introduction to data mining. Pearson Education
India.
[Vendramin et al. 2010] Vendramin, L., Campello, R. J., e Hruschka, E. R. (2010). Rela-
tive clustering validity criteria: A comparative overview. Statistical analysis and data
mining: the ASA data science journal, 3(4):209–235.
[Xu e Wunsch 2005] Xu, R. e Wunsch, D. (2005). Survey of clustering algorithms. IEEE
Transactions on neural networks, 16(3):645–678.
[Zhu e Goldberg 2009] Zhu, X. e Goldberg, A. B. (2009). Introduction to semi-
supervised learning. Synthesis lectures on artificial intelligence and machine learning,
3(1):1–130.
50
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
Capítulo
5
Classificação e regressão
Neste capítulo serão apresentados os principais conceitos relacionados a AM pre-
ditivo, que utilizam dados rotulados para a indução de modelos de classificação ou de
regressão.
O presente capítulo está organizado conforme segue. Na Seção 5.2 são apresen-
tados métodos de aprendizado supervisionado baseados em distâncias entre objetos. Na
Seção 5.3 são propostos exercícios para fixar os conceitos apresentados.
5.1. Aprendizado de máquina supervisionado
Em AM supervisionado, algoritmos são utilizados para induzir modelos preditivos por
meio da observação de um conjunto de objetos rotulados [Von Luxburg e Schölkopf 2008],
tipicamente referenciado como conjunto de treinamento. Os rótulos contidos em tal con-
junto correspondem a classes ou valores obtidos por alguma função desconhecida. Desse
modo, um algoritmo de classificação buscará produzir um classificador capaz de genera-
lizar as informações contidas no conjunto de treinamento, com a finalidade de classificar,
posteriormente, objetos cujo rótulo seja desconhecido.
Formalmente, um conjunto de dados de treinamento pode ser definido como uma
coleção de tuplas {(xi,yi)}n
i=1, onde, em cada tupla, xi indica um objeto descrito por m ca-
racterísticas e yi indica o rótulo correspondente a xi. Quando os valores de yi são definidos
por uma quantidade limitada de valores discretos, tem-se um problema de classificação.
Quando tais valores são contínuos, tem-se um problema de regressão.
5.2. Métodos baseados em distâncias entre objetos
Métodos baseados em distâncias adotam a ideia de que objetos que pertencem a uma
mesma classe possuem uma relação de proximidade no espaço de atributos considerados
[Faceli et al. 2011].
5.2.1. k-Nearest Neighbors (kNN)
O algoritmo kNN é um dos algoritmos de classificação mais conhecidos e fáceis de se
implementar na literatura de aprendizado de máquina e mineração de dados. Sua ideia
consiste em, dado um objeto desconhecido, procurar pelos k vizinhos mais próximos a
ele em um conjunto de dados previamente conhecido, segundo uma medida de distância
pré-estabelecida. A classe do novo objeto será assumida como o voto majoritário entre os
18
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
seus k vizinhos.
Devido à sua simplicidade, o kNN possui ampla utilização na solução de diver-
sos problemas do mundo real. Os pseudo-códigos para treinamento e teste do kNN são
apresentados, respectivamente, nos Algoritmos 1 e 2.
Algoritmo 1: Treinamento kNN.
Entrada: conjunto de treinamento T = {xi,yi}n
i=1, valor de k e uma medida
de distância d(·, ·)
Saída : classificador kNN
1 armazenar o conjunto de treinamento e o valor de k;
Algoritmo 2: Teste kNN.
Entrada: classificador kNN e um objeto x cuja classe é desconhecida
Saída : classe y atribuída a x
1 buscar pelos k objetos mais próximos a x no conjunto de dados de
treinamento do classificador kNN informado;
2 dentre os k vizinhos, determinar y como a classe mais frequente entre eles,
resolvendo possíveis empates de maneira arbitrária;
Em Python, pode-se executar o kNN conforme o código que pode ser visto a
seguir.
import pandas
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
# carregaa base de dados iris
dados = pandas.read_csv('iris.csv')
# para criar um classificador, precisaremos separar
# o DataFrame original em dois numpy.arrays:
# - O primeiro deles, bidimensional, contendo os objetos
# e os atributos;
# - O segundo deles, unidimensional, contendo apenas as
# classes representadas por valores inteiros;
# Para obter os objetos e seus atributos, procede-se
# conforme abaixo
colunas = dados.columns.drop('Name')
# obtem numpy.array bidimensional
X = dados[colunas].values
# Para obter as classes como inteiros, utilizamos
# a classe LabelEncoder da scikit-learn
19
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
le = LabelEncoder()
y = le.fit_transform(dados['Name'])
# train_test_split separa o conjunto de dados original
# aleatoriamente em treinamento e teste
# train_size indica a proporcao de objetos presentes
# no conjunto de treinamento (neste caso 70% dos objetos)
# caso deseje-se uma separacao estratificada, deve-se
# informar um parametro adicional stratify=y
X_treino, X_teste, y_treino, y_teste = \
train_test_split(X, y, train_size=0.7, test_size=0.3)
# instancia um knn
# n_neighbors indica a quantidade de vizinhos
# metric indica a medida de distancia utilizada
knn = KNeighborsClassifier(n_neighbors=5,
metric='euclidean')
# treina o knn
knn.fit(X_treino, y_treino)
# testa o knn com X_teste
# y_pred consistira em um numpy.array onde
# cada posicao contem a classe predita pelo knn
# para o respectivo objeto em X_teste
y_pred = knn.predict(X_teste)
Exercício 5.2.1. Discuta sobre os possíveis problemas que podem ocorrer com o kNN
nos cenários a seguir:
a Quando a escala e intervalo de valores entre os atributos é diferente. Por exemplo,
ao considerar atributos que indicam alguma medição em centímetros ou em metros.
b Quando a dimensionalidade dos objetos é alta1. Por exemplo, o que pode ocorrer
ao buscar pelos k vizinhos mais próximos quando a base de dados possui 50, 100,
200, 300 ou mais atributos?
5.3. Exercícios
1. O kNN pode também ser utilizado para problemas de regressão local [Mitchell 1997,
Faceli et al. 2011]. Com a scikit-learn esse tipo de tarefa pode ser realizada por
meio da classe KNeighborsRegressor2. Escreva um código que treine um
classificador kNN para regressão considerando os requisitos a seguir:
1Este é um problema, em aprendizado de máquina, conhecido como "maldição da dimensionalidade"
2http://scikit-learn.org/stable/modules/generated/sklearn.neighbors.
KNeighborsRegressor.html
20
http://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighborsRegressor.html
http://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighborsRegressor.html
Padilha, V. A. e Carvalho, A. C. P. L. F., Mineração de Dados em Python
• Para o treinamento utilize o arquivo regressao_treino.csv.
• Para o teste, utilize o arquivo regressao_teste.csv.
• Como medida de distância, considere a distância euclidiana.
• Teste o kNN com ponderação uniforme entre seus vizinhos (ou seja, consi-
derando weights=’uniform’) e pelo inverso das distâncias (isto é, utili-
zando weights=’distance’).
• Teste os seguintes valores para k: 3, 5, 10, 20.
Gere um scatter plot para os dados de treinamento utilizando como eixo horizontal
os valores dos exemplos e como eixo vertical os rótulos. Gere um line plot3 para
cada resultado das possíveis combinações entre ponderação e valor de k. Responda:
a Qual das duas ponderações parece ser mais adequada?
b Note que existem alguns pontos ruidosos no treinamento. Qual tipo de pon-
deração é mais influenciado por eles? Por que?
c Ao analisar os gráficos obtidos, responda qual função matemática gerou os
dados.
Referências
[Faceli et al. 2011] Faceli, K., Lorena, A. C., Gama, J., e Carvalho, A. C. P. L. F. (2011).
Inteligência artificial: Uma abordagem de aprendizado de máquina. Rio de Janeiro:
LTC, 2:192.
[Mitchell 1997] Mitchell, T. M. (1997). Machine learning. 1997. Burr Ridge, IL: Mc-
Graw Hill, 45(37):870–877.
[Von Luxburg e Schölkopf 2008] Von Luxburg, U. e Schölkopf, B. (2008). Statistical
learning theory: models, concepts, and results. arXiv preprint arXiv:0810.4752.
3https://matplotlib.org/users/pyplot_tutorial.html
21
https://matplotlib.org/users/pyplot_tutorial.html
Predictive	model	selection	and	evaluation
First	of	all,	we	do	all	necessary	imports	and	load	the	Breast	Cancer	Wisconsin	Diagnostic	dataset.
In	[1]:
import	numpy	as	np
import	pandas	as	pd
import	matplotlib.pyplot	as	plt
from	sklearn.preprocessing	import	LabelEncoder
from	sklearn.model_selection	import	train_test_split,	StratifiedKFold
from	sklearn.neighbors	import	KNeighborsClassifier
from	sklearn.metrics	import	accuracy_score,	zero_one_loss
from	sklearn.metrics	import	roc_curve,	auc
from	sklearn.datasets	import	load_breast_cancer
from	scipy.stats	import	wilcoxon,	friedmanchisquare,	rankdata
from	Orange.evaluation	import	compute_CD,	graph_ranks
DEFAULT_N_NEIGHBORS	=	5
#	Random	seed.	This	is	needed	to	make	all	results	reproducible.
seed	=	10
#	Loading	Breast	Cancer	dataset
breast_cancer	=	pd.read_csv('data/breast_cancer.csv',	index_col=0)
labels	=	'diagnosis'
breast_cancer.head()
Then,	we	encode	the	'diagnosis'	str	values	as	int	values.
In	[16]:
le	=	LabelEncoder()
breast_cancer[labels]	=	le.fit_transform(breast_cancer[labels])
Then,	we	write	a	simple	method	to	train,	test	and	return	a	kNN	classifier,	its	predicted	results,	its	accuracy	score	and	its	0-1	loss	for	a
dataset.
In	[3]:
def	knn_fit_predict_evaluate(X_train,	X_test,	y_train,	y_test,	k=DEFAULT_N_NEIGHBORS):
				knn	=	KNeighborsClassifier(n_neighbors=k,	weights='distance',	metric='euclidean')
				knn.fit(X_train,	y_train)
				y_pred	=	knn.predict(X_test)
				accuracy	=	accuracy_score(y_test,	y_pred)
				loss	=	zero_one_loss(y_test,	y_pred)
				return	knn,	y_pred,	accuracy,	loss
To	train	and	test	sklearn	classifiers,	we	will	need	the	data	as	numpy	arrays.	So,	we	can	extract	them	using	the	code	below.
Out[1]:
mean_radius mean_texture mean_perimeter mean_area mean_smoothness mean_compactness mean_concavity
sample_id
842302 17.99 10.38 122.80 1001.0 0.11840 0.27760 0.3001
842517 20.57 17.77 132.90 1326.0 0.08474 0.07864 0.0869
84300903 19.69 21.25 130.00 1203.0 0.10960 0.15990 0.1974
84348301 11.42 20.38 77.58 386.1 0.14250 0.28390 0.2414
84358402 20.29 14.34 135.10 1297.0 0.10030 0.13280 0.1980
5	rows	×	31	columns
In	[4]:
np.set_printoptions(precision=4,	suppress=True)
#	Extracting	Breast	Cancer	values	without	the	label	column.
X	=	breast_cancer.drop(labels,	axis=1).values
print('X:\n{}\n'.format(X))
#	Extracting	Breast	Cancer	labels.
y	=	breast_cancer[labels].values
print('y:\n{}\n'.format(y))
Simple	holdout
The	simple	holdout	method	consists	of	splitting	the	original	dataset	in	two	disjoint	subsets:
train:	which	contains	a	proportion	of	p	objects	from	the	original	dataset;
test:	which	contains	a	proportion	of	1 − p	objects	from	the	original	dataset.
The	aforementioned	split	can	be	performed	with	the	train_test_split	method.
In	[5]:
#	Splitting	the	original	dataset.
#	The	train	subset	will	contain	a	proportion	of	0.66	objects	from	the	original	dataset.
#	The	test	subset	will	contain	a	proportion	of	0.34	objects	from	the	original	dataset.
X_train,	X_test,	y_train,	y_test	=	train_test_split(X,	y,	test_size=0.34,	stratify=y,	random_state=seed)
#	Training,	testing	and	evaluating	a	kNN	classifier	with	simple	holdout.
knn,	y_pred,	accuracy,	loss	=	knn_fit_predict_evaluate(X_train,	X_test,	y_train,	y_test)
print('Accuracy:	{}'.format(accuracy))
print('0-1	loss:	{}'.format(loss))
#	Since	the	accuracy	and	0-1	loss	are	complementary	measures,	their	sum	must	be	1.0.
print('Accuracy	+	0-1	loss	=	{}'.format(accuracy	+	loss))
K-fold	cross-validation
K-fold	cross-validation	consists	of	splitting	the	original	dataset	in	k	disjoint	subsets	of	approximately	equal	size.	Then,	at	each	iteration,	k − 1
subsets	are	used	as	the	training	set	and	the	remaining	subset	is	used	as	the	test	set.	In	the	end,	we	can	calculate	the	meanaccuracy	as
the	performance	measure.
Sklearn	provides	several	different	classes	to	perform	k-fold	cross-validation.	In	this	example,	we	use	the	StratifiedKFold	class,	since	it	breaks
the	original	dataset	in	k	stratified	disjoint	subsets.	So,	each	subset	will	maintain	the	same	proportion	of	objects	in	each	class	as	in	the	original
dataset.
X:
[[		17.99					10.38				122.8				...,				0.2654				0.4601				0.1189]
	[		20.57					17.77				132.9				...,				0.186					0.275					0.089	]
	[		19.69					21.25				130.					...,				0.243					0.3613				0.0876]
	...,
	[		16.6						28.08				108.3				...,				0.1418				0.2218				0.0782]
	[		20.6						29.33				140.1				...,				0.265					0.4087				0.124	]
	[			7.76					24.54					47.92			...,				0.								0.2871				0.0704]]
y:
[1	1	1	1	1	1	1	1	1	1	1	1	1	1	1	1	1	1	1	0	0	0	1	1	1	1	1	1	1	1	1	1	1	1	1	1	1
	0	1	1	1	1	1	1	1	1	0	1	0	0	0	0	0	1	1	0	1	1	0	0	0	0	1	0	1	1	0	0	0	0	1	0	1	1
	0	1	0	1	1	0	0	0	1	1	0	1	1	1	0	0	0	1	0	0	1	1	0	0	0	1	1	0	0	0	0	1	0	0	1	0	0
	0	0	0	0	0	0	1	1	1	0	1	1	0	0	0	1	1	0	1	0	1	1	0	1	1	0	0	1	0	0	1	0	0	0	0	1	0
	0	0	0	0	0	0	0	0	1	0	0	0	0	1	1	0	1	0	0	1	1	0	0	1	1	0	0	0	0	1	0	0	1	1	1	0	1
	0	1	0	0	0	1	0	0	1	1	0	1	1	1	1	0	1	1	1	0	1	0	1	0	0	1	0	1	1	1	1	0	0	1	1	0	0
	0	1	0	0	0	0	0	1	1	0	0	1	0	0	1	1	0	1	0	0	0	0	1	0	0	0	0	0	1	0	1	1	1	1	1	1	1
	1	1	1	1	1	1	1	0	0	0	0	0	0	1	0	1	0	0	1	0	0	1	0	1	1	0	0	0	0	0	0	0	0	0	0	0	0
	0	1	0	0	1	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	1	0	1	0	0	0	0	1	1	1	0	0
	0	0	1	0	1	0	1	0	0	0	1	0	0	0	0	0	0	0	1	1	1	0	0	0	0	0	0	0	0	0	0	0	1	1	0	1	1
	1	0	1	1	0	0	0	0	0	1	0	0	0	0	0	1	0	0	0	1	0	0	1	1	0	0	0	0	0	0	1	0	0	0	0	0	0
	0	1	0	0	0	0	0	1	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	1	0	1	1	0	1	0	0	0	0	0	1	0	0
	1	0	1	0	0	1	0	1	0	0	0	0	0	0	0	0	1	1	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	1	0
	0	0	0	0	0	0	1	0	1	0	0	1	0	0	0	0	0	1	1	0	1	0	1	0	0	0	0	0	1	0	0	1	0	1	0	1	1
	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	1	0	1	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0
	0	0	0	0	0	0	0	1	1	1	1	1	1	0]
Accuracy:	0.9226804123711341
0-1	loss:	0.07731958762886593
Accuracy	+	0-1	loss	=	1.0
In	[6]:
splits	=	10
skfold	=	StratifiedKFold(n_splits=splits,	random_state=seed)
trained_knns	=	[]
accuracies	=	[]
#	skfold.split(X,	y)	returns	an	iterator	over	tuples.
#	In	each	tuple,	the	first	element	consists	of	the	indices	of	examples	from	the	train	set.
#	The	second	element	consists	of	the	indices	of	examples	from	the	test	set.
for	train_idx,	test_idx	in	skfold.split(X,	y):
				X_train,	X_test	=	X[train_idx],	X[test_idx]
				y_train,	y_test	=	y[train_idx],	y[test_idx]
				knn,	y_pred,	acc,	loss	=	knn_fit_predict_evaluate(X_train,	X_test,	y_train,	y_test)
				trained_knns.append(knn)
				accuracies.append(acc)
accuracies	=	np.array(accuracies)
print('{}-fold	cross-validation	accuracy	mean:	{}'.format(splits,	np.mean(accuracies)))
print('{}-fold	cross-validation	accuracy	std:	{}'.format(splits,	np.std(accuracies,	ddof=1)))
ROC	analysis
For	a	binary	classification	problem,	where	we	have	a	positive	(+ )	and	a	negative	( − )	class,	we	can	obtain	the	confusion	matrix	of	the
expected	and	predicted	results.	The	confusion	matrix	is	organized	as:
Predicted	 + Predicted	 −
Expected	 + TP FN
Expected	 − FP TN
From	the	above	matrix,	we	can	extract	the	following	quantities:
True	Positives	(TP):	the	number	of	positive	examples	that	were	correctly	predicted	as	positive;
True	Negatives	(TN):	the	number	of	negative	examples	that	were	correctly	predicted	as	negative;
False	Negatives	(FN):	the	number	of	positive	examples	that	were	wrongly	predicted	as	negative;
False	Positives	(FP):	the	number	of	negative	examples	that	were	wrongly	predicted	as	positive.
Then,	we	can	obtain	two	measures:
True	Positive	Rate	(TPR):	also	known	as	sensibility.	It	measures	the	hit	rate	for	the	positive	class.	It	is	calculated	as:	
TPR =
TP
TP + FN ;
False	Positive	Rate	(FPR):	also	known	as	specificity.	It	measures	the	hit	rate	for	the	negative	class.	It	is	calculated	as:	
FPR =
FP
TN + FP .
Many	classifiers	output	scores	(or	probabilities)	when	classifying	an	unseen	example.	These	scores	are	usually	thresholded	in	order	to
return	a	binary	classification.
The	ROC	analysis	consists	of	using	several	thresholds	for	the	output	scores	of	a	classifier.	Then,	for	each	threshold,	the	respective	TPR	and
FPR	values	can	be	calculated.	By	plotting	the	obtained	(TPR,	FPR)	pairs,	we	obtain	the	ROC	curve.
Finally,	a	commonly	used	measure	to	compare	classifiers	is	the	area	under	the	ROC	curve	(ROC	AUC).	Such	a	measure	lies	between	0	and	1,
with	values	close	to	1	indicating	better	results.
We	present	an	example	below.
10-fold	cross-validation	accuracy	mean:	0.9298429262812202
10-fold	cross-validation	accuracy	std:	0.02691043470531831
In	[7]:
#	Splitting	X	and	y	in	train	and	test	sets.
X_train,	X_test,	y_train,	y_test	=	train_test_split(X,	y,	test_size=0.34,	stratify=y,	random_state=seed)
#	Creating	and	training	a	kNN	classifier.
knn	=	KNeighborsClassifier(n_neighbors=DEFAULT_N_NEIGHBORS,	weights='distance',	metric='euclidean')
knn.fit(X_train,	y_train)
#	Predicting	probability	scores	for	the	test	set.
y_prob	=	knn.predict_proba(X_test)
#	Calculatting	False	Positive	Rate	and	True	Positive	Rate	values	for	different	thresholds.
#	The	first	parameter	consists	of	the	expected	labels.
#	The	second	parameter	consists	of	the	predicted	scores	for	the	positive	class.	In	this	example
#	the	positive	class	is	assumed	to	be	the	one	with	label	=	1.
false_positive_rate,	true_positive_rate,	thresholds	=	roc_curve(y_test,	y_prob[:,	1])
#	Calculating	the	area	under	the	ROC	curve.
roc_auc	=	auc(false_positive_rate,	true_positive_rate)
Finally,	we	plot	the	ROC	curve.	The	diagonal	line	of	such	a	plot	indicates	a	classifier	with	random	predictions.
In	[8]:
#	setting	linewidth	=	2
lw	=	2
plt.plot(false_positive_rate,
									true_positive_rate,
									color='blue',
									lw=lw,
									label='ROC	curve	(area	=	{:.4f})'.format(roc_auc))
plt.plot([0,	1],	[0,	1],	color='red',	lw=lw,	linestyle='--',	label='Random	classifier')
plt.xlim([0.0,	1.0])
plt.ylim([0.0,	1.05])
plt.xlabel('False	Positive	Rate')
plt.ylabel('True	Positive	Rate')
plt.title('ROC')
plt.legend(loc="lower	right")
plt.show()
Hypothesis	testing
Comparing	two	classifiers	over	multiple	datasets
Usually,	when	comparing	two	classifiers	(namely,	f1	and	f2),	the	null-hypothesis	(H0)	states	that	their	performances	are	equivalent.	For	this
situation,	Demšar	(2006)	recommends	the	Wilcoxon	signed-rank	test.
Next,	we	present	an	example	extracted	from	(Demšar,	2006).	In	such	an	example,	we	have	the	area	under	the	curve	(AUC)	for	the	C4.5
algorithm	with	the	parameter	m	(the	minimal	number	of	examples	in	a	leaf)	equal	to	0	and	C4.5	with	tunned	m	(C4.5+m)	considering	14
datasets.
In	[9]:
#	Loading	the	example	DataFrame.
performances	=	pd.read_csv('data/example_wilcoxon_demsar.csv')
performances
In	[10]:
#	Getting	C4.5	AUC	values.
c45	=	np.array(performances['C4.5'])
#	Getting	C4.5+m	AUC	values.
c45m	=	np.array(performances['C4.5+m'])
#	Running	Wilcoxon	test.	When	zero_method='zsplit'	the	zero	ranks	are	splitted	between	positive	and	negative	ones.
wilcoxon(c45,	c45m,	zero_method='zsplit')
The	Wilcoxon	signed-rank	test	outputs	a	p-value	close	to	0.01.	If	we	consider	a	significance	level	(α)	of	0.05	we	can	conclude	that	C4.5	and
C4.5+m	performances	are	not	equivalent.
Comparing	multiple	classifiers	over	multiple	datasets
The	Wilcoxon	signed-rank	test	was	not	designed	to	compare	multiple	random	variables.	So,	when	comparing	multiple	classifiers,	an
"intuitive"	approach	would	be	to	apply	the	Wilcoxon	test	to	all	possible	pairs.	However,	when	multiple	tests	are	conducted,	some	of	them	will
reject	the	null	hypothesis	only	by	chance	(Demšar,	2006).
For	the	comparison	of	multiple	classifiers,	Demšar	(2006)	recommends	the	Friedman	test.
The	Friedman	test	ranks	the	algorithms	from	best	to	worst	on	each	dataset	with	respect	to	their	performances.	Its	null-hypothesis	(H0)
states	that	all	algorithms	are	equivalent	and	their	mean	ranks	are	equal.
Next,	we	present	an	example	extracted	from	(Demšar,	2006).	In	suchan	example,	we	have	the	AUC	for	four	classifiers:	C4.5	with	m = 0	and
the	confidence	interval	parameter	cf = 0.25,	C4.5	with	tunned	m,	C4.5	with	tunned	cf	and	C4.5	with	both	parameters	tunned.
Out[9]:
dataset C4.5 C4.5+m
0 adult(sample) 0.763 0.768
1 breast_cancer 0.599 0.591
2 breast_cancer_wisconsin 0.954 0.971
3 cmc 0.628 0.661
4 ionosphere 0.882 0.888
5 iris 0.936 0.931
6 liver_disorders 0.661 0.668
7 lung_cancer 0.583 0.583
8 lymphography 0.775 0.838
9 mushroom 1.000 1.000
10 primary_tumor 0.940 0.962
11 rheum 0.619 0.666
12 voting 0.972 0.981
13 wine 0.957 0.978
Out[10]:
WilcoxonResult(statistic=12.0,	pvalue=0.010968496564224731)
In	[11]:
#	Loading	the	example	DataFrame.
performances	=	pd.read_csv('data/example_friedman_nemenyi_demsar.csv')
performances
In	[12]:
#	First,	we	extract	the	algorithms	names.
algorithms_names	=	performances.drop('dataset',	axis=1).columns
#	Then,	we	extract	the	performances	as	a	numpy.ndarray.
performances_array	=	performances[algorithms_names].values
#	Finally,	we	apply	the	Friedman	test.
friedmanchisquare(*performances_array)
The	Friedman	test	outputs	a	very	small	p-value.	For	many	significance	levels	(α)	we	can	conclude	that	the	performances	of	all	algorithms	are
not	equivalent.
Considering	that	the	null-hypothesis	was	rejected,	we	usually	have	two	scenarios	for	a	post-hoc	test	(Demšar,	2006):
All	classifiers	are	compared	to	each	other.	In	this	case	we	apply	the	Nemenyi	post-hoc	test.
All	classifiers	are	compared	to	a	control	classifier.	In	this	scenario	we	apply	the	Bonferroni-Dunn	post-hoc	test.
To	perform	both	of	the	aformentioned	post-hoc	tests,	we	need	the	average	rank	of	each	algorithm,
In	[13]:
#	Calculating	the	ranks	of	the	algorithms	for	each	dataset.	The	value	of	p	is	multipled	by	-1
#	because	the	rankdata	method	ranks	from	the	smallest	to	the	greatest	performance	values.
#	Since	we	are	considering	AUC	as	our	performance	measure,	we	want	larger	values	to	be	best	ranked.
ranks	=	np.array([rankdata(-p)	for	p	in	performances_array])
#	Calculating	the	average	ranks.
average_ranks	=	np.mean(ranks,	axis=0)
print('\n'.join('{}	average	rank:	{}'.format(a,	r)	for	a,	r	in	zip(algorithms_names,	average_ranks)))
Then,	we	will	calculate	the	critical	differences	and	plot	the	results	of	each	test	(Nemenyi	and	Bonferroni-Dunn).
Out[11]:
dataset C4.5 C4.5+m C4.5+cf C4.5+m+cf
0 adult(sample) 0.763 0.768 0.771 0.798
1 breast_cancer 0.599 0.591 0.590 0.569
2 breast_cancer_wisconsin 0.954 0.971 0.968 0.967
3 cmc 0.628 0.661 0.654 0.657
4 ionosphere 0.882 0.888 0.886 0.898
5 iris 0.936 0.931 0.916 0.931
6 liver_disorders 0.661 0.668 0.609 0.685
7 lung_cancer 0.583 0.583 0.563 0.625
8 lymphography 0.775 0.838 0.866 0.875
9 mushroom 1.000 1.000 1.000 1.000
10 primary_tumor 0.940 0.962 0.965 0.962
11 rheum 0.619 0.666 0.614 0.669
12 voting 0.972 0.981 0.975 0.975
13 wine 0.957 0.978 0.946 0.970
Out[12]:
FriedmanchisquareResult(statistic=51.285714285714278,	pvalue=1.7912382226666844e-06)
C4.5	average	rank:	3.142857142857143
C4.5+m	average	rank:	2.0
C4.5+cf	average	rank:	2.9285714285714284
C4.5+m+cf	average	rank:	1.9285714285714286
In	[14]:
#	This	method	computes	the	critical	difference	for	Nemenyi	test	with	alpha=0.1.
#	For	some	reason,	this	method	only	accepts	alpha='0.05'	or	alpha='0.1'.
cd	=	compute_CD(average_ranks,
																n=len(performances),
																alpha='0.1',
																test='nemenyi')
#	This	method	generates	the	plot.
graph_ranks(average_ranks,
												names=algorithms_names,
												cd=cd,
												width=10,
												textspace=1.5,
												reverse=True)
plt.show()
In	[15]:
#	This	method	computes	the	critical	difference	for	Bonferroni-Dunn	test	with	alpha=0.05.
#	For	some	reason,	this	method	only	accepts	alpha='0.05'	or	alpha='0.1'.
cd	=	compute_CD(average_ranks,
																n=len(performances),
																alpha='0.05',
																test='bonferroni-dunn')
#	This	method	generates	the	plot.
graph_ranks(average_ranks,
												names=algorithms_names,
												cd=cd,
												cdmethod=0,
												width=10,
												textspace=1.5,
												reverse=True)
plt.show()
References
Demšar,	J.	(2006).	Statistical	comparisons	of	classifiers	over	multiple	data	sets.	Journal	of	Machine	learning	research,	7,	1-30.
Naive	Bayes
Naive	Bayes	is	a	very	simple	but	powerful	classification	method.	For	a	given	object	x,	Naive	Bayes	calculates	x's	probability	to	belong	to	each
class	yi	(i = 1, ⋯, k),	using	the	Bayes'	theorem:
P (yi | x) =
P(yi) P (x1, ⋯, xm | yi)
P (x1, ⋯, xm) .
Additionally,	it	assumes	that	the	features	are	independent	from	each	other	(which	is	the	reason	why	it	is	called	naive):
P(x1, ⋯, xm | yi) = ∏m
j = 1P (xj | yi).
So,	we	obtain:
P(yi | x) =
P (yi) ∏m
j = 1P (xj | yi)
P (x1, ⋯, xm) .
For	a	given	object	x,	Naive	Bayes	will	output	the	the	Maximum	a	Posteriori	(MAP)	estimate:
ŷ = arg maxy P (y | x) = arg maxy
P (y) ∏m
j = 1P (xj | y)
P (x1, ⋯, xm) .
Note	that	P (x1, ⋯, xm)	is	constant.	Thus,	we	can	drop	it	to	obtain:
ŷ = arg maxy P (yi) ∏m
j = 1P (xj | y).
Practical	example
First	of	all,	we	do	all	the	necessary	imports	and	load	the	Mushroom	dataset.
In	[1]:
import	pandas	as	pd
import	matplotlib.pyplot	as	plt
from	sklearn.preprocessing	import	LabelEncoder
from	sklearn.model_selection	import	train_test_split
from	sklearn.naive_bayes	import	BernoulliNB
from	sklearn.metrics	import	accuracy_score,	roc_curve,	auc
#	setting	random	seed
seed	=	10
data	=	pd.read_csv('data/mushroom.csv')
#	We	drop	the	'stalk-root'	feature	because	it	is	the	only	one	containing	missing	values.
data	=	data.drop('stalk-root',	axis=1)
data.head()
Out[1]:
cap-
shape
cap-
surface
cap-
color bruises? odor gill-
attachment
gill-
spacing
gill-
size
gill-
color
stalk-
shape ...
stalk-
color-
above-
ring
stalk-
color-
below-
ring
veil-
type
veil-
color
ring-
number
ring-
type
0 x s n t p f c n k e ... w w p w o p
1 x s y t a f c b k e ... w w p w o p
2 b s w t l f c b n e ... w w p w o p
3 x y w t p f c n n e ... w w p w o p
4 x s g f n f w b k t ... w w p w o e
5	rows	×	22	columns
Unfortunately,	scikit-learn	does	not	implement	the	classical	Naive	Bayes	algorithm	which	calculates	the	conditional	probabilities	P(xj | yi)	as	the
proportion	of	objects	from	class	yi	that	assume	each	particular	categorical	value	for	feature	j.	However,	scikit-learn	contains	the	BernoulliNB
class	which	assumes	that	data	is	distributed	according	to	multivariate	Bernoulli	distributions.
So,	for	the	Mushroom	dataset,	we	can	transform	each	categorical	feature	to	dummy	variables.	Note	that	such	a	conversion	clearly
violates	the	indepence	assumption	between	features.	However,	Naive	Bayes	has	been	proven	to	achieve	good	performance	in
several	applications	where	indepence	is	violated	(for	example,	in	text	classication).
In	[2]:
#	Creating	a	new	DataFrame	representation	for	each	feature	as	dummy	variables.
dummies	=	[pd.get_dummies(data[c])	for	c	in	data.drop('label',	axis=1).columns]
#	Concatenating	all	DataFrames	containing	dummy	variables.
binary_data	=	pd.concat(dummies,	axis=1)
#	Getting	binary_data	as	a	numpy.array.
X	=	binary_data.values
#	Getting	the	labels.
le	=	LabelEncoder()
y	=	le.fit_transform(data['label'].values)
#	Splitting	the	binary	dataset	into	train	and	test	sets.
X_train,	X_test,	y_train,	y_test	=	train_test_split(X,	y,	test_size=0.34,	stratify=y,	random_state=seed)
#	Creates	a	BernoulliNB.	binarize=None	indicates	that	there	is	no	need	to	binarize	the	input	data.
nb	=	BernoulliNB(binarize=None)
nb.fit(X_train,	y_train)
y_pred	=	nb.predict(X_test)
accuracy	=	accuracy_score(y_test,	y_pred)
print('BernoulliNB	accuracy	score:	{}'.format(accuracy))
In	[3]:
#	Getting	the	probabilities	for	each	class.
y_prob	=	nb.predict_proba(X_test)
#	Calculating	ROC	curve	and	ROC	AUC.
false_positive_rate,	true_positive_rate,	thresholds	=	roc_curve(y_test,	y_prob[:,	1])
roc_auc	=	auc(false_positive_rate,	true_positive_rate)
#	Plotting	ROC	curve.lw	=	2
plt.plot(false_positive_rate,	true_positive_rate,	color='blue',	lw=lw,	label='ROC	curve	(area	=	{:.4f})'.format(roc_auc)
)
plt.plot([0,	1],	[0,	1],	color='red',	lw=lw,	linestyle='--',	label='Random	classifier')
plt.xlim([0.0,	1.0])
plt.ylim([0.0,	1.05])
plt.xlabel('False	Positive	Rate')
plt.ylabel('True	Positive	Rate')
plt.title('ROC')
plt.legend(loc="lower	right")
plt.show()
BernoulliNB	accuracy	score:	0.9464350343829171
Decision	Trees
Decision	Trees	are	classification	methods	that	are	able	to	extract	simple	rules	about	the	data	features	which	are	inferred	from	the	input
dataset.	Several	algorithms	for	decision	tree	induction	are	available	in	the	literature.	Scikit-learn	contains	the	implementation	of	the	CART
(Classification	and	Regression	Trees)	induction	algorithm.
Practical	examples
Fist	of	all,	we	do	all	necessary	imports.
In	[1]:
import	pandas	as	pd
import	graphviz
from	sklearn.preprocessing	import	LabelEncoder
from	sklearn.tree	import	DecisionTreeClassifier,	export_graphviz
from	sklearn.model_selection	import	train_test_split
from	sklearn.metrics	import	accuracy_score
#	Setting	random	seed.
seed	=	10
Dataset	with	continuous	features
Next,	we	load	the	Iris	dataset,	extract	its	values	and	labels	and	split	them	into	train	and	test	sets.
In	[2]:
#	Loading	Iris	dataset.
data	=	pd.read_csv('data/iris.csv')
#	Creating	a	LabelEncoder	and	fitting	it	to	the	dataset	labels.
le	=	LabelEncoder()
le.fit(data['Name'].values)
#	Converting	dataset	str	labels	to	int	labels.
y	=	le.transform(data['Name'].values)
#	Extracting	the	instances	data.
X	=	data.drop('Name',	axis=1).values
#	Splitting	into	train	and	test	sets.
X_train,	X_test,	y_train,	y_test	=	train_test_split(X,	y,	test_size=0.34,	stratify=y,	random_state=seed)
Then,	we	will	fit	and	test	a	DecisionTreeClassifier.	Scikit-learn	does	not	implement	any	post-prunning	step.	So,	to	avoid	overfitting,	we	can
control	the	tree	size	with	the	parameters	min_samples_leaf,	min_samples_split	and	max_depth.
In	[3]:
#	Creating	a	DecisionTreeClassifier.
#	The	criterion	parameter	indicates	the	measure	used	(possible	values:	'gini'	for	the	Gini	index	and
#	'entropy'	for	the	information	gain).
#	The	min_samples_leaf	parameter	indicates	the	minimum	of	objects	required	at	a	leaf	node.
#	The	min_samples_split	parameter	indicates	the	minimum	number	of	objects	required	to	split	an	internal	node.	
#	The	max_depth	parameter	controls	the	maximum	tree	depth.	Setting	this	parameter	to	None	will	grow	the
#	tree	until	all	leaves	are	pure	or	until	all	leaves	contain	less	than	min_samples_split	samples.
tree	=	DecisionTreeClassifier(criterion='gini',
																														min_samples_leaf=5,
																														min_samples_split=5,
																														max_depth=None,
																														random_state=seed)
tree.fit(X_train,	y_train)
y_pred	=	tree.predict(X_test)
accuracy	=	accuracy_score(y_test,	y_pred)
print('DecisionTreeClassifier	accuracy	score:	{}'.format(accuracy))
Finally,	we	can	plot	the	obtained	tree	to	visualize	the	rules	extracted	from	the	dataset.
DecisionTreeClassifier	accuracy	score:	0.9615384615384616
In	[4]:
def	plot_tree(tree,	dataframe,	label_col,	label_encoder,	plot_title):
				label_names	=	pd.unique(dataframe[label_col])
				#	Obtaining	plot	data.
				graph_data	=	export_graphviz(tree,
																																	feature_names=dataframe.drop(label_col,	axis=1).columns,
																																	class_names=label_names,
																																	filled=True,
																																	rounded=True,
																																	out_file=None)
				#	Generating	plot.
				graph	=	graphviz.Source(graph_data)
				graph.render(plot_title)
				return	graph
tree_graph	=	plot_tree(tree,	data,	'Name',	le,	'Iris')
tree_graph
Dataset	with	categorical	features
Unfortunately,	the	DecisionTreeClassifier	class	does	not	handle	categorical	features	directly.	So,	we	might	consider	to	transform	them	to
dummy	variables.	However,	this	approach	must	be	taken	with	a	grain	of	salt	because	decision	trees	tend	to	overfit	on	data
with	a	large	number	of	features.
Out[4]:
PetalWidth	<=	0.8
gini	=	0.6666
samples	=	98
value	=	[33,	32,	33]
class	=	Iris-setosa
gini	=	0.0
samples	=	33
value	=	[33,	0,	0]
class	=	Iris-setosa
True
PetalLength	<=	4.85
gini	=	0.4999
samples	=	65
value	=	[0,	32,	33]
class	=	Iris-virginica
False
PetalWidth	<=	1.45
gini	=	0.1207
samples	=	31
value	=	[0,	29,	2]
class	=	Iris-versicolor
PetalWidth	<=	1.75
gini	=	0.1609
samples	=	34
value	=	[0,	3,	31]
class	=	Iris-virginica
gini	=	0.0
samples	=	22
value	=	[0,	22,	0]
class	=	Iris-versicolor
gini	=	0.3457
samples	=	9
value	=	[0,	7,	2]
class	=	Iris-versicolor
gini	=	0.4898
samples	=	7
value	=	[0,	3,	4]
class	=	Iris-virginica
gini	=	0.0
samples	=	27
value	=	[0,	0,	27]
class	=	Iris-virginica
In	[5]:
#	Loading	Mushroom	dataset.
data	=	pd.read_csv('data/mushroom.csv')
#	We	drop	the	'stalk-root'	feature	because	it	is	the	only	one	containing	missing	values.
data	=	data.drop('stalk-root',	axis=1)
#	Creating	a	new	DataFrame	representation	for	each	feature	as	dummy	variables.
dummies	=	[pd.get_dummies(data[c])	for	c	in	data.drop('label',	axis=1).columns]
#	Concatenating	all	DataFrames	containing	dummy	variables.
binary_data	=	pd.concat(dummies,	axis=1)
#	Getting	binary_data	as	a	numpy.array.
X	=	binary_data.values
#	Getting	the	labels.
le	=	LabelEncoder()
y	=	le.fit_transform(data['label'].values)
#	Splitting	the	binary	dataset	into	train	and	test	sets.
X_train,	X_test,	y_train,	y_test	=	train_test_split(X,	y,	test_size=0.34,	stratify=y,	random_state=seed)
#	Creating	a	DecisionTreeClassifier.
tree	=	DecisionTreeClassifier(criterion='gini',
																														min_samples_leaf=5,
																														min_samples_split=5,
																														max_depth=None,
																														random_state=seed)
tree.fit(X_train,	y_train)
Now,	we	will	apply	the	obtained	tree	on	the	test	set.
In	[6]:
y_pred	=	tree.predict(X_test)
accuracy	=	accuracy_score(y_test,	y_pred)
print('DecisionTreeClassifier	accuracy	score:	{}'.format(accuracy))
We	can	observe	that	the	above	decision	tree	is	pretty	accurate.
Now,	let's	check	its	depth.
In	[7]:
print('DecisionTreeClassifier	max_depth:	{}'.format(tree.tree_.max_depth))
What	if	we	fit	a	decision	tree	with	a	smaller	depth?
In	[8]:
#	Creating	a	DecisionTreeClassifier.
tree	=	DecisionTreeClassifier(criterion='gini',
																														min_samples_leaf=5,
																														min_samples_split=5,
																														max_depth=3,
																														random_state=seed)
tree.fit(X_train,	y_train)
y_pred	=	tree.predict(X_test)
accuracy	=	accuracy_score(y_test,	y_pred)
print('DecisionTreeClassifier	accuracy	score:	{}'.format(accuracy))
Out[5]:
DecisionTreeClassifier(class_weight=None,	criterion='gini',	max_depth=None,
												max_features=None,	max_leaf_nodes=None,
												min_impurity_split=1e-07,	min_samples_leaf=5,
												min_samples_split=5,	min_weight_fraction_leaf=0.0,
												presort=False,	random_state=10,	splitter='best')
DecisionTreeClassifier	accuracy	score:	0.9992761491132827
DecisionTreeClassifier	max_depth:	6
DecisionTreeClassifier	accuracy	score:	0.9659790083242852
We	can	observe	that	the	new	tree	is	almost	as	accurate	as	the	first	one.	Apparently	both	trees	are	able	to	handle	the	mushroom	data	pretty
well.	The	second	three	might	be	preferred,	since	it	is	a	simpler	and	computationally	cheaper	model.
Finally,	we	plot	the	second	tree.
In	[9]:
#	Appending	'label'	column	to	binary	DataFrame.
binary_data['label']	=	data['label']
tree_graph	=	plot_tree(tree,	binary_data,	'label',	le,	'Mushroom')
tree_graph
Out[9]:
n	<=	0.5
gini	=	0.4994
samples	=	5361
value	=	[2777,	2584]
class	=	p
f	<=	0.5
gini	=	0.2845
samples	=	3040
value	=	[522,	2518]
class	=	e
True
r	<=	0.5
gini	=	0.0553
samples	=	2321
value	=	[2255,	66]
class	=	p
False
h	<=	0.5
gini	=	0.4826
samples=	880
value	=	[522,	358]
class	=	p
gini	=	0.0
samples	=	2160
value	=	[0,	2160]
class	=	e
gini	=	0.3739
samples	=	695
value	=	[522,	173]
class	=	p
gini	=	0.0
samples	=	185
value	=	[0,	185]
class	=	e
y	<=	0.5
gini	=	0.0251
samples	=	2284
value	=	[2255,	29]
class	=	p
gini	=	0.0
samples	=	37
value	=	[0,	37]
class	=	e
gini	=	0.0035
samples	=	2250
value	=	[2246,	4]
class	=	p
gini	=	0.3893
samples	=	34
value	=	[9,	25]
class	=	e
Artificial	Neural	Networks
Perceptron
The	Perceptron	is	a	very	simple	linear	binary	classifier.	It	basically	maps	and	input	vector	x	to	a	binary	output	f(x).
Given	a	weight	vector	w,	the	Perceptron's	classfication	rule	is:	f(x) = 1	if	w ⋅ x + b > 0	or	f(x) = 0	otherwise.	Here,	b	is	a	bias	value	which	is
responsible	for	shifting	the	Perceptron's	hyperplane	away	from	the	origin.
Practical	example
First	we	do	all	necessary	imports.
In	[1]:
import	pandas	as	pd
import	numpy	as	np
import	matplotlib.pyplot	as	plt
from	matplotlib.colors	import	ListedColormap
from	sklearn.preprocessing	import	LabelEncoder,	StandardScaler
from	sklearn.linear_model	import	Perceptron
from	sklearn.neural_network	import	MLPClassifier
from	sklearn.model_selection	import	train_test_split
from	sklearn.metrics	import	accuracy_score
#	Setting	random	seed.
seed	=	10
The	most	simple	examples	for	Perceptron	are	the	basic	logic	operations,	such	as:	AND,	OR	and	XOR.
The	AND	operation	is	defined	as:
x0 x1 y
0 0 0
1 0 0
0 1 0
1 1 1
Below	we	run	the	Perceptron	to	learn	the	logical	AND.
In	[2]:
#	Setting	the	input	samples.
X	=	np.array([[0,	0],
														[0,	1],
														[1,	0],
														[1,	1]],
													dtype=np.double)
#	Setting	the	expected	outputs.
y_AND	=	np.array([0,	0,	0,	1])
#	Creating	and	training	a	Perceptron.
p	=	Perceptron(random_state=seed,	eta0=0.1,	max_iter=1000)
p.fit(X,	y_AND)
#	Obtaining	f(x)	scores.
pred_scores	=	p.decision_function(X)
print("Perceptron's	AND	scores:	{}".format(pred_scores))
Then,	we	plot	the	Perceptron's	decision	boundary.	The	colorbar	to	the	left	shows	the	scores	achieved	by	w ⋅ x + b.	Each	point	color	indicates
a	different	class	(blue	=	1,	red	=	0).
Perceptron's	AND	scores:	[	-2.00000000e-01		-1.00000000e-01		-2.77555756e-17			1.00000000e-01]
In	[3]:
#	Method	to	plot	the	Perceptron's	decision	boundary.
#	This	code	is	based	on	http://scikit-learn.org/stable/auto_examples/classification/plot_classifier_comparison.html
def	plot_decision_boundary(classifier,	X,	y,	title):
				xmin,	xmax	=	np.min(X[:,	0])	-	0.05,	np.max(X[:,	0])	+	0.05
				ymin,	ymax	=	np.min(X[:,	1])	-	0.05,	np.max(X[:,	1])	+	0.05
				step	=	0.01
				cm	=	plt.cm.coolwarm_r
				#cm	=	plt.cm.RdBu
				thr	=	0.0
				xx,	yy	=	np.meshgrid(np.arange(xmin	-	thr,	xmax	+	thr,	step),	np.arange(ymin	-	thr,	ymax	+	thr,	step))
				if	hasattr(classifier,	'decision_function'):
								Z	=	classifier.decision_function(np.hstack((xx.ravel()[:,	np.newaxis],	yy.ravel()[:,	np.newaxis])))
				else:
								Z	=	classifier.predict_proba(np.hstack((xx.ravel()[:,	np.newaxis],	yy.ravel()[:,	np.newaxis])))[:,	1]
				Z	=	Z.reshape(xx.shape)
				plt.contourf(xx,	yy,	Z,	cmap=cm,	alpha=0.8)
				plt.colorbar()
				plt.scatter(X[:,	0],	X[:,	1],	c=y,	cmap=ListedColormap(['#FF0000',	'#0000FF']),	alpha=0.6)
				plt.xlim(xmin,	xmax)
				plt.ylim(ymin,	ymax)
				plt.xticks((0.0,	1.0))
				plt.yticks((0.0,	1.0))
				plt.title(title)
#	Plotting	Perceptron	decision	boundary.
#	The	colorbar	shows	the	scores	obtained	for	f(x).
plot_decision_boundary(p,	X,	y_AND,	'AND	decision	boundary')
plt.show()
The	OR	operation	is	defined	as:
x0 x1 y
0 0 0
1 0 1
0 1 1
1 1 1
Below	we	run	the	Perceptron	to	the	logical	OR,	print	its	achieved	scores	and	plot	its	decision	boundary.
In	[4]:
y_OR	=	np.array([0,	1,	1,	1])
p.fit(X,	y_OR)
#	Obtaining	f(x)	scores.
pred_scores	=	p.decision_function(X)
print("Perceptron's	OR	scores:	{}".format(pred_scores))
plot_decision_boundary(p,	X,	y_OR,	'OR	decision	boundary')
plt.show()
Finally,	we	analyze	the	XOR	operation,	which	is	defined	as:
x0 x1 y
0 0 0
1 0 1
0 1 1
1 1 0
Below	we	plot	XOR.
In	[5]:
y_XOR	=	np.array([0,	1,	1,	0])
plt.scatter(X[:,	0],	X[:,	1],	c=y_XOR,	cmap=ListedColormap(['#FF0000',	'#0000FF']),	alpha=1.0)
plt.show()
Clearly,	this	is	not	a	linear	separable	problem.	In	other	words,	it	is	not	possible	to	separate	the	two	classes	with	a	single	hyperplane.
This	kind	of	problem	motivates	us	to	use	Multilayer	Perceptrons	(MLPs),	which	are	shown	in	the	sequence.
Multilayer	Perceptron	(MLP)
A	MLP	is	a	neural	network	which	is	composed	by	at	least	three	different	layers:	an	input	layer,	a	hidden	layer	and	an	output	layer.	Except	for
the	input	layer,	the	remaining	ones	are	composed	by	Perceptrons	with	nonlinear	activation	functions	(e.g.,	sigmoid	or	tanh).
MLPs	are	usually	trained	using	the	backpropagation	algorithm	and	are	able	to	deal	with	not	linearly	separable	problems.
Below	we	present	an	example	for	the	XOR	problem.
Perceptron's	OR	scores:	[-0.1		0.1		0.1		0.3]
Practical	example	-	XOR
In	[6]:
#	Creating	a	MLPClassifier.
#	hidden_layer_sizes	receive	a	tuple	where	each	position	i	indicates	the	number	of	neurons
#	in	the	ith	hidden	layer
#	activation	specifies	the	activation	function	(other	options	are:	'identity',	'logistic'	and	'relu')
#	max_iter	indicates	the	maximum	number	of	training	iterations
#	There	are	other	parameters	which	can	also	be	changed.
#	See	http://scikit-learn.org/stable/modules/generated/sklearn.neural_network.MLPClassifier.html
mlp	=	MLPClassifier(hidden_layer_sizes=(5,),
																				activation='tanh',
																				max_iter=10000,
																				random_state=seed)
#	Training	and	plotting	the	decision	boundary.
mlp.fit(X,	y_XOR)
plot_decision_boundary(mlp,	X,	y_XOR,	'XOR')
plt.show()
pred	=	mlp.predict_proba(X)
print("MLP's	XOR	probabilities:\n[class0,	class1]\n{}".format(pred))
Practical	example	-	Breast	Cancer
First	of	all,	we	load	the	dataset,	encode	its	labels	as	int	values	and	split	it	into	training	and	test	sets.
In	[7]:
#	Loading	Breast	Cancer	dataset.
data	=	pd.read_csv('data/breast_cancer.csv')
#	Creating	a	LabelEncoder	and	transforming	the	dataset	labels.
le	=	LabelEncoder()
y	=	le.fit_transform(data['diagnosis'].values)
#	Extracting	the	instances	data.
X	=	data.drop('diagnosis',	axis=1).values
#	Splitting	into	train	and	test	sets.
X_train,	X_test,	y_train,	y_test	=	train_test_split(X,	y,	test_size=0.34,	stratify=y,	random_state=seed)
Then,	we	create,	train	and	test	a	MLPClassifier.
MLP's	XOR	probabilities:
[class0,	class1]
[[	0.90713158		0.09286842]
	[	0.0837964			0.9162036	]
	[	0.04619978		0.95380022]
	[	0.95695933		0.04304067]]
In	[8]:
mlp	=	MLPClassifier(hidden_layer_sizes=(10,),
																				activation='tanh',
																				max_iter=10000,
																				random_state=seed)
mlp.fit(X_train,	y_train)
y_pred	=	mlp.predict(X_test)
accuracy	=	accuracy_score(y_test,	y_pred)
print("MLP's	accuracy	score:	{}".format(accuracy))
We	can	observe	that	its	accuracy	score	was	rather	low.
Unfortunately,	MLPs	are	very	sensitive	to	different	feature	scales.	So,	it	is	normally	necessary	to	normalize	or	rescale	the	input	data.
In	[9]:
#	Creating	a	StandardScaler.	This	object	normalizes	features	to	zero	mean	and	unit	variance.
scaler	=	StandardScaler()
scaler.fit(X_train)
#	Normalizing	train	and	test	data.
X_train_scaled,	X_test_scaled	=	scaler.transform(X_train),	scaler.transform(X_test)
#	Training	MLP	with	normalized	data.
mlp.fit(X_train_scaled,	y_train)
#	Testing	MLP	with	normalized	data.
y_pred	=	mlp.predict(X_test_scaled)
accuracy	=	accuracy_score(y_test,	y_pred)
print("MLP's	accuracy	score:	{}".format(accuracy))
MLP's	accuracy	score:	0.6288659793814433
MLP's	accuracy	score:	0.979381443298969
Support	Vector	Machines	(SVMs)
SVMs	are	very	powerful	binary	classifiers,	based	on	the	Statistical	Learning	Theory	(SLT)	framework.	SVMs	can	be	used	to	solve	hard
classification	problems,	where	they	look	for	an	optimal	hyperplane	able	to	maximize	the	classifiermargin.
Practical	example	-	classifier	margin
First	of	all	we	do	all	necessary	imports.
In	[1]:
import	pandas	as	pd
import	numpy	as	np
import	matplotlib.pyplot	as	plt
from	mpl_toolkits.mplot3d	import	Axes3D
from	sklearn.datasets	import	make_blobs,	make_circles
from	sklearn.preprocessing	import	LabelEncoder,	StandardScaler
from	sklearn.model_selection	import	train_test_split
from	sklearn.metrics	import	accuracy_score
from	sklearn.svm	import	SVC
#	Setting	random	seed.
seed	=	10
Then,	we	generate	a	very	simple	linear	separable	dataset	and	plot	it.
In	[2]:
#	Generating	a	linear	separable	dataset	with	50	samples	and	2	classes.
X,	y	=	make_blobs(n_samples=50,	centers=2,	center_box=[-7.5,	7.5],	random_state=seed)
#	Method	to	plot	the	linear	separable	dataset.
def	plot_data(X,	y):
				class0	=	np.where(y	==	0)[0]
				plt.scatter(X[class0,	0],	X[class0,	1],	c='red',	marker='s')
				class1	=	np.where(y	==	1)[0]
				plt.scatter(X[class1,	0],	X[class1,	1],	c='blue',	marker='o')
plot_data(X,	y)
plt.show()
Next,	we	train	a	SVM	classifier	with	linear	kernel	and	plot	the	optimal	hyperplane	as	well	as	the	classifier	margins.
In	[3]:
svm	=	SVC(C=100,	kernel='linear',	random_state=seed)
svm.fit(X,	y)
plot_data(X,	y)
#	Method	to	plot	SVMs'	hyperplane	and	margins.
#	This	code	is	based	on	http://scikit-learn.org/stable/auto_examples/svm/plot_separating_hyperplane.html
def	plot_margins(svm,	X,	y):
				xmin,	xmax	=	plt.xlim()
				ymin,	ymax	=	plt.ylim()
				#	create	grid	to	evaluate	model
				xx	=	np.linspace(xmin,	xmax,	30)
				yy	=	np.linspace(ymin,	ymax,	30)
				XX,	YY	=	np.meshgrid(xx,	yy)
				xy	=	np.vstack([XX.ravel(),	YY.ravel()]).T
				Z	=	svm.decision_function(xy).reshape(XX.shape)
				#	plot	decision	boundary	and	margins
				plt.contour(XX,	YY,	Z,	colors='black',	levels=[-1,	0,	1],	alpha=1.0,	linestyles=['--',	'-',	'--'])
plot_margins(svm,	X,	y)
plt.show()
In	the	above	plot,	the	filled	line	represents	the	optimal	hyperplane	found	while	the	dashed	lines	represent	the	hyperplanes	defined	by	the
support	vectors.	The	margin	of	the	classifier	is	the	distance	between	the	optimal	hyperplane	and	any	of	the	support	vector	hyperplanes.
Practical	example	-	Non-linear	decision	boundary
SVMs	are	linear	classifiers.	Since	most	of	the	real	world	problems	are	not	linearly	separable,	how	can	we	deal	with	them?
Next,	we	will	show	a	very	simple	application	of	the	Kernel	Trick,	which	ables	us	to	learn	non-linear	decision	boundaries.
First,	we	generate	a	very	simple	and	not	linearly	separable	dataset.
In	[14]:
X,	y	=	make_circles(n_samples=100,	noise=0.05,	factor=0.5,	random_state=seed)
plot_data(X,	y)
plt.show()
Then,	we	try	to	fit	a	custom	SVM	with	linear	kernel.	Clearly,	this	classifier	will	not	achieve	good	results.
In	[5]:
svm	=	SVC(C=100,	kernel='linear',	random_state=seed)
svm.fit(X,	y)
plot_data(X,	y)
plot_margins(svm,	X,	y)
plt.show()
However,	if	we	apply	a	polynomial	kernel	of	degree	2,	we	are	able	to	learn	the	optimal	decision	boundary	for	this	dataset.
In	[6]:
svm	=	SVC(C=100,	kernel='poly',	degree=2,	random_state=seed)
svm.fit(X,	y)
plot_data(X,	y)
plot_margins(svm,	X,	y)
plt.show()
The	Kernel	Trick	consists	of	implicitly	mapping	a	lower	dimensional	dataset,	which	is	not	linearly	separable,	to	a	higher	dimensional	space
where	the	data	becomes	linearly	separable.
In	the	above	example,	the	standard	linear	kernel	calculates	the	standard	dot	product	as	the	similarity	between	two	vectors	u	and	v.	That	is:	
k(u, v) = u ⋅ v.
When	we	apply	a	polynomial	kernel	of	degree	2,	we	are	calculating	the	similarity	between	two	vectors	u	and	v	as:
k(u, v) = (u ⋅ v)2
k(u, v) = (u1v1 + u2v2)2
k(u, v) = u2
1v2
1 + 2u1v1u2v2 + u2
2v2
2
The	above	calculation	can	be	rewritten	as:
k(u, v) =
u2
1
√2u1u2
u2
2
⋅
v2
1
√2v1v2
v2
2
,
which	is	a	dot	product	of	two	three	dimensional	vectors.
Finally,	we	will	plot	the	original	dataset	in	this	new	three	dimensional	space.
[ ] [ ]
In	[24]:
to3d	=	lambda	x	:	[x[0]	**	2,	np.sqrt(2)	*	x[0]	*	x[1],	x[1]	**	2]
X_3D	=	np.array(list(map(to3d,	X)))
fig	=	plt.figure(1,	figsize=(8,	6))
ax	=	Axes3D(fig,	elev=-150,	azim=115)
class0	=	np.where(y	==	0)[0]
ax.scatter(X_3D[class0,	0],	X_3D[class0,	1],	X_3D[class0,	2],	c='red',	marker='s')
class1	=	np.where(y	==	1)[0]
ax.scatter(X_3D[class1,	0],	X_3D[class1,	1],	X_3D[class1,	2],	c='blue',	marker='o')
ax.set_xlabel('x[0]	**	2')
ax.set_ylabel('np.sqrt(2)	*	x[0]	*	x[1]')
ax.set_zlabel("x[1]	**	2")
ax.set_xticklabels([])
ax.set_yticklabels([])
ax.set_zticklabels([])
plt.show()
As	it	can	be	seen,	the	original	dataset	is	linear	separable	in	this	new	three	dimensional	space.
Practical	example	-	Breast	Cancer
Finally,	as	a	last	example,	we	will	apply	SVMs	on	the	Breast	Cancer	dataset.
In	[8]:
#	Loading	Breast	Cancer	dataset.
data	=	pd.read_csv('data/breast_cancer.csv')
#	Creating	a	LabelEncoder	and	transforming	the	dataset	labels.
le	=	LabelEncoder()
y	=	le.fit_transform(data['diagnosis'].values)
#	Extracting	the	instances	data.
X	=	data.drop('diagnosis',	axis=1).values
#	Splitting	into	train	and	test	sets.
X_train,	X_test,	y_train,	y_test	=	train_test_split(X,	y,	test_size=0.34,	stratify=y,	random_state=seed)
Since	this	dataset	has	high	dimensionality	and	probably	is	not	linearly	separable,	we	will	apply	a	SVM	with	Radial	Basis	Function	(RBF)	kernel.
In	[9]:
svm	=	SVC(kernel='rbf')
svm.fit(X_train,	y_train)
y_pred	=	svm.predict(X_test)
accuracy	=	accuracy_score(y_test,	y_pred)
print("SVM's	accuracy	score:	{}".format(accuracy))
Unfortunately,	SVMs	are	sensitive	to	data	scale.	Thus,	we	will	standartize	the	dataset	and	train	the	SVM	again.
In	[10]:
scaler	=	StandardScaler()
scaler.fit(X_train)
#	Normalizing	train	and	test	data.
X_train_scaled,	X_test_scaled	=	scaler.transform(X_train),	scaler.transform(X_test)
#	Training	SVM	with	normalized	data.
svm.fit(X_train_scaled,	y_train)
#	Testing	SVM	with	normalized	data.
y_pred	=	svm.predict(X_test_scaled)
accuracy	=	accuracy_score(y_test,	y_pred)
print("SVM's	accuracy	score:	{}".format(accuracy))
Multiclass	problems
SVMs	were	designed	to	deal	with	binary	classification	problems.	Several	approaches	are	available	to	deal	with	multiclass	problems.	Some	of
them	are:
One-vs-one	classifiers:	suppose	the	classification	problem	is	composed	by	k	classes.	Thus,	k(k − 1) /2	SVMs	are	fitted,	each	one	for	a
different	pair	of	classes.	For	prediction,	the	class	that	received	most	of	the	votes	is	returned	as	output.
One-vs-all	classifiers:	suppose	the	classification	problem	is	composed	by	k	classes.	Then,	k	different	classifiers	are	fitted,	one	for
each	class.
The	sklearn.svm.SVC	class	implements	the	one-vs-one	scheme.
SVM's	accuracy	score:	0.6288659793814433
SVM's	accuracy	score:	0.9845360824742269
Ensemble	methods
Ensemble	methods	combine	several	base	classifiers	in	order	to	improve	their	robustness	when	compared	to	their	predictions	alone.	Several
methods	have	been	proposed	in	the	machine	learning	literature.	Scikit-learn	provides	us	several	classes	to	fit	ensemble	method,	for
example,	VotingClassifier,	BaggingClassifier,	AdaBoostClassifier	and	RandomForestClassifier,	to	name	a	few.	These	classes	will	be	explained
in	the	sequence.
First	we	do	all	necessary	imports,	load	the	breast	cancer	dataset	and	define	a	method	to	plot	a	classifier's	decision	boundary.
In	[1]:
import	pandas	as	pd
import	numpy	as	np
import	matplotlib.pyplot	as	plt
from	sklearn.ensemble	import	VotingClassifier,	BaggingClassifier,	AdaBoostClassifier,	RandomForestClassifier
from	sklearn.tree	import	DecisionTreeClassifier
from	sklearn.neural_network	import	MLPClassifier
from	sklearn.neighbors	import	KNeighborsClassifier
from	sklearn.preprocessing	import	LabelEncoder,	StandardScaler
from	sklearn.model_selection	import	train_test_split
from	sklearn.metrics	import	accuracy_score
#	Random	seed.
seed	=	10
#	Loading	Iris	dataset.
data	=	pd.read_csv('data/iris.csv')
#	Creating	a	LabelEncoder	and	fitting	it	to	the	dataset	labels.
le	=	LabelEncoder()
le.fit(data['Name'].values)
#	Convertingdataset	str	labels	to	int	labels.
y	=	le.transform(data['Name'].values)
#	Extracting	the	instances	data.	In	this	example	we	will	consider	only	the	first	two	features	to	be	able	to
#	plot	the	data	and	the	decision	boundaries	of	the	classifiers.
X	=	data.drop('Name',	axis=1).values[:,	:2]
#	Splitting	into	train	and	test	sets.
X_train,	X_test,	y_train,	y_test	=	train_test_split(X,	y,	test_size=0.34,	stratify=y,	random_state=seed)
#	Method	to	plot	a	classifier's	decision	boundary.
#	This	code	is	based	on:
#	http://scikit-learn.org/stable/auto_examples/semi_supervised/plot_label_propagation_versus_svm_iris.html
def	plot_decision_boundary(classifier,	X,	y,	title):
				xmin,	xmax	=	np.min(X[:,	0])	-	0.05,	np.max(X[:,	0])	+	0.05
				ymin,	ymax	=	np.min(X[:,	1])	-	0.05,	np.max(X[:,	1])	+	0.05
				step	=	0.01
				xx,	yy	=	np.meshgrid(np.arange(xmin,	xmax,	step),	np.arange(ymin,	ymax,	step))
				Z	=	classifier.predict(np.hstack((xx.ravel()[:,	np.newaxis],	yy.ravel()[:,	np.newaxis])))
				Z	=	Z.reshape(xx.shape)
				colormap	=	plt.cm.Paired
				plt.contourf(xx,	yy,	Z,	cmap=colormap)
				color_map_samples	=	{0:	(0,	0,	.9),	1:	(1,	0,	0),	2:	(.8,	.6,	0)}
				colors	=	[color_map_samples[c]	for	c	in	y]
				plt.scatter(X[:,	0],	X[:,	1],	c=colors,	edgecolors='black')
				plt.xlim(xmin,	xmax)
				plt.ylim(ymin,	ymax)
				plt.title(title)
Voting	classifier
The	idea	of	the	Voting	Classifier	is	to	combine	different	types	of	classifiers	and	to	produce	a	prediction	as	the	majority	vote	among	them	or
the	argmax	of	the	mean	probability	of	a	class.	In	scikit-learn,	this	approach	is	implemented	in	the	VotingClassifier	class.
In	[2]:
plt.figure(figsize=(8,	8))
#	Fitting	a	Decision	Tree.
tree	=	DecisionTreeClassifier(min_samples_split=5,	min_samples_leaf=3,	random_state=seed)
tree.fit(X_train,	y_train)
plt.subplot(2,	2,	1)
plot_decision_boundary(tree,	X_train,	y_train,	'Decision	Tree	decision	boundary')
#	Fitting	a	MLP.
mlp	=	MLPClassifier(hidden_layer_sizes=(10,),	max_iter=10000,	random_state=seed)
mlp.fit(X_train,	y_train)
plt.subplot(2,	2,	2)
plot_decision_boundary(mlp,	X_train,	y_train,	'MLP	decision	boundary')
#	Fitting	a	kNN.
knn	=	KNeighborsClassifier(n_neighbors=3)
knn.fit(X_train,	y_train)
plt.subplot(2,	2,	3)
plot_decision_boundary(knn,	X_train,	y_train,	'kNN	decision	boundary')
#	Fitting	a	Voting	Classifier	by	combining	the	three	above	classifiers.
voting_clf	=	VotingClassifier(estimators=[('Tree',	tree),	('MLP',	mlp),	('kNN',	knn)],	voting='hard')
voting_clf.fit(X_train,	y_train)
plt.subplot(2,	2,	4)
plot_decision_boundary(voting_clf,	X_train,	y_train,	'Voting	Classifier	decision	boundary')
plt.tight_layout()
plt.show()
Bagging	classifier
Bagging	applies	the	same	classifier	on	subsamples	(usually	with	the	same	size)	of	the	original	dataset	with	replacement.	In	scikit-learn,	this
method	is	implemented	through	BaggingClassifier	class.	Its	predictions	return	the	label	with	highest	mean	probability	among	the	base
classifiers.	If	the	base	classifiers	do	not	implement	the	predict_proba	method,	this	class	predicts	the	label	by	majority	voting.
As	mentioned	in	scikit-learn's	documentation,	the	bagging	method	usually	works	well	with	more	complex	models	(such	as	fully	fitted
decision	trees).
In	[3]:
plt.figure(figsize=(8,	4))
tree	=	DecisionTreeClassifier(random_state=seed)
tree.fit(X_train,	y_train)
plt.subplot(1,	2,	1)
plot_decision_boundary(tree,	X_train,	y_train,	'Decision	Tree	decision	boundary')
bagging_clf	=	BaggingClassifier(base_estimator=tree,	n_estimators=50,	random_state=seed)
bagging_clf.fit(X_train,	y_train)
plt.subplot(1,	2,	2)
plot_decision_boundary(bagging_clf,	X_train,	y_train,	'Bagging	Classifier	decision	boundary')
plt.tight_layout()
plt.show()
Boosting	classifier
The	Boosting	method	tries	to	combine	several	weak	classifiers	(i.e.,	classifiers	that	are	slightly	better	than	random	classifiers)	into	a	strong
classifier.	At	each	step,	the	procedure	fits	a	new	classifier	with	different	weights	on	the	objects	from	the	training	set.	The	idea	is	simple,
objects	that	are	assigned	the	wrong	label	will	have	their	weights	increased	in	the	next	iteration,	while	the	others	will	have	their	weights
decreased	in	the	next	iteration.
The	most	popular	boosting	algorithm	of	is	AdaBoost.	In	scikit-learn,	it	is	implemented	in	the	AdaBoostClassifier	class.
In	[4]:
plt.figure(figsize=(8,	4))
tree	=	DecisionTreeClassifier(min_samples_split=5,	min_samples_leaf=5,	max_depth=3,	random_state=seed)
tree.fit(X_train,	y_train)
plt.subplot(1,	2,	1)
plot_decision_boundary(tree,	X_train,	y_train,	'Decision	Tree	decision	boundary')
boosting_clf	=	AdaBoostClassifier(n_estimators=50)
boosting_clf.fit(X_train,	y_train)
plt.subplot(1,	2,	2)
plot_decision_boundary(boosting_clf,	X_train,	y_train,	'AdaBoost	Classifier	decision	boundary')
plt.tight_layout()
plt.show()
Random	Forest	classifier
Random	Forest	consists	of	an	ensemble	method	composed	by	multiple	decision	trees.	Each	tree	is	trained	with	a	subsample	with
replacement	from	the	original	dataset	and,	at	each	step,	a	node	split	is	performed	by	choosing	the	best	split	among	a	random	subset	of	the
features	instead	of	the	best	split	overall.
Many	experimental	machine	learning	studies	suggest	that	Random	Forest	is	one	of	the	best	classifiers	from	the	literature.	In	scikit-learn,	this
algorithm	is	implemented	through	RandomForestClassifier	class.
In	[5]:
random_forest_clf	=	RandomForestClassifier(n_estimators=50,	random_state=seed)
random_forest_clf.fit(X_train,	y_train)
plt.figure(figsize=(5,	5))
plot_decision_boundary(random_forest_clf,	X_train,	y_train,	'Random	Forest	Classifier	decision	boundary')
plt.show()
	Instalação em Linux
	Instalação em Windows
	Referências e tutoriais
	Introdução à NumPy
	Arrays
	Exploração de dados
	Dados univariados
	Medidas de posição ou localidade
	Medidas de dispersão ou espalhamento
	Medidas de distribuição
	Dados multivariados
	Exemplo: Iris
	Visualização de dados
	Histogramas
	Scatter plots
	Box plots
	Exercícios
	Conjunto de dados
	Eliminação manual de atributos
	Amostragem
	Amostragem simples
	Amostragem estratificada
	Dados ausentes
	Dados redundantes
	Ruídos
	Transformação de dados
	Transformação de dados simbólicos para dados numéricos
	Transformação de dados numéricos para dados simbólicos
	Normalização de atributos numéricos
	Redução de dimensionalidade
	Principal Component Analysis (PCA)
	Filtragem por limiar de variância
	Exercícios (1)
	Aprendizado de máquina não supervisionado
	Medidas de (dis)similaridade
	Algoritmos hierárquicos
	Algoritmo k-means
	Algoritmo DBSCAN
	Validação de agrupamentos
	Critérios de validação relativa
	Índice de Dunn (ID)
	Largura de silhueta (LS)
	Critérios de validação interna
	Critérios de validação externa
	Índice Rand (IR)
	Índice Jaccard (IJ)
	Índice Rand Ajustado (IRA)
	Informação Mútua (IM)
	Informação Mútua Normalizada (IMN)
	Exemplos
	Exercícios (2)
	Aprendizado de máquina supervisionado
	Métodos baseados em distâncias entre objetos
	k-Nearest Neighbors (kNN)
	Exercícios (3)

Mais conteúdos dessa disciplina