1. Considerações iniciais

Nesse artigo seguiremos com um roteiro para analisar e prever as vendas utilizando o banco de dados Bigmart, uma rede de varejo. Esse artigo é um desafio proposto pelo site https://www.analyticsvidhya.com com dados de 2013. Seguiremos como proposto pelo desafio.

Seguiremos os seguintes passos:
1. Formular uma hipótese - Tentar entender o problema e pensar em possíveis causas que aumentam ou diminuem as vendas
2. Explorar os dados - Veremos os dados como estão dispostos como números ou categorias e tirar insights basedos em nossas hipóteses
3. Limpar os dados - Remover outliers e dados faltantes, decidiremos o que fazer com os dados
4. Modificar os dados - Criar novas variáveis de acordo com o que observamos na exploração de dados e em nossas hipóteses para análise
5. Construir um modelo - fazer modelos preditivos com os dados

Entendo o problema

“The data scientists at BigMart have collected 2013 sales data for 1559 products across 10 stores in different cities. Also, certain attributes of each product and store have been defined. The aim is to build a predictive model and find out the sales of each product at a particular store.
Using this model, BigMart will try to understand the properties of products and stores which play a key role in increasing sales.”

Formulando a hipótese

Em ciência de dados entender o problema e definir como aborda-lo é essencial e o mais importante. Esse passo, faremos antes mesmo de olhar os dados e assim podemos definir como queremos chegar a soluçãoo e como queremos fazer.
A ideia aqui é entender quais características dos produtos e lojas que impactam no aumento das vendas, vamos pensar e surir com algumas hipósteses.

A nível de loja:
Tamanho da Cidade
Tamanho da Loja
Conservação
Tempo de loja
Marketing
Localidade da loja na cidade
Experiencia de compra

A nível de produtos:
Reputação das marcas vendidas
Qualidade dos produtos
Utilidades dos produtos
Disposição dos produtos
Visibilidade dos produtos
Tamanho das prateleiras expostas
Propaganda ou ativações
Produtos em Oferta
Distancia entre produtos relacionados

2. Explorando os dados

Nesssa etapa iremos avaliar os dados e possíveis erros e irregularidades apresentadas. Nesse momento iremos comparar as informações dos dados apresentados no banco de dados e os necessários pelas nossas hipóteses. O Banco de dados tem dois arquivos, test e train com 561 e 8523 obs, respectivamente. E fornecido do site temos o dicionário do banco de dados:

Data Dictionary
Variable Description
Item_Identifier Unique product ID
Item_Weight Weight of product
Item_Fat_Content Whether the product is low fat or not
Item_Visibility The % of total display area of all products in a store allocated to the particular product
Item_Type The category to which the product belongs
Item_MRP Maximum Retail Price (list price) of the product
Outlet_Identifier Unique store ID
Outlet_Establishment_Year The year in which store was established
Outlet_Size The size of the store in terms of ground area covered
Outlet_Location_Type The type of city in which the store is located
Outlet_Type Whether the outlet is just a grocery store or some sort of supermarket
Item_Outlet_Sales Sales of the product in the particulat store. This is the outcome variable to be predicted.

Vamos listar quais hipóteses possuímos são descritas pelas variáveis:
Tamanho da Cidade
Tamanho da Loja
Tempo de loja Qualidade dos produtos*
Utilidades dos produtos
Reputação das marcas vendidas
Visibilidade dos produtos

Hipóteses não descritas pelos nossos dados:
Conservação da Loja Marketing
Localidade da loja na cidade
Experiencia de compra
Disposição dos produtos
Tamanho das prateleiras expostas
Propaganda ou ativações
Produtos em Oferta
Distância entre produtos relacionados

Notamos que possuímos apenas 6 variáveis presentes nos dados que fazem parte da nossa hipótese inicial e uma que poderíamos adaptar, que seriam os produtos low fat como sendo uma qualidade do produto.

Começamos com o carregamento das bibliotecas que iremos utilizar e com a importação dos dados, no meu caso estão na pasta data no diretório do repositório. E usaremos o pacote tydeverse

library(readr)
library(tidyverse)
test <- read_csv("data/Test.csv")
train <- read_csv("data/Train.csv")

Para manusear os dados, unimos os dois data sets e posteriormente separamos novamente, em geral é uma boa idéia pois não geramos problemas ao manusear os dados separadamente.

dados <- dplyr::bind_rows(test, train)

Vamos dar uma olhada nos dados que faltam do nosso banco de dados

colSums(is.na(dados))
##           Item_Identifier               Item_Weight 
##                         0                      2439 
##          Item_Fat_Content           Item_Visibility 
##                         0                         0 
##                 Item_Type                  Item_MRP 
##                         0                         0 
##         Outlet_Identifier Outlet_Establishment_Year 
##                         0                         0 
##               Outlet_Size      Outlet_Location_Type 
##                      4016                         0 
##               Outlet_Type         Item_Outlet_Sales 
##                         0                      5681

Veja que Item_Outlet_Sales é a variavel para predição, logo os dados faltantes são dos dados test.csv.

Vamos analisar cada uma das variáveis:

summary(dados)
##  Item_Identifier     Item_Weight     Item_Fat_Content   Item_Visibility  
##  Length:14204       Min.   : 4.555   Length:14204       Min.   :0.00000  
##  Class :character   1st Qu.: 8.710   Class :character   1st Qu.:0.02704  
##  Mode  :character   Median :12.600   Mode  :character   Median :0.05402  
##                     Mean   :12.793                      Mean   :0.06595  
##                     3rd Qu.:16.750                      3rd Qu.:0.09404  
##                     Max.   :21.350                      Max.   :0.32839  
##                     NA's   :2439                                         
##   Item_Type            Item_MRP      Outlet_Identifier 
##  Length:14204       Min.   : 31.29   Length:14204      
##  Class :character   1st Qu.: 94.01   Class :character  
##  Mode  :character   Median :142.25   Mode  :character  
##                     Mean   :141.00                     
##                     3rd Qu.:185.86                     
##                     Max.   :266.89                     
##                                                        
##  Outlet_Establishment_Year Outlet_Size        Outlet_Location_Type
##  Min.   :1985              Length:14204       Length:14204        
##  1st Qu.:1987              Class :character   Class :character    
##  Median :1999              Mode  :character   Mode  :character    
##  Mean   :1998                                                     
##  3rd Qu.:2004                                                     
##  Max.   :2009                                                     
##                                                                   
##  Outlet_Type        Item_Outlet_Sales 
##  Length:14204       Min.   :   33.29  
##  Class :character   1st Qu.:  834.25  
##  Mode  :character   Median : 1794.33  
##                     Mean   : 2181.29  
##                     3rd Qu.: 3101.30  
##                     Max.   :13086.97  
##                     NA's   :5681

Temos algumas considerações: 1. Visibilidade do item como zero. Não faz sentido, pois o produto está a venda e não pode ter um valor como zero. 2. A idade da loja estão em um formato não muito útil para análise, podemos criar uma variável que nos diga os anos e não quando foi inalgurada.

rapply(dados,function(x)length(unique(x)))
##           Item_Identifier               Item_Weight 
##                      1559                       416 
##          Item_Fat_Content           Item_Visibility 
##                         5                     13006 
##                 Item_Type                  Item_MRP 
##                        16                      8052 
##         Outlet_Identifier Outlet_Establishment_Year 
##                        10                         9 
##               Outlet_Size      Outlet_Location_Type 
##                         4                         3 
##               Outlet_Type         Item_Outlet_Sales 
##                         4                      3494

Para aas variáveis que apresentam como character vamos olhar com mais atenção. Vamos ver a frequencia de cada caracter nas colunas Item_Fat_Content, Item_Type, Outlet_Size, Outlet_Location_Type e Outlet_Type.

dados %>%
  group_by(Item_Fat_Content) %>%
  summarise(n = length(Item_Fat_Content))
## # A tibble: 5 x 2
##   Item_Fat_Content     n
##              <chr> <int>
## 1               LF   522
## 2          low fat   178
## 3          Low Fat  8485
## 4              reg   195
## 5          Regular  4824
dados %>%
  group_by(Item_Type) %>%
  summarise(n = length(Item_Type))
## # A tibble: 16 x 2
##                Item_Type     n
##                    <chr> <int>
##  1          Baking Goods  1086
##  2                Breads   416
##  3             Breakfast   186
##  4                Canned  1084
##  5                 Dairy  1136
##  6          Frozen Foods  1426
##  7 Fruits and Vegetables  2013
##  8           Hard Drinks   362
##  9    Health and Hygiene   858
## 10             Household  1548
## 11                  Meat   736
## 12                Others   280
## 13               Seafood    89
## 14           Snack Foods  1989
## 15           Soft Drinks   726
## 16         Starchy Foods   269
dados %>%
  group_by(Outlet_Size) %>%
  summarise(n = length(Outlet_Size))
## # A tibble: 4 x 2
##   Outlet_Size     n
##         <chr> <int>
## 1        High  1553
## 2      Medium  4655
## 3       Small  3980
## 4        <NA>  4016
dados %>%
  group_by(Outlet_Location_Type) %>%
  summarise(n = length(Outlet_Location_Type))
## # A tibble: 3 x 2
##   Outlet_Location_Type     n
##                  <chr> <int>
## 1               Tier 1  3980
## 2               Tier 2  4641
## 3               Tier 3  5583
dados %>%
  group_by(Outlet_Type) %>%
  summarise(n = length(Outlet_Type))
## # A tibble: 4 x 2
##         Outlet_Type     n
##               <chr> <int>
## 1     Grocery Store  1805
## 2 Supermarket Type1  9294
## 3 Supermarket Type2  1546
## 4 Supermarket Type3  1559

3. Limpando os dados

No tratamento dos dados o que faremos é inserir dados que estão em falta ou excluir. Assim como remover outliers ou inconsistências.
Como temos cerca de 4000 dados faltantes na variável Outlet_Size e uma variável muito semelhante, a Outlet_Type, decidi não usar a varíavel Outlet_size. Não sei se é uma boa idéia, porém como são muitos dados faltantes em relação ao número total de observações, veremos como fica o modelo sem essa variável.

-Substituindo Item_Weight

Como um passo para podermos usar em nosso modelo a variável (apesar de não pertencer a nossa hipótese, pode ser útil) faremos a substituição dos valores NA pela média, assim a influência nos parâmetros da distribuição são melhores preservados.

dados$Item_Weight[is.na(dados$Item_Weight)] <- mean(dados$Item_Weight, na.rm=TRUE)

Modificando os dados

Navegue pelas abas para ver todo conteúdo.

i. Item_Visibility

Como vimos a variável possui valor mínimo de 0, o que não faz sentido, uma vez que o item está a venda. Assim faremos a substituição do valor nulo pela média do conjunto.

dados$Item_Visibility[dados$Item_Visibility == 0] <- mean(dados$Item_Visibility)

summary(dados$Item_Visibility)
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
## 0.003575 0.033143 0.062347 0.070034 0.094037 0.328391

Agora não temos valores zeros para essa variável

dados %>%
  group_by(Item_Visibility == 0) %>%
  summarise(n = length(Item_Visibility))
## # A tibble: 1 x 2
##   `Item_Visibility == 0`     n
##                    <lgl> <int>
## 1                  FALSE 14204

Agora vamos criar uma nova variável que explique a nossa hipótese de que um item com maior exposição tem maior chance de vender mais. Faremo a relacão Item_Visibility/mean(Item_visibility). Então, seria a visibilidade do item em relação a média. Acredito que nesse passo poderíamos apenas fazer um pre-processamento de padronização.

dados <- mutate(dados, Item_Visibility_MeanRatio = Item_Visibility/mean(Item_Visibility))

summary(dados$Item_Visibility_MeanRatio)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
## 0.05104 0.47325 0.89024 1.00000 1.34273 4.68901

Pronto! nossa nova variável está criada!

Nota: Essa técnica é amplamente usada para análise e pode melhorar seu modelo consideravelmente. Não é a toa que os KPIs são muito utilizados e exigidos dos profissionais hoje. São, inclusive melhores para visualização e tomadas de decisão.

ii. Nova categoria de item

Olhando atentamente a variável Item_Identifier vemos que o nome de cada item tem iniciais FD, NC ou DR. Analisando com a variavel Item_type, podemos chegar a conclusão que refere-se a Comida (FD=Food), Não-consumível (NC=Not-consumable) e Bebibas (DR=Drinks). Assim poderíamos simplesmente usar as iniciais para gerar uma nova coluna de tipo de item.
Nota: Poderíamos fazer a transformação manualmente em relação ao Item_Type para a mesma finalidade.

dados <- mutate(dados, Item_Identifier_Combined = substr(dados$Item_Identifier,1,2))

dados %>%
  group_by(Item_Identifier_Combined) %>%
  summarise(n = length(Item_Identifier_Combined))
## # A tibble: 3 x 2
##   Item_Identifier_Combined     n
##                      <chr> <int>
## 1                       DR  1317
## 2                       FD 10201
## 3                       NC  2686

iii. Idade do estabelecimento

Agora, iremos, de forma simples, calcular a idade do estabelecimento através da variável Outlet_Establishment_Year.

dados <- mutate(dados, Outlet_Years = 2013 - Outlet_Establishment_Year)

summary(dados$Outlet_Years)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    4.00    9.00   14.00   15.17   26.00   28.00

iv. Organizando Item_Fat_Content**

Na primeira tabela, notamos que Low fat esta escrito de várias formas: LF low fat e Low Fat, assim como Regular e reg. Vamos somar para apenas Low fat e Regular.

dados$Item_Fat_Content <- gsub("LF", "Low Fat", dados$Item_Fat_Content)
dados$Item_Fat_Content <- gsub("low fat", "Low Fat", dados$Item_Fat_Content)
dados$Item_Fat_Content <- gsub("reg", "Regular", dados$Item_Fat_Content)

dados %>%
  group_by(Item_Fat_Content) %>%
  summarise(n = length(Item_Fat_Content))
## # A tibble: 2 x 2
##   Item_Fat_Content     n
##              <chr> <int>
## 1          Low Fat  9185
## 2          Regular  5019

Além disso como vimos, alguns itens não são comestíveis, assim precisamos criar uma outra categoria sem relação com ambas.

dados$Item_Fat_Content[dados$Item_Identifier_Combined=="NC"] <- "Non-consumable"

dados %>%
  group_by(Item_Fat_Content) %>%
  summarise(n = length(Item_Fat_Content))
## # A tibble: 3 x 2
##   Item_Fat_Content     n
##              <chr> <int>
## 1          Low Fat  6499
## 2   Non-consumable  2686
## 3          Regular  5019

v. Organizando em fatores

O R permite o uso dos levels e factors nos algoritmos. De modo que podemos ou usar desse recurso ou criar novas variáveis "dummy" (representadas apenas por 0 e 1). Inicialmente faremos apenas o uso de factors.

dados$Item_Fat_Content <- as.factor(dados$Item_Fat_Content)
dados$Item_Type <- as.factor(dados$Item_Type)
dados$Outlet_Identifier <- as.factor(dados$Outlet_Identifier)
dados$Outlet_Size <- as.factor(dados$Outlet_Size)
dados$Outlet_Location_Type <- as.factor(dados$Outlet_Location_Type)
dados$Outlet_Type <- as.factor(dados$Outlet_Type)
dados$Item_Identifier_Combined <- as.factor(dados$Item_Identifier_Combined)

O R permite a manipulação dos dados de uma for incrivelmente simples.

vi. Criando Dummy Variables

De maneira fácil, transfomamos os fatores, ou levels contidos nas variáveis em dummy variables. O que significa transformar em zeros e uns. Assim, criando novas coluna com 1 para contem aquele fator ou descrição e zero caso contrário.

library(dummies)
## dummies-1.5.6 provided by Decision Patterns
dados_asdummy <- c()
dados_asdummy <- dados_asdummy %>%
  bind_cols(
  data.frame(dummy(dados$Item_Fat_Content)),
  data.frame(dummy(dados$Outlet_Identifier)),
  data.frame(dummy(dados$Outlet_Location_Type)),
  data.frame(dummy(dados$Item_Identifier_Combined))
  )

vii. Selecionando colunas

Agora, selecionamos as colunas numéricas que usaremos e unimos com as variáveis criadas como dummies:

dados <- select(dados, c(1,2,5,9,10,12))
dados <- bind_cols(dados, dados_asdummy)

Apartir de agora iremos criar os nossos modelos de predição. Separando e exportando os dados com novo nome de arquivo.

4.Exportando os dados

Agora dividiremos os dados em train e test novamente e exportaremos com outro nome para manter os arquivos originais.

train_modified <- dplyr::filter(dados, !is.na(Item_Outlet_Sales))
test_modified <- dplyr::filter(dados, is.na(Item_Outlet_Sales))

Salvando em arquivos:

write.csv(train_modified, "data/train_modified.csv")
write.csv(test_modified, "data/test_modified.csv")

5. Considerações finais

Há várias maneiras de se fazer e decidir quais variáveis poderão ser usadas, a maneira apresentada é bem popular.
Sobre dummy_variables
Normalmente omitimos uma variável criada pelos fatores, pois fica redundante um fator a ser 1 e b ser 0 e outra variável que diga a ser 0 e b com valor 1.
Por questões técnicas (leia-se tempo de processamento), separei em duas partes, que será atualizado quando terminado os nossos modelos de predição. A idéia é criar de 3 a 5 modelos diferentes, não sei ao certo e compara-los.

Escrito por {Vinicius L. 2017}.