EDA de um ângulo diferente

imagem

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:

  1. Pré-processamento pequeno
  2. Visualização do preditor
  3. Discretização de variáveis
  4. Correlationfunnel
  5. Correlações cruzadas classificadas
  6. easyalluvial

Terminamos com o introdutório e tomamos como base um exemplo prático.

Abordagem de exemplo
Inicialmente, 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) 

títulomachofêmea
Senhor5170 0
Senhora0 0126
Senhorita0 0185
Mestre40.0 0
Senhor80 0
Rev60 0
Dr61
Senhora0 02

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) 

imagem

É 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() 

imagem

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) 

imagem

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:

imagem

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:

  1. Correlationfunnel - mostra o efeito de valores preditores individuais em um destino (ou seja, você pode chamá-lo de aprendizado supervisionado pela EDA)
  2. 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)
  3. 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)
imagem

A metodologia está bem descrita na vinheta da biblioteca; darei um fragmento de cálculo da correlação por valores binários

imagem

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! 

imagem

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

imagem

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) 

imagem

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) 

imagem

easyalluvial


Exploração de dados com parcelas aluviais

imagem

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) 

imagem

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) 

imagem

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) 

imagem

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 aluviais
E 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) 

imagem

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 :)

Source: https://habr.com/ru/post/pt480532/


All Articles