Para este trabajo se escogió un database de Ventas de Chocolate, con venta en dólares. Descargado en Kaggle. https://www.kaggle.com/datasets/atharvasoundankar/chocolate-sales
df <- read.csv("Chocolate_Sales.csv")
head(df, 3)
## Sales.Person Country Product Date Amount Boxes.Shipped
## 1 Jehu Rudeforth UK Mint Chip Choco 04-Jan-22 $5,320 180
## 2 Van Tuxwell India 85% Dark Bars 01-Aug-22 $7,896 94
## 3 Gigi Bohling India Peanut Butter Cubes 07-Jul-22 $4,501 91
tail(df, 3)
## Sales.Person Country Product Date Amount
## 1092 Ches Bonnell Canada Organic Choco Syrup 26-Jul-22 $574
## 1093 Dotty Strutley India Eclairs 28-Jul-22 $2,086
## 1094 Karlen McCaffrey India 70% Dark Bites 23-May-22 $5,075
## Boxes.Shipped
## 1092 217
## 1093 384
## 1094 344
Genera un resumen estadístico de cada variable. Para variables numéricas, muestra la media, mediana, mínimo, máximo y cuartiles.
Para variables categóricas, muestra la frecuencia de cada categoría.
summary(df)
## Sales.Person Country Product Date
## Length:1094 Length:1094 Length:1094 Length:1094
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
## Amount Boxes.Shipped
## Length:1094 Min. : 1.0
## Class :character 1st Qu.: 70.0
## Mode :character Median :135.0
## Mean :161.8
## 3rd Qu.:228.8
## Max. :709.0
Para ver la estructura de mis datos, puedo hacerlo con str.
Acá debemos de tener en cuenta los tipos de datos que tiene cada columna ya que algunos se tendrán que transformar para su uso estadístico.
str(df)
## 'data.frame': 1094 obs. of 6 variables:
## $ Sales.Person : chr "Jehu Rudeforth" "Van Tuxwell" "Gigi Bohling" "Jan Morforth" ...
## $ Country : chr "UK" "India" "India" "Australia" ...
## $ Product : chr "Mint Chip Choco" "85% Dark Bars" "Peanut Butter Cubes" "Peanut Butter Cubes" ...
## $ Date : chr "04-Jan-22" "01-Aug-22" "07-Jul-22" "27-Apr-22" ...
## $ Amount : chr "$5,320 " "$7,896 " "$4,501 " "$12,726 " ...
## $ Boxes.Shipped: int 180 94 91 342 184 38 176 73 59 102 ...
En la estructura anterior, se puede apreciar que en _*Amount se muestra como char, entonces a continuación procederemos a eliminar el signo de dólar($), la coma(,) y convertirlo en número, para poder trabajar con los datos.
Para lograrlo se investigó la función gsub, que significa “Global Substitution”, y se usa para reemplazar patrones dentro de un string.
Formula: gsub(pattern, replacement, x) donde x = a la columna del df. En este caso df$Amount.
El patrón es una expresión regular, “[\$, ]”, y esto se reemplaza por -> “” que no tiene nada.
Finalmente, se convierte en numero con la función as.numeric()
df$Amount <- as.numeric(gsub("[\\$, ]", "", df$Amount))
summary(df)
## Sales.Person Country Product Date
## Length:1094 Length:1094 Length:1094 Length:1094
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
## Amount Boxes.Shipped
## Min. : 7 Min. : 1.0
## 1st Qu.: 2390 1st Qu.: 70.0
## Median : 4868 Median :135.0
## Mean : 5652 Mean :161.8
## 3rd Qu.: 8027 3rd Qu.:228.8
## Max. :22050 Max. :709.0
Para poder usar Sales.Person de mejor manera, lo hemos
convertido a factor, y podemos ver determinar a simple vista un top de
mejores vendedores, aunque los datos no están ordenados de mayor a
menor.
df$Sales.Person <- as.factor(df$Sales.Person)
sort(table(df$Sales.Person), decreasing = TRUE)
##
## Kelci Walkden Brien Boise Van Tuxwell Beverie Moffet
## 54 53 51 50
## Dennison Crosswaite Oby Sorrel Ches Bonnell Gigi Bohling
## 49 49 48 47
## Karlen McCaffrey Curtice Advani Kaine Padly Madelene Upcott
## 47 46 45 45
## Marney O'Breen Barr Faughny Gunar Cockshoot Jehu Rudeforth
## 45 43 43 43
## Roddy Speechley Mallorie Waber Andria Kimpton Jan Morforth
## 43 41 39 39
## Husein Augar Dotty Strutley Rafaelita Blaksland Wilone O'Kielt
## 38 36 34 34
## Camilla Castle
## 32
Acá convertimos Country a factor. Mas adelante veremos
un gráfico para observar visualmente el país con mayor venta
country_factor <- as.factor(df$Country)
sort(table(country_factor), decreasing = TRUE)
## country_factor
## Australia India USA UK Canada New Zealand
## 205 184 179 178 175 173
Vamos a explorar el promedio, la media.
Acá estamos usando num_cols para almacenar todos los rows de las columnas que solo son numéricas con sapply.
num_cols <- df[, sapply(df, is.numeric)]
head(num_cols, 5)
## Amount Boxes.Shipped
## 1 5320 180
## 2 7896 94
## 3 4501 91
## 4 12726 342
## 5 13685 184
Acá vamos aplicar el min para num_cols
min_values <- apply(num_cols, 2, min, na.rm = TRUE)
min_values
## Amount Boxes.Shipped
## 7 1
Lo mismo para el max en num_cols
max_values <- apply(num_cols, 2, max, na.rm = TRUE)
max_values
## Amount Boxes.Shipped
## 22050 709
Aplicaremos la misma fórmula para revisar los promedios
mean_values <- apply(num_cols, 2, mean, na.rm = TRUE)
mean_values
## Amount Boxes.Shipped
## 5652.308 161.798
Con la mediana verificaremos que tantos valores atípicos tiene el promedio de datos
median_values <- apply(num_cols, 2, median, na.rm = TRUE)
median_values
## Amount Boxes.Shipped
## 4868.5 135.0
Revisaremos si tenemos una DE alta o baja con respecto al promedio
sd_values <- apply(num_cols, 2, sd, na.rm = TRUE)
sd_values
## Amount Boxes.Shipped
## 4102.4420 121.5441
cor(df$Amount, df$Boxes.Shipped)
## [1] -0.01882685
No existe una correlación entre Amount y
Boxes.Shipped. Las ventas no dependen de la cantidad de
cajas enviadas.
min_values y el
max_values}}` para Amount es muy
significativa, lo que indica que hay amplia variabilidad en los montos
de las ventas.median_values < mean_values lo que
quiere decir que hay valores muy altos que llevan la media hacia
arriba.sd_values es bastante alta, lo que confirma la alta
variabilidad en los montos de las ventas.min_values y el
max_values para Box Shipped es muy
significativa, lo que indica que hay amplia variabilidad en la cantidad
de productos enviados.median_values < mean_values pero no
es una diferencia tan pronunciada con en Amount.sd_values es considerablemente alta, lo que confirma
la variabilidad en la cantidad de cajas enviadas.Los gráficos que vamos a utilizar en este caso serán:
librerías a utilizar
library(ggplot2)
## Warning: package 'ggplot2' was built under R version 4.4.3
library(dplyr)
## Warning: package 'dplyr' was built under R version 4.4.3
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(lubridate)
## Warning: package 'lubridate' was built under R version 4.4.3
##
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
##
## date, intersect, setdiff, union
Acá mostraremos la distribución de los puntos de datos.
ggplot(df, aes(x=Boxes.Shipped, y = Amount, color=Country))+
geom_point(size=1, alpha = 0.6) +
geom_smooth(method = "lm", se = FALSE, color = "#ff5959") +
labs(title = "Relacion entre Amount y Boxes.Shipped",
x = "Boxes Shipped",
y = "Amount (USD)") +
scale_y_continuous(labels = scales::label_dollar()) +
theme_minimal() #+
## `geom_smooth()` using formula = 'y ~ x'
Como la DE es larga, habían sospechas de outliers, acá podemos identificar esos valores atípicos visualmente.
ggplot(df, aes(x = Amount)) +
geom_boxplot(fill="lightblue") +
scale_x_continuous(labels = scales::label_dollar()) +
labs(title = "Boxplot per Amount (USD)",
x = "Amount (USD)",
y = "Frequency")
ggplot(df, aes(x = Boxes.Shipped)) +
geom_boxplot(fill="lightblue") +
labs(title = "Boxplot Boxes shipped",
x = "Boxes Shipped",
y = "Frequency")
Acá veremos los distintos usos del Grafico de Barras, funciona muy bien para rankings
Para el *new_topN usamos “%>%” que es el pipe operator** del paquete dplyr que nos ayuda a concatenar varias operaciones. Entonces pasamos df, a la siguiente función de la cadena.
Arrange() se utiliza para reordenar las filas de un data frame basándose en los valores de una o más columnas. Mientras que desc() nos ordena de mayor a menor.
Para un Top N usaremos esta función la cual podremos utilizar a través de nuestro EDA y podremos definir si es un ranking más o menos ventas con head o tail.
new_topN <- function(topN, rank = "head") {
df %>%
group_by(Product) %>% # Group by Product
summarize(Amount = sum(Amount)) %>% # Sum sales for each product
arrange(desc(Amount)) %>% # Sort in descending order
{ if (rank == "head") head(., topN) else tail(., topN) } # Apply head or tail dynamically
}
A continuación se presenta un gráfico de barras, con el fin de mostrar el top 20 de los productos más vendidos.
Aquí usamos la función *scale_x_continuous** para darle formato a de dólares al eje X
ggplot(new_topN(10, "head"), aes(x = Amount, y = reorder(Product, Amount), fill = Product)) +
geom_bar(stat = "identity") +
geom_text(aes(label = scales::label_dollar()(Amount)),
hjust = 1.2, color = "white", size = 4, fontface = "bold") +
scale_x_continuous(labels = scales::label_dollar()) +
labs(title = "Top 10 de Mayores Ingresos Generados por Producto",
x = "Amount (USD)", y = "Product") +
theme_minimal()
Acá se presenta un gráfico de barras, mostrando el top 5 de los productos menos vendidos.
ggplot(new_topN(5, "tail"), aes(x = Amount, y = reorder(Product, Amount), fill = Product)) +
geom_bar(stat = "identity") +
geom_text(aes(label = scales::label_dollar()(Amount)), # Add formatted labels
hjust = 1.2, color = "white", size = 4, fontface = "bold") + # Adjust label position & color
scale_x_continuous(labels = scales::label_dollar()) +
labs(title = "Top 5 de Menores Ingresos Generados por Producto", x = "Amount (USD)", y = "Product") +
theme_minimal()
sales_rate <- df %>%
group_by(Sales.Person) %>%
summarize(Total_Amount = sum(Amount)) %>%
arrange(desc(Total_Amount)) %>% # Sort from highest to lowest
slice(1:10)
ggplot(sales_rate, aes(x = reorder(Sales.Person, Total_Amount), y = Total_Amount)) +
geom_bar(stat = "identity", fill = "#4682B4") +
geom_text(aes(label = scales::label_dollar()(Total_Amount)), # Add formatted labels
hjust = 0.5, vjust = 2, color = "white", size = 3, fontface = "bold") +
scale_y_continuous(labels = scales::label_dollar()) +
labs(title = "Top 10 de los mejores Vendedores", x = "Sales Person", y = "Amount (USD)") +
scale_fill_viridis_d(option = "C") + # "C" is a blueish Viridis scale
theme_minimal() +
theme(axis.text.x = element_text(angle=45, hjust = 1))
country_sales <- df %>%
group_by(Country) %>%
summarize(Total_Amount = sum(Amount)) %>%
arrange(desc(Total_Amount)) # Sort from highest to lowest
ggplot(country_sales, aes(x = Total_Amount, y = reorder(Country, Total_Amount))) +
geom_bar(stat = "identity", fill = "#4682B4") +
geom_text(aes(label = scales::label_dollar()(Total_Amount)), # Add formatted labels
hjust = 1.2, color = "white", size = 4, fontface = "bold") +
scale_x_continuous(labels = scales::label_dollar()) +
labs(title = "Pais con mayor venta de Chocolates", x = "Country", y = "Amount (USD)") +
scale_fill_viridis_d(option = "C") + # "C" is a blueish Viridis scale
theme_minimal()
Vamos a darle formato a Date para poder trabajar con él
y poder visualizar el comportamiento de los Datos.
library(lubridate)
df$Date <- as.Date(df$Date, format = "%d-%b-%y")
df$Month <- format(df$Date, "%Y-%m")
df$month <- floor_date(df$date, "month")
monthly_sales <- aggregate(Amount ~ Month, data = df, sum)
monthly_sales <- monthly_sales[order(monthly_sales$Amount, decreasing = TRUE), ]
ggplot(monthly_sales, aes(x = Month, y = Amount, group = 1)) +
geom_line(color = "#4682B4", size = 1) +
geom_point(color = "#3c6e99", size = 3) +
geom_text(aes(label = scales::label_dollar()(Amount)), # Add formatted labels
hjust = 0.5, vjust = -0.4, color = "black", size = 4) +
scale_y_continuous(labels = scales::label_dollar()) +
labs(title = "Comportamiento de Ventas Mensuales",
x = "Mes",
y = "Ventas Totales") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.