
Não falaremos sobre comida, mas sobre
análise exploratória de dados (EDA ), que é um prelúdio para qualquer ML severa.
Sejamos honestos, o processo é bastante chato e, para obter pelo menos algumas informações significativas sobre nossos dados, você precisa gastar tempo suficiente ativamente usando sua biblioteca de visualização favorita.
Agora imagine que somos bastante preguiçosos (mas curiosos) e seguiremos este postulado ao longo deste artigo.
Com base nisso, nos perguntamos a seguinte pergunta: existe uma ferramenta tão complicada na natureza que permitiria pressionar CTRL + ENTER no seu IDE favorito e exibir em uma única tela (sem rolar para baixo e inúmeras facetas microscópicas) uma imagem completa com informações úteis sobre nosso conjunto de dados?
Ao mesmo tempo, lembramos de um pensamento diferente - se esse instrumento existir, ele não substituirá o EDA clássico, mas será uma grande ajuda para nós nesses casos em que você não precisará gastar horas em visualização para enfatizar rapidamente os principais padrões em nossos dados.
A estrutura deste artigo:
- Pré-processamento pequeno
- Visualização do preditor
- Discretização de variáveis
- Correlationfunnel
- Correlações cruzadas classificadas
- easyalluvial
Terminamos com o introdutório e tomamos como base um exemplo prático.
Abordagem de exemploInicialmente, eu queria pegar uma matriz de dados obscura, mas no final percebi que, por exemplo, isso não seria muito bom - os padrões encontrados podem parecer não óbvios e, portanto, controversos, mas nosso objetivo é preparar a matriz com algoritmos que não possuem informações a priori e nos mostrarão o que já sabemos, confirmando nossa viabilidade.
O Titanic me pareceu o mais conveniente como exemplo, seu tamanho não é muito pequeno como o Iris, possui variáveis não informativas, é bem estudado e possui preditores claros e, principalmente, uma base histórica.
Além disso, encontrei um artigo sobre Habré em que o autor conduziu uma EDA bastante meticulosa desse conjunto de dados e, com base nas imagens, demonstrei as descobertas. Este será um tipo de nossa linha de base.
Link para o artigo com um grande nome para nossa "Linha de base_EDA":
Titanic no Kaggle: Você não vai ler este post até o fim .
Para não se preocupar com o download / leitura de csv da rede, capturamos imediatamente o conjunto de dados original do CRAN
install.packages("titanic") data("titanic_train",package="titanic")
Breve pré-processamento
Este exemplo é tão circulado na rede ao pré-processar para cima e para baixo que não vou particularmente abordar este tópico, estou fazendo coisas básicas: extraio o nome da gonoratora (título) como um importante preditor e o uso para preencher as diferenças de idade.
library(tidyverse) titanic_train %>% str d <- titanic_train %>% as_tibble %>% mutate(title=str_extract(Name,"\\w+\\.") %>% str_replace(fixed("."),"")) %>% mutate(title=case_when(title %in% c('Mlle','Ms')~'Miss', # title=='Mme'~ 'Mrs', title %in% c('Capt','Don','Major','Sir','Jonkheer', 'Col')~'Sir', title %in% c('Dona', 'Lady', 'Countess')~'Lady', TRUE~title)) %>% mutate(title=as_factor(title), Survived=factor(Survived,levels = c(0,1),labels=c("no","yes")), Sex=as_factor(Sex), Pclass=factor(Pclass,ordered = T)) %>% group_by(title) %>% # - mutate(Age=replace_na(Age,replace = median(Age,na.rm = T))) %>% ungroup # table(d$title,d$Sex)
Nem todos os iogurtes são igualmente saudáveis ...
Normalmente, no início da análise, deixo de lado as variáveis não informativas (deixo de lado e não as excluo permanentemente, porque quando eu tiro o máximo proveito do modelo, a engenharia de algumas das variáveis pendentes fornece uma certa porcentagem do ganho de qualidade do modelo).
As métricas para avaliar a "utilidade" de uma variável são freqRatio (a proporção das frequências do valor mais popular em relação ao segundo valor em frequência) e percentUnique (potência ou cardinalidade - a proporção de um número único de valores do número total de valores)
Ajuda detalhada pode ser vista no pacote de intercalação
?caret::nearZeroVar
(feat.scan <- caret::nearZeroVar(x = d,saveMetrics = T) %>% rownames_to_column("featName") %>% as_tibble)

É mais conveniente para mim monitorar variáveis em um plano bidimensional (registrando os dois eixos para que os pontos não sejam colocados em excesso em uma pequena pilha devido a pontos extremos).
Eu nunca me perguntei se essa etapa é a EDA, mas enquanto escrevia este artigo, pensei: agora estamos realizando uma análise exploratória de uma certa utilidade dos preditores, a avaliação visual deles;
# install.packages("ggrepel") library(ggrepel) ggplot(feat.scan,aes(x=percentUnique,y=freqRatio,label=featName,col=featName))+ geom_point(size=2)+ geom_text_repel(data = feat.scan,size=5)+scale_x_log10()+scale_y_log10()+theme_bw()

Consideramos preditores extremos como não informativos, tanto em termos de potência (eixo X) ou razão de frequência (eixo Y) e, portanto, deixamos de lado:
PassengerId Nome; Ticket Cabine
useless.feature <- c("PassengerId","Name","Ticket","Cabin") d <- d %>% select_at(vars(-useless.feature))
Este universo é discreto
Para entender como as bibliotecas listadas abaixo preparam os dados - nesta seção, mostramos com pequenos exemplos o que acontece nessas bibliotecas no estágio de preparação dos dados.
Na primeira etapa, é necessário trazer todos os dados para um único tipo - geralmente os dados em um conjunto podem ser categóricos e numéricos; além disso, os números podem ter discrepâncias e os dados categóricos podem ser categorias raras.
Para converter variáveis contínuas em categóricas, podemos decompor nossos números em compartimentos com um determinado período de amostragem.
O exemplo mais simples de decomposição em 5 bin:
iris %>% as_tibble %>% mutate_if(is.numeric,.funs = ggplot2::cut_number,n=5)

Para obter a força e a diretividade das relações dos elementos individuais entre os preditores, um segundo truque é usado -
uma codificação quente library(recipes) iris %>% as_tibble %>% mutate_if(is.numeric,cut_number,n=5) %>% recipe(x = .) %>% step_dummy(all_nominal(),one_hot = T) %>% prep %>% juice %>% glimpse
Em vez de cinco preditores, agora temos 23 deles, mas binários:

Em geral, os truques de conversão terminam aí, mas o trabalho de 2 de 3 bibliotecas para nossa EDA "não clássica" começa com esses estágios.
Em seguida, apresento a funcionalidade de três bibliotecas de visualização:
- Correlationfunnel - mostra o efeito de valores preditores individuais em um destino (ou seja, você pode chamá-lo de aprendizado supervisionado pela EDA)
- Lares - mostra o efeito de valores preditores individuais em outros valores individuais de outros preditores (ou seja, você pode chamá-lo de aprendizado não supervisionado da EDA)
- easyalluvial - mostra a relação cumulativa dos valores agrupados dos principais preditores de "X" por destino (ou seja, você pode chamá-lo de aprendizado supervisionado pela EDA)
É evidente que sua funcionalidade é diferente, portanto, demonstrando essas bibliotecas, citarei as conclusões do autor no
artigo de nossa "Linha de base_EDA", dependendo da funcionalidade descrita acima deste pacote. (Por exemplo, se o autor mostrar a dependência da idade em relação à sobrevivência, inserirei essa citação no funil de Correlação, se a idade estiver na classe, em Lares etc.)
A primeira biblioteca está no palco.
correlação
correlacionar funil é acelerar a Análise Exploratória de Dados (EDA)
A metodologia está bem descrita na
vinheta da biblioteca; darei um fragmento de cálculo da correlação por valores binários

A biblioteca assume a presença de um alvo (variável dependente) em nossos dados e imediatamente em uma imagem mostra a força e a direção do relacionamento e também classifica em ordem decrescente essa força formando um funil visual (na verdade, é daí que o nome vem).
As funções de binarização incorporadas à biblioteca permitem reduzir pequenas categorias em Outros.
Como a biblioteca não funciona com variáveis inteiras, as converteremos em numérico e retornaremos ao nosso Titanic.
#install.packages("correlationfunnel") library(correlationfunnel) d <- d %>% mutate_if(is.integer,as.numeric) d %>% binarize(n_bins = 5,thresh_infreq = .02,one_hot = T) %>% # correlate(target = Survived__yes) %>% plot_correlation_funnel() # "interactive = T" - plotly!

No eixo X, temos a força e a direção da correlação; no eixo Y, nossos preditores são classificados em ordem decrescente. O primeiro sempre reflete o alvo como ele tem a correlação mais forte consigo mesmo (-1; 1).
Vamos verificar como as conclusões deste gráfico se sobrepõem às conclusões do autor da nossa "Linha de base_EDA".
O gráfico a seguir confirma a teoria de que quanto maior a classe da cabine de passageiros, maior a chance de sobrevivência. (Por "acima" ", quero dizer a ordem inversa, porque a primeira classe é maior que a segunda e, principalmente, a terceira.)
O funil mostra que a classe é o terceiro preditor em termos de força de correlação e, de fato, na 3ª classe, a correlação inversa, na 1ª, é fortemente positiva.
Compare as chances de sobrevivência de homens e mulheres. Os dados confirmam a teoria expressa anteriormente.
(Em geral, já podemos dizer que os principais fatores do modelo serão o sexo do passageiro)
O funil mostra que o sexo do passageiro é o 2º de acordo com o grau de correlação, o sexo feminino é correlacionado com a sobrevivência, o sexo masculino é correlacionado com a morte.
Você também pode testar a hipótese de que os mais jovens sobrevivem, porque eles se movem mais rápido, nadam melhor etc.
Como você pode ver, uma dependência explícita não é visível aqui.
O funil realmente fala da fraca significância desse preditor (lembro que o gonorante / título contém idade, razão pela qual a idade não é tão significativa), mas mesmo aqui o funil mostra que há mais chances de sobreviver nas categorias "menos infinito - 20 anos" (ou seja, crianças ) e 30-38 (pessoas ricas, possivelmente 1 classe).
Vamos introduzir um indicador como o Percentual de Sobrevivência e examinar sua dependência dos grupos que surgiram no estágio anterior
(o grupo do autor significa o título).
O funil confirma totalmente as descobertas do autor
Agora, vejamos as informações que podem ser obtidas com o número de parentes no navio.
É muito provável que a ausência de parentes, assim como um grande número, afete negativamente a sobrevivência.
O SibSP no funil diz claramente a mesma coisa.
E, claro, além das conclusões do autor, aqui você pode ver outros padrões, deixarei o prazer da contemplação para o leitor
Lares
Encontre insights com correlações cruzadas classificadas
O autor desta biblioteca foi ainda mais longe - ele mostra as dependências não apenas do destino, mas também de tudo.
As correlações cruzadas classificadas não apenas explicam os relacionamentos de um recurso de destino específico com o restante, mas o relacionamento de todos os valores em seus dados em um formato tabular fácil de usar e entender.
Ele converte automaticamente colunas categóricas em numéricas com uma codificação ativa (1s e 0s) e outros agrupamentos inteligentes, como rótulos de "outros", para valores não muito frequentes e novos recursos desatualizados.
Usando o link acima, você pode ver um exemplo em que o autor alimenta o conjunto de dados de Star Wars em seu pacote e demonstra as dependências encontradas. Fiquei preso na página dele, muito bem.
Vamos tentar o nosso exemplo.
# , : # devtools::install_github("laresbernardo/lares") library(lares) corr_cross(df = d,top = 30)

Além da interseção com as conclusões baseadas em aspas, em Correlationfunnell, apresentamos algumas citações que podemos ver aqui, independentemente do destino:
Outros padrões também podem ser encontrados. Existe uma correlação negativa entre idade e classe, o que provavelmente se deve a passageiros mais velhos, que poderiam pagar com mais frequência por uma cabine mais cara.
Na citação acima, o autor tira essa conclusão sobre a análise de correlação de 2 campos agregados, mas com a codificação de um a quente, isso é evidente a partir da forte correlação positiva entre Idade + P_Classe_1.
Além disso, o preço do bilhete e a classe estão intimamente relacionados (alto coeficiente de correlação), o que é bastante esperado.
Terceira linha acima: Tarifa + P_Class_1
Além de cruzar com as conclusões do autor, aqui podemos enfatizar coisas muito mais interessantes, deixarei também o prazer da contemplação para o leitor.
Além da seleção opcional dos principais insights X mais poderosos, você também pode refletir toda a imagem e o local desses pontos significativos na massa total
corr_cross(df = d,type=2)

easyalluvial
Exploração de dados com parcelas aluviais
Aqui, como nos 2 pacotes anteriores, o autor realiza a binarização de variáveis numéricas no início, mas seus caminhos com essas bibliotecas divergem: em vez de {One-HotEncoding + correlation}, a biblioteca apresenta o X principal dos preditores mais interessantes (o usuário decide quais transferir) ) por valores, formando fluxos cuja cor depende do destino e a largura do fluxo no número de observações nesse fluxo.
As variáveis numéricas são decompostas em categorias HH (Alta Alta), MH (Média Alta), M (Média), ML (Média Baixa), LL (Baixa Baixa)
Primeiro, vamos pegar os preditores mais significativos com base no gráfico do correlacionado funil:
cor.feat <- c("title","Sex","Pclass","Fare")
Em seguida, fazemos um cronograma
# install.packages("easyalluvial") library(easyalluvial) al <- d %>% select(Survived,cor.feat) %>% alluvial_wide(fill_by = "first_variable") add_marginal_histograms(p = al,data_input = d,keep_labels = F)

Para as citações do autor, redesenhamos o gráfico usando os preditores apropriados.
cor.feat <- c("Sex","Pclass","Age") al <- d %>% select(Survived,cor.feat) %>% alluvial_wide(fill_by = "first_variable") add_marginal_histograms(p = al,data_input = d,keep_labels = F)

Por exemplo, o gráfico a seguir mostra claramente que os principais grupos de sobreviventes são mulheres do primeiro e do segundo ano de todas as idades.
O gráfico também mostra que as mulheres sobreviventes do terceiro ano também não são um grupo pequeno
E entre os homens, todos os meninos com menos de 15 anos sobreviveram, exceto a terceira classe de serviço e uma pequena proporção de homens mais velhos e principalmente da primeira classe.
O mencionado é confirmado, mas novamente vemos os fluxos de homens sobreviventes da classe 3 na categoria de idade LL, ML.
Tudo acima foi sobre o pacote “easyalluvial”, mas o autor escreveu um segundo pacote “parcats” que, no topo da plotagem, torna o gráfico acima interativo (como no título desta seção).
Isso possibilita não apenas ver o contexto da dica de ferramenta, mas também reorientar os fluxos para uma melhor percepção visual. (infelizmente, enquanto a biblioteca não é muito otimizada e fica mais lenta no titanic)
# install.packages("parcats") library(parcats) cor.feat <- c("title","Sex","Pclass","Fare") a <- d %>% select(Survived,cor.feat) %>% alluvial_wide(fill_by = "first_variable") parcats(p = a,marginal_histograms = T,data_input = d)

Bônus
A biblioteca easyalluvial, além da análise exploratória de dados, também pode ser usada como intérprete para modelos de caixa preta (modelos que analisam parâmetros que não podem ser entendidos - por qual lógica o modelo fornece uma resposta com base em determinados preditores).
Link para o artigo do autor:
Visualize a resposta do modelo com gráficos aluviaisE a peculiaridade é que, de todas as bibliotecas que eu vi, o máximo em um gráfico explicou a resposta da caixa preta em não mais do que o sistema de coordenadas bidimensionais (uma para cada preditor), a cor explicou a resposta.
A biblioteca easyalluvial permite fazer isso por mais de 2 preditores ao mesmo tempo (é melhor não se deixar levar, é claro).
Por exemplo, vamos treinar uma floresta aleatória em nossa matriz de dados e refletir a explicação de uma floresta aleatória usando três preditores.
library(ranger) m <- ranger(formula = Survived~.,data = d,mtry = 6,min.node.size = 5, num.trees = 600, importance = "permutation") library(easyalluvial) (imp <- importance(m) %>% as.data.frame %>% easyalluvial::tidy_imp(imp = .,df=d)) # # N- ( . !) dspace <- get_data_space(df = d,imp,degree = 3) # pred = predict(m, data = dspace) alluvial_model_response(pred$predictions, dspace, imp, degree = 3)
Além disso, o autor possui um conector para os modelos CARET (não sei o quão relevante isso está considerando agora nos modelos de organização)
library(caret) trc <- trainControl(method = "none") m <- train(Survived~.,data = d,method="rf",trControl=trc,importance=T) alluvial_model_response_caret(train = m,degree = 4,bins=5,stratum_label_size = 2.8)

Conclusão
Repito mais uma vez que não peço a substituição do EDA clássico, mas concordo que é bom quando existe uma alternativa que economiza muito tempo, especialmente considerando que as pessoas são naturalmente preguiçosas o suficiente, e esse, como você sabe, é o motor do progresso :)