Objetivo: Ayudar a Walmart a predecir sus ventas

Librerias

library(rsconnect)
library(janitor)
library(dplyr)
library(wordspace)
library(tidyverse)

Insertar Bases de Datos

train<-read.csv("C:\\Users\\javaw\\OneDrive - Instituto Tecnologico y de Estudios Superiores de Monterrey\\7mo Semestre\\Modulo 3\\train.csv")

test<-read.csv("C:\\Users\\javaw\\OneDrive - Instituto Tecnologico y de Estudios Superiores de Monterrey\\7mo Semestre\\Modulo 3\\test.csv")

stores<-read.csv("C:\\Users\\javaw\\OneDrive - Instituto Tecnologico y de Estudios Superiores de Monterrey\\7mo Semestre\\Modulo 3\\stores.csv")

features<-read.csv("C:\\Users\\javaw\\OneDrive - Instituto Tecnologico y de Estudios Superiores de Monterrey\\7mo Semestre\\Modulo 3\\features.csv")

Entender bases de datos

Train

train<-clean_names(train)
summary(train)
##     i_store          dept           date            weekly_sales   
##  Min.   : 1.0   Min.   : 1.00   Length:421570      Min.   : -4989  
##  1st Qu.:11.0   1st Qu.:18.00   Class :character   1st Qu.:  2080  
##  Median :22.0   Median :37.00   Mode  :character   Median :  7612  
##  Mean   :22.2   Mean   :44.26                      Mean   : 15981  
##  3rd Qu.:33.0   3rd Qu.:74.00                      3rd Qu.: 20206  
##  Max.   :45.0   Max.   :99.00                      Max.   :693099  
##  is_holiday     
##  Mode :logical  
##  FALSE:391909   
##  TRUE :29661    
##                 
##                 
## 

Observaciones
1. Hay 45 tiendas.
2. La columna de dept va desde 1 hasta 99.
3. La fecha está como character en lugar de estar como formato de fecha. Las fechas van de 2010-02-05 a 2012-11-01.
4. Las ventas semanales tienen valores negativos.

Stores

stores<-clean_names(stores)
summary(stores)
##     i_store       type                size       
##  Min.   : 1   Length:45          Min.   : 34875  
##  1st Qu.:12   Class :character   1st Qu.: 70713  
##  Median :23   Mode  :character   Median :126512  
##  Mean   :23                      Mean   :130288  
##  3rd Qu.:34                      3rd Qu.:202307  
##  Max.   :45                      Max.   :219622
count(stores, type, sort = TRUE)
##   type  n
## 1    A 22
## 2    B 17
## 3    C  6

Observaciones
1. Hay 45 tiendas.
2. La columna de type describe el tipo de tienda, hay 3 tipos, A, B, C.
3. La columna de size describe el tamaño de la tienda.

Test

test<-clean_names(test)
summary(test)
##     i_store           dept           date           is_holiday     
##  Min.   : 1.00   Min.   : 1.00   Length:115064      Mode :logical  
##  1st Qu.:11.00   1st Qu.:18.00   Class :character   FALSE:106136   
##  Median :22.00   Median :37.00   Mode  :character   TRUE :8928     
##  Mean   :22.24   Mean   :44.34                                     
##  3rd Qu.:33.00   3rd Qu.:74.00                                     
##  Max.   :45.00   Max.   :99.00

Observaciones
1. Hay 45 tiendas.
2. La columna de dept va desde 1 hasta 99.
3. La fecha está como character en lugar de estar como formato de fecha. Y son distintas a las fechas que hay en train.
4. Los días festivos también difieren a los que hay en train.

Features

features<-clean_names(features)
summary(features)
##     i_store       date            temperature       fuel_price   
##  Min.   : 1   Length:8190        Min.   : -7.29   Min.   :2.472  
##  1st Qu.:12   Class :character   1st Qu.: 45.90   1st Qu.:3.041  
##  Median :23   Mode  :character   Median : 60.71   Median :3.513  
##  Mean   :23                      Mean   : 59.36   Mean   :3.406  
##  3rd Qu.:34                      3rd Qu.: 73.88   3rd Qu.:3.743  
##  Max.   :45                      Max.   :101.95   Max.   :4.468  
##                                                                  
##    mark_down1       mark_down2          mark_down3          mark_down4      
##  Min.   : -2781   Min.   :  -265.76   Min.   :  -179.26   Min.   :    0.22  
##  1st Qu.:  1578   1st Qu.:    68.88   1st Qu.:     6.60   1st Qu.:  304.69  
##  Median :  4744   Median :   364.57   Median :    36.26   Median : 1176.42  
##  Mean   :  7032   Mean   :  3384.18   Mean   :  1760.10   Mean   : 3292.94  
##  3rd Qu.:  8923   3rd Qu.:  2153.35   3rd Qu.:   163.15   3rd Qu.: 3310.01  
##  Max.   :103185   Max.   :104519.54   Max.   :149483.31   Max.   :67474.85  
##  NA's   :4158     NA's   :5269        NA's   :4577        NA's   :4726      
##    mark_down5            cpi         unemployment    is_holiday     
##  Min.   :  -185.2   Min.   :126.1   Min.   : 3.684   Mode :logical  
##  1st Qu.:  1440.8   1st Qu.:132.4   1st Qu.: 6.634   FALSE:7605     
##  Median :  2727.1   Median :182.8   Median : 7.806   TRUE :585      
##  Mean   :  4132.2   Mean   :172.5   Mean   : 7.827                  
##  3rd Qu.:  4832.6   3rd Qu.:213.9   3rd Qu.: 8.567                  
##  Max.   :771448.1   Max.   :229.0   Max.   :14.313                  
##  NA's   :4140       NA's   :585     NA's   :585

Observaciones
1. Hay 45 tiendas.
2. Hay información de markdowns que se hicieron y solo estan disponibles a partir de nov 2011. La fecha está en caracter también.
3. Está presente el indice de precios al consumidor.
4. También está presente el desempleo.
5. De igual manera, esta marcado si fue en festividad o no.
6. Hay NA’s en más de la mitad de lso registros de MarkDown (1 al 5).
7. Hay 585 Na’s en CPI y desempleo, hay 585 registros de festividades, sin embargo no hay relación entre estas al revisar la base de datos.

Herramienta “El Generador de Valor de Datos”

Paso 1. Definir el área de negocio a impactar y su KPI

El departamento de mercadotecnia de EUA (con muestra de 45 tiendas) en el indicador de Ventas Semanales.

Paso 2. Seleccionar plantilla(s) para crear valor a partir de los datos de los clientes.
Visión Segmentación Personalización Contextualización
Paso 3. Generar ideas o conceptos específicos.

Elaborar un modelo predictivo de ventas semanales.

Paso 4.Reunir los datos requeridos.

Elaborar una base de datos con la variable dependiente(ventas semanales) y las variables independientes a definir.

Paso 5. Plan de ejecución.

Mecadotecnia elaborará plan para desplegar el modelo predictivo en fases.
Fase 1. Piloto en San Antonio, TX.
Fase 2. Texas
Fase 3. EUA

Sistemas asegurará la captura del markdown en las bases de datos.

Unión y limpieza de bases de datos

Podemos optar por unir las bases de datos de train, stores, y features, ya que todas juntas contienen información relevante que debe de ser analizada para predecir las ventas en la base de datos de test. Primero podemos unir train y stores.

df<-merge(train,stores, by="i_store")

Posteriormente hay que unir features pero hay que dejar solo las variables que nos interesan: temperatura, precio de combustible, CPI y desempleo.

features2<-select(features,-(mark_down1:mark_down5))
#De igual manera puede realizarse con subset
featuresprueba<-subset(features,select=-c(mark_down1:mark_down5))
df2<-merge(df,features2)

Encontrar y si es necesario remplazar NA’s

colSums(is.na(df2))
##      i_store         date   is_holiday         dept weekly_sales         type 
##            0            0            0            0            0            0 
##         size  temperature   fuel_price          cpi unemployment 
##            0            0            0            0            0

No hay NA’s ya que se eliminaron las columnas de los markdowns, ademas que los NA,s de CPI y unemployment eran en fechas posteriores del 2012.

Cambiar formato de fecha

df2$date<-as.Date(df2$date, format = "%d/%m/%Y")
str(df2)
## 'data.frame':    421570 obs. of  11 variables:
##  $ i_store     : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ date        : Date, format: "2011-04-01" "2011-04-01" ...
##  $ is_holiday  : logi  FALSE FALSE FALSE FALSE FALSE FALSE ...
##  $ dept        : int  49 26 81 34 59 30 7 85 8 28 ...
##  $ weekly_sales: num  13168 5947 28545 9950 317 ...
##  $ type        : chr  "A" "A" "A" "A" ...
##  $ size        : int  151315 151315 151315 151315 151315 151315 151315 151315 151315 151315 ...
##  $ temperature : num  59.2 59.2 59.2 59.2 59.2 ...
##  $ fuel_price  : num  3.52 3.52 3.52 3.52 3.52 ...
##  $ cpi         : num  215 215 215 215 215 ...
##  $ unemployment: num  7.68 7.68 7.68 7.68 7.68 ...

Ventas con valores negativos

Al evaluar la base de datos, se puede observar que todos los valores negativos provienen del departamento 47. Esto podría significar que ese departamento es de devoluciones.

signcount(df2$weekly_sales)
##    pos   zero    neg 
## 420212     73   1285

Sin embargo, no se cuenta con elementos necesarios para corroborar esta información, y los registros son pocos por lo que se opta eliminar los registros con ventas negativas.

df3<-filter(df2, weekly_sales > 0)
summary(df3$weekly_sales)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##       0    2120    7662   16033   20271  693099

Agregar número de la semana

Si bien ya tenemos la fecha como tal, el numero de la semana puede ser un factor que afecte en las ventas.

df4<-df3
df4$week_number<-strftime(df4$date, format="%V") %>% as.integer(df4$Date)
str(df4)
## 'data.frame':    420212 obs. of  12 variables:
##  $ i_store     : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ date        : Date, format: "2011-04-01" "2011-04-01" ...
##  $ is_holiday  : logi  FALSE FALSE FALSE FALSE FALSE FALSE ...
##  $ dept        : int  49 26 81 34 59 30 7 85 8 28 ...
##  $ weekly_sales: num  13168 5947 28545 9950 317 ...
##  $ type        : chr  "A" "A" "A" "A" ...
##  $ size        : int  151315 151315 151315 151315 151315 151315 151315 151315 151315 151315 ...
##  $ temperature : num  59.2 59.2 59.2 59.2 59.2 ...
##  $ fuel_price  : num  3.52 3.52 3.52 3.52 3.52 ...
##  $ cpi         : num  215 215 215 215 215 ...
##  $ unemployment: num  7.68 7.68 7.68 7.68 7.68 ...
##  $ week_number : int  13 13 13 13 13 13 13 13 13 13 ...

Separar la fecha por año, mes y día

df5<-df4
df5<-df5 %>% 
  mutate(year=lubridate::year(date),
         month=lubridate::month(date),
         day=lubridate::day(date))
str(df5)
## 'data.frame':    420212 obs. of  15 variables:
##  $ i_store     : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ date        : Date, format: "2011-04-01" "2011-04-01" ...
##  $ is_holiday  : logi  FALSE FALSE FALSE FALSE FALSE FALSE ...
##  $ dept        : int  49 26 81 34 59 30 7 85 8 28 ...
##  $ weekly_sales: num  13168 5947 28545 9950 317 ...
##  $ type        : chr  "A" "A" "A" "A" ...
##  $ size        : int  151315 151315 151315 151315 151315 151315 151315 151315 151315 151315 ...
##  $ temperature : num  59.2 59.2 59.2 59.2 59.2 ...
##  $ fuel_price  : num  3.52 3.52 3.52 3.52 3.52 ...
##  $ cpi         : num  215 215 215 215 215 ...
##  $ unemployment: num  7.68 7.68 7.68 7.68 7.68 ...
##  $ week_number : int  13 13 13 13 13 13 13 13 13 13 ...
##  $ year        : num  2011 2011 2011 2011 2011 ...
##  $ month       : num  4 4 4 4 4 4 4 4 4 4 ...
##  $ day         : int  1 1 1 1 1 1 1 1 1 1 ...

Modelo Predictivo

Regresión lineal

colnames(df3)
##  [1] "i_store"      "date"         "is_holiday"   "dept"         "weekly_sales"
##  [6] "type"         "size"         "temperature"  "fuel_price"   "cpi"         
## [11] "unemployment"
regresion <- lm(weekly_sales~i_store+is_holiday+dept+type+size+temperature+fuel_price+cpi+unemployment+week_number+year+month+day, data=df5)
summary(regresion)
## 
## Call:
## lm(formula = weekly_sales ~ i_store + is_holiday + dept + type + 
##     size + temperature + fuel_price + cpi + unemployment + week_number + 
##     year + month + day, data = df5)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
## -34331 -12895  -5852   5626 671540 
## 
## Coefficients:
##                  Estimate Std. Error t value Pr(>|t|)    
## (Intercept)     1.110e+06  2.999e+05   3.701 0.000214 ***
## i_store        -1.426e+02  3.087e+00 -46.198  < 2e-16 ***
## is_holidayTRUE  8.511e+02  1.391e+02   6.119 9.45e-10 ***
## dept            1.108e+02  1.097e+00 101.013  < 2e-16 ***
## typeB          -3.133e+02  1.078e+02  -2.908 0.003642 ** 
## typeC           5.836e+03  1.840e+02  31.709  < 2e-16 ***
## size            9.920e-02  9.584e-04 103.511  < 2e-16 ***
## temperature     3.701e+00  2.133e+00   1.735 0.082688 .  
## fuel_price      4.791e+02  1.480e+02   3.237 0.001207 ** 
## cpi            -2.340e+01  9.996e-01 -23.409  < 2e-16 ***
## unemployment   -2.538e+02  2.062e+01 -12.308  < 2e-16 ***
## week_number     7.678e+02  4.566e+02   1.682 0.092648 .  
## year           -5.485e+02  1.485e+02  -3.695 0.000220 ***
## month          -3.167e+03  1.988e+03  -1.594 0.111036    
## day            -1.281e+02  6.539e+01  -1.959 0.050115 .  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 21690 on 420197 degrees of freedom
## Multiple R-squared:  0.08982,    Adjusted R-squared:  0.08979 
## F-statistic:  2962 on 14 and 420197 DF,  p-value: < 2.2e-16

Predicción

datos_nuevos <- data.frame(i_store=1, is_holiday= TRUE, dept=1, type="A", size= 151315, week_number =1, temperature = 59.17, fuel_price = 3.524, cpi= 214.8372, unemployment = 7.682, year =2012, month = 1, day=1)
predict(regresion,datos_nuevos)
##        1 
## 14667.94

Gráfica

Para realizar el gráfico dinámico se consideraron las opciones de graficar las ventas promedio de acuerdo al mes, día, o número de semana del año 2012.

df6<-filter(df5,year==2012)
df7<-group_by(df6,week_number) %>% summarise(total_wsales = mean(weekly_sales))
df8<-group_by(df6,month) %>% summarise(total_wsales = mean(weekly_sales))
df9<-group_by(df6,day) %>% summarise(total_wsales = mean(weekly_sales))

Histograma

Adicional, se optó por realiza run histograma dinámico donde se puede ajustar los breaks y la densidad.

Gráfico General

También se optó por realizar un gráfico general.

#selectInput("variables",label="x",choices = names(df6),selected = "month")
#renderPlot(plot(df6$weekly_sales,df6[,input$variables])
  
#)

Conclusión

El modelo de regresión tiene un valor de r cuadrada ajustada de 0.08970, lo cual es bajo, por lo que no podemos afirmar que sea un modelo predictivo acertado. Esto se debe a la temporalidad, por ejemplo, hay picos en ventas cada vez que es Thanksgiving o cuando es el Super Bowl. Para predecir las ventas en un modelo que tiene este tipo de temporalidad hay que recurrir a otros modelos.

Adicionalmente, al realizar los pasos finales de los gráficos dinámicos, las imagenes previamente insertadas dejaron de visualizarse. Se optó por eliminar las imagenes cuando se corre el codigo en shiny.

Propuestas

Considero, que el modelo de predicción no posee la confiabilidad necesaria para poder implementar acciones a partir de este. Mi propuesta radica en la creación de un nuevo modelo predictivo que considere la temporalidad de las festividades para poder predecir con mayor certeza las ventas. Hasta el momento, considero que cualquier predicción tiene una alta probabilidad de ser incorrecta.