Sobre os dados

Descrição dos dados

É um dataset do Kaggle no qual pode ser acessado aqui.

Contexto

O Zoo Animal Classification Dataset consiste originalmente em 101 animais de um zoológico e 16 variáveis com diversas características que os descrevem (os atributos), além da classe a que cada animal pertence (o alvo).

Esse conjunto de dados original é frequentemente usado no aprendizado de máquina, pois é um exemplo completo, mas simples, para praticar problemas de classificação com classes de vários rótulos. No entanto, como ele fornece apenas cem linhas, alguns algoritmos de ML podem não funcionar bem. É exatamente aí que esse conjunto de dados estendido se encaixa.

Conteudo

Como um complemento ao conjunto de dados de classificação de animais do zoológico original, este conjunto de dados fornece:

  • 43 animais de várias classes, exceto Mamíferos e Pássaros (já que essas últimas classes são muito frequentes no conjunto de dados original);
  • 70 animais de espécies mantidas pelo Zoológico de São Paulo, sendo principalmente animais da fauna brasileira em risco de extinção.

Descrevendo o dado

No arquivo zoo2.csv temos várias colunas no qual significam:

  • hair: se tem cabelo
  • feathers: se tem penas
  • eggs: se botar ovos
  • milk: se produz leite
  • airborne: se voar
  • aquatic: se é aquático
  • predator: se for um predador
  • toothed: se tem dentes
  • backbone: se tem uma espinha dosal
  • breathes: se tem ar respirando
  • venomous: se for venenoso
  • fins: se tem barbatanas
  • legs: número de pernas, que pode ser: {0,2,4,5,6,8}
  • tail: se tem cauda
  • domestic: se for doméstico
  • catsize: se for do tamanho de um gato
  • class_type: uma classe a que pertence (número inteiro entre 1 e 7)

Limitações

  • Podem haver atributos (colunas) faltantes para descrever todos os tipos possíveis de tipos de classe animal
  • Algumas colunas possuem somente 1 valor para todo o dataset (consideradas irrelevantes)

Analise exploratória

Primeiramente carregamos nossos dados:

data <- read_csv(here::here("data/zoo2.csv"),
  col_types = cols(
    animal_name = col_character(),
    hair = col_double(),
    feathers = col_double(),
    eggs = col_double(),
    milk = col_double(),
    airborne = col_double(),
    aquatic = col_double(),
    predator = col_double(),
    toothed = col_double(),
    backbone = col_double(),
    breathes = col_double(),
    venomous = col_double(),
    fins = col_double(),
    legs = col_double(),
    tail = col_double(),
    domestic = col_double(),
    catsize = col_double(),
    class_type = col_double()
))

glimpse(data)
## Rows: 43
## Columns: 18
## $ animal_name <chr> "turtle", "chameleon", "iguana", "lizard", "gecko", "pytho…
## $ hair        <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ feathers    <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ eggs        <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
## $ milk        <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ airborne    <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ aquatic     <dbl> 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1…
## $ predator    <dbl> 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0…
## $ toothed     <dbl> 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1…
## $ backbone    <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
## $ breathes    <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1…
## $ venomous    <dbl> 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ fins        <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0…
## $ legs        <dbl> 4, 4, 4, 4, 4, 0, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 4…
## $ tail        <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
## $ domestic    <dbl> 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1…
## $ catsize     <dbl> 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0…
## $ class_type  <dbl> 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5…

E visualizamos um sumário sobre as colunas:

data %>%
  summary()
##  animal_name             hair            feathers      eggs        milk  
##  Length:43          Min.   :0.00000   Min.   :0   Min.   :1   Min.   :0  
##  Class :character   1st Qu.:0.00000   1st Qu.:0   1st Qu.:1   1st Qu.:0  
##  Mode  :character   Median :0.00000   Median :0   Median :1   Median :0  
##                     Mean   :0.02326   Mean   :0   Mean   :1   Mean   :0  
##                     3rd Qu.:0.00000   3rd Qu.:0   3rd Qu.:1   3rd Qu.:0  
##                     Max.   :1.00000   Max.   :0   Max.   :1   Max.   :0  
##     airborne         aquatic          predator         toothed      
##  Min.   :0.0000   Min.   :0.0000   Min.   :0.0000   Min.   :0.0000  
##  1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:0.0000  
##  Median :0.0000   Median :0.0000   Median :0.0000   Median :0.0000  
##  Mean   :0.1628   Mean   :0.4651   Mean   :0.3023   Mean   :0.4419  
##  3rd Qu.:0.0000   3rd Qu.:1.0000   3rd Qu.:1.0000   3rd Qu.:1.0000  
##  Max.   :1.0000   Max.   :1.0000   Max.   :1.0000   Max.   :1.0000  
##     backbone         breathes         venomous           fins       
##  Min.   :0.0000   Min.   :0.0000   Min.   :0.0000   Min.   :0.0000  
##  1st Qu.:0.0000   1st Qu.:1.0000   1st Qu.:0.0000   1st Qu.:0.0000  
##  Median :1.0000   Median :1.0000   Median :0.0000   Median :0.0000  
##  Mean   :0.5814   Mean   :0.7674   Mean   :0.1163   Mean   :0.1628  
##  3rd Qu.:1.0000   3rd Qu.:1.0000   3rd Qu.:0.0000   3rd Qu.:0.0000  
##  Max.   :1.0000   Max.   :1.0000   Max.   :1.0000   Max.   :1.0000  
##       legs            tail           domestic         catsize      
##  Min.   :0.000   Min.   :0.0000   Min.   :0.0000   Min.   :0.0000  
##  1st Qu.:0.000   1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:0.0000  
##  Median :4.000   Median :0.0000   Median :0.0000   Median :0.0000  
##  Mean   :3.209   Mean   :0.4884   Mean   :0.1163   Mean   :0.3721  
##  3rd Qu.:6.000   3rd Qu.:1.0000   3rd Qu.:0.0000   3rd Qu.:1.0000  
##  Max.   :8.000   Max.   :1.0000   Max.   :1.0000   Max.   :1.0000  
##    class_type   
##  Min.   :3.000  
##  1st Qu.:3.000  
##  Median :5.000  
##  Mean   :4.837  
##  3rd Qu.:6.000  
##  Max.   :7.000

Assim como foi informado alguns dos atributos possuem somente um unico valor em todo o dataset, como é o caso do fearthes, eggs e milk. Outra observação, é que a variavel preditora class_type possui somente valores de 3 a 7.

data = data %>%
  select(animal_name, hair, airborne, aquatic, predator, toothed, backbone, breathes, venomous, fins, legs, tail, domestic, catsize, class_type)

Olhemos então para o número de pernas (legs), por ser nossa única variável não-binária, em relação ao tipo de classe animal (class_type) temos:

data %>%
  ggplot(aes(x = legs, y = class_type)) +
  geom_count() +
  labs(
    title = "Tipo de classe (class_type) x número de pernas (legs)",
  )

Vemos que algumas tipos de classe possuem somente uma caractéristica para seus indivíduos, em relação a quantidade de pernas, e outras possuem mais. O tipo de classe 6 possui seus indivíduos com 6 pernas e é uma característica exclusiva. Já o tipo de classe 5, possui indivíduos com 2 e 4 pernas, em que com 2 parece ser algum caso especial e único no dataset, e com 4 pernas temos outros tipos de classe que possuem essa característica. Podemos ver que o atributo legs pode ser um bom divisor inicial para determinar o tipo de classe. Mas será que realmente é um bom atributo? Criemos um modelo somente com essa variável para descobrir isso com o R².

data_t = data %>% 
  mutate(dec = as.factor(class_type))
  
bm <- glm(class_type ~ legs,
          data = data_t)

tidy(bm, conf.int = TRUE, exponentiate = TRUE) %>% 
  select(-statistic, -p.value)
pscl::pR2(bm)
## fitting null model for pseudo-r2
##          llh      llhNull           G2     McFadden         r2ML         r2CU 
## -75.56345158 -76.86110315   2.59530315   0.01688307   0.05857057   0.06025886

Vemos que o R² é muito baixo (0.016) usando somente legs. Diante disso, como escolher qual o melhor atributo? Criemos então vários modelos com todos os atributos e analisamos seus coeficientes e R²

bm <- glm(class_type ~ hair,
          data = data_t)

tidy(bm, conf.int = TRUE, exponentiate = TRUE) %>% 
  select(-statistic, -p.value)
pscl::pR2(bm)
## fitting null model for pseudo-r2
##           llh       llhNull            G2      McFadden          r2ML 
## -76.527324296 -76.861103153   0.667557714   0.004342624   0.015404713 
##          r2CU 
##   0.015848751
bm <- glm(class_type ~ airborne,
          data = data_t)

tidy(bm, conf.int = TRUE, exponentiate = TRUE) %>% 
  select(-statistic, -p.value)
pscl::pR2(bm)
## fitting null model for pseudo-r2
##          llh      llhNull           G2     McFadden         r2ML         r2CU 
## -73.97038534 -76.86110315   5.78143562   0.03760963   0.12580515   0.12943147
bm <- glm(class_type ~ aquatic,
          data = data_t)

tidy(bm, conf.int = TRUE, exponentiate = TRUE) %>% 
  select(-statistic, -p.value)
pscl::pR2(bm)
## fitting null model for pseudo-r2
##           llh       llhNull            G2      McFadden          r2ML 
## -76.110172828 -76.861103153   1.501860648   0.009769965   0.034324084 
##          r2CU 
##   0.035313470
bm <- glm(class_type ~ predator,
          data = data_t)

tidy(bm, conf.int = TRUE, exponentiate = TRUE) %>% 
  select(-statistic, -p.value)
pscl::pR2(bm)
## fitting null model for pseudo-r2
##         llh     llhNull          G2    McFadden        r2ML        r2CU 
## -74.1160933 -76.8611032   5.4900197   0.0357139   0.1198605   0.1233155
bm <- glm(class_type ~ toothed,
          data = data_t)

tidy(bm, conf.int = TRUE, exponentiate = TRUE) %>% 
  select(-statistic, -p.value)
pscl::pR2(bm)
## fitting null model for pseudo-r2
##         llh     llhNull          G2    McFadden        r2ML        r2CU 
## -68.0413751 -76.8611032  17.6394562   0.1147489   0.3364957   0.3461951
bm <- glm(class_type ~ backbone,
          data = data_t)

tidy(bm, conf.int = TRUE, exponentiate = TRUE) %>% 
  select(-statistic, -p.value)
pscl::pR2(bm)
## fitting null model for pseudo-r2
##         llh     llhNull          G2    McFadden        r2ML        r2CU 
## -45.1505244 -76.8611032  63.4211575   0.4125699   0.7712008   0.7934306
bm <- glm(class_type ~ breathes,
          data = data_t)

tidy(bm, conf.int = TRUE, exponentiate = TRUE) %>% 
  select(-statistic, -p.value)
pscl::pR2(bm)
## fitting null model for pseudo-r2
##           llh       llhNull            G2      McFadden          r2ML 
## -7.684881e+01 -7.686110e+01  2.459060e-02  1.599678e-04  5.717109e-04 
##          r2CU 
##  5.881904e-04
bm <- glm(class_type ~ venomous,
          data = data_t)

tidy(bm, conf.int = TRUE, exponentiate = TRUE) %>% 
  select(-statistic, -p.value)
pscl::pR2(bm)
## fitting null model for pseudo-r2
##           llh       llhNull            G2      McFadden          r2ML 
## -76.682189824 -76.861103153   0.357826657   0.002327749   0.008287022 
##          r2CU 
##   0.008525894
bm <- glm(class_type ~ fins,
          data = data_t)

tidy(bm, conf.int = TRUE, exponentiate = TRUE) %>% 
  select(-statistic, -p.value)
pscl::pR2(bm)
## fitting null model for pseudo-r2
##          llh      llhNull           G2     McFadden         r2ML         r2CU 
## -75.41111562 -76.86110315   2.89997507   0.01886504   0.06521739   0.06709727
bm <- glm(class_type ~ tail,
          data = data_t)

tidy(bm, conf.int = TRUE, exponentiate = TRUE) %>% 
  select(-statistic, -p.value)
pscl::pR2(bm)
## fitting null model for pseudo-r2
##         llh     llhNull          G2    McFadden        r2ML        r2CU 
## -43.5175803 -76.8611032  66.6870457   0.4338153   0.7879348   0.8106469
bm <- glm(class_type ~ domestic,
          data = data_t)

tidy(bm, conf.int = TRUE, exponentiate = TRUE) %>% 
  select(-statistic, -p.value)
pscl::pR2(bm)
## fitting null model for pseudo-r2
##          llh      llhNull           G2     McFadden         r2ML         r2CU 
## -73.86561549 -76.86110315   5.99097533   0.03897274   0.13005476   0.13380356
bm <- glm(class_type ~ catsize,
          data = data_t)

tidy(bm, conf.int = TRUE, exponentiate = TRUE) %>% 
  select(-statistic, -p.value)
pscl::pR2(bm)
## fitting null model for pseudo-r2
##          llh      llhNull           G2     McFadden         r2ML         r2CU 
## -70.31342588 -76.86110315  13.09535455   0.08518844   0.26253978   0.27010745

Observamos então que os melhores R² foram com tail (0.4338153), backbone (0.4125699) e toothed (0.1147489), montamos um modelo com os 3 e temos um R² de 0.54. Vemos que com isso, os fatores: ter cauda, ter espinha dosal ou ter dentes, ajuda bastante na classificação dos tipos. Mas e se adicionarmos mais variáveis, como fica o R²? Pegamos o 4º (catsize, R² 0.08518844) e 5º (domestic, R² 0.03897274) atributo com maior R² e analisamos o ganho.

bm <- glm(class_type ~ tail + backbone + toothed,
          data = data_t)

tidy(bm, conf.int = TRUE, exponentiate = TRUE) %>% 
  select(-statistic, -p.value)
pscl::pR2(bm)
## fitting null model for pseudo-r2
##         llh     llhNull          G2    McFadden        r2ML        r2CU 
## -34.8657468 -76.8611032  83.9907127   0.5463798   0.8581907   0.8829280

É observado que quase não há ganho, sendo assim, não importantes para nosso estudo que busca descrever quais as variáveis para o modelo de regressão que melhor explica o tipo de classe animal.

bm <- glm(class_type ~ tail + backbone + toothed + catsize + domestic,
          data = data_t)

tidy(bm, conf.int = TRUE, exponentiate = TRUE) %>% 
  select(-statistic, -p.value)
pscl::pR2(bm)
## fitting null model for pseudo-r2
##         llh     llhNull          G2    McFadden        r2ML        r2CU 
## -34.6921696 -76.8611032  84.3378671   0.5486382   0.8593310   0.8841011