This statistical analysis examines the Chocolate Sales dataset to identify sales patterns, customer demand, and business performance across different countries, products, and salespersons. The dataset includes key variables such as sales amount, boxes shipped, product type, country, salesperson, and transaction date. The analysis begins with data cleaning and descriptive statistics to understand the characteristics of the dataset. It then applies various statistical techniques and visualisations, including histograms, boxplots, scatter plots, bar charts, correlation analysis, regression, and hypothesis testing. The findings provide valuable insights into sales trends, product performance, regional differences, and factors influencing revenue, supporting informed business decision-making.
The aim of this study is to perform a comprehensive statistical analysis of the Chocolate Sales dataset to identify sales patterns, evaluate product and salesperson performance, examine regional sales differences, and investigate the relationship between sales amount and boxes shipped using descriptive statistics, data visualisation, and inferential statistical techniques.
To clean and prepare the Chocolate Sales dataset for statistical analysis. To summarise the dataset using descriptive statistical measures such as mean, median, standard deviation, and frequency distributions. To analyse sales performance across different countries, products, and salespersons. To identify trends in sales and shipments over time using date-based analysis. To examine the relationship between sales amount and boxes shipped through correlation and regression analysis. To compare sales performance among countries and products using appropriate statistical tests such as ANOVA and t-tests. To present the findings through effective data visualisations and provide business insights for decision-making.
##Research Questions What are the key characteristics of the Chocolate Sales dataset based on descriptive statistics? Which countries, products, and salespersons generate the highest sales revenue? How do chocolate sales vary over different time periods? Is there a significant relationship between the number of boxes shipped and the sales amount? Are there significant differences in sales performance across countries and product categories? What business insights can be obtained from the statistical analysis and visualisation of the Chocolate Sales dataset?
#Load Libraries
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.2.1 ✔ readr 2.2.0
## ✔ forcats 1.0.1 ✔ stringr 1.6.0
## ✔ ggplot2 4.0.3 ✔ tibble 3.3.1
## ✔ lubridate 1.9.5 ✔ tidyr 1.3.2
## ✔ purrr 1.2.2
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(lubridate)
library(psych)
##
## Attaching package: 'psych'
##
## The following objects are masked from 'package:ggplot2':
##
## %+%, alpha
library(skimr)
library(corrplot)
## corrplot 0.95 loaded
library(GGally)
library(PerformanceAnalytics)
## Loading required package: xts
## Loading required package: zoo
##
## Attaching package: 'zoo'
##
## The following objects are masked from 'package:base':
##
## as.Date, as.Date.numeric
##
##
## ######################### Warning from 'xts' package ##########################
## # #
## # The dplyr lag() function breaks how base R's lag() function is supposed to #
## # work, which breaks lag(my_xts). Calls to lag(my_xts) that you type or #
## # source() into this session won't work correctly. #
## # #
## # Use stats::lag() to make sure you're not using dplyr::lag(), or you can add #
## # conflictRules('dplyr', exclude = 'lag') to your .Rprofile to stop #
## # dplyr from breaking base R's lag() function. #
## # #
## # Code in packages is not affected. It's protected by R's namespace mechanism #
## # Set `options(xts.warn_dplyr_breaks_lag = FALSE)` to suppress this warning. #
## # #
## ###############################################################################
##
## Attaching package: 'xts'
##
## The following objects are masked from 'package:dplyr':
##
## first, last
##
##
## Attaching package: 'PerformanceAnalytics'
##
## The following object is masked from 'package:graphics':
##
## legend
library(viridis)
## Loading required package: viridisLite
library(scales)
##
## Attaching package: 'scales'
##
## The following object is masked from 'package:viridis':
##
## viridis_pal
##
## The following objects are masked from 'package:psych':
##
## alpha, rescale
##
## The following object is masked from 'package:purrr':
##
## discard
##
## The following object is masked from 'package:readr':
##
## col_factor
library(plotly)
##
## Attaching package: 'plotly'
##
## The following object is masked from 'package:ggplot2':
##
## last_plot
##
## The following object is masked from 'package:stats':
##
## filter
##
## The following object is masked from 'package:graphics':
##
## layout
library(reshape2)
##
## Attaching package: 'reshape2'
##
## The following object is masked from 'package:tidyr':
##
## smiths
#Import Dataset
sales <- read.csv("C:/Users/User/Downloads/Chocolate Sales.csv", stringsAsFactors = FALSE)
#Data Cleaning
#Convert Amount to numeric
sales$Amount <- gsub("\\$", "", sales$Amount)
sales$Amount <- gsub(",", "", sales$Amount)
sales$Amount <- as.numeric(sales$Amount)
#Convert Date
sales$Date <- dmy(sales$Date)
#Convert categorical variables
sales$Country <- as.factor(sales$Country)
sales$Product <- as.factor(sales$Product)
sales$Sales.Person <- as.factor(sales$Sales.Person)
#If your column name contains a space
names(sales)
## [1] "Sales.Person" "Country" "Product" "Date"
## [5] "Amount" "Boxes.Shipped"
names(sales)[1] <- "SalesPerson"
#Then
sales$SalesPerson <- as.factor(sales$SalesPerson)
#Missing Values
colSums(is.na(sales))
## SalesPerson Country Product Date Amount
## 0 0 0 0 0
## Boxes.Shipped
## 0
any(is.na(sales))
## [1] FALSE
#Duplicate Records
sum(duplicated(sales))
## [1] 0
#Descriptive Statistic
summary(sales)
## SalesPerson Country Product
## Kelci Walkden : 54 Australia :205 50% Dark Bites : 60
## Brien Boise : 53 Canada :175 Eclairs : 60
## Van Tuxwell : 51 India :184 Smooth Sliky Salty : 59
## Beverie Moffet : 50 New Zealand:173 White Choc : 58
## Dennison Crosswaite: 49 UK :178 Drinking Coco : 56
## Oby Sorrel : 49 USA :179 Spicy Special Slims: 54
## (Other) :788 (Other) :747
## Date Amount Boxes.Shipped
## Min. :2022-01-03 Min. : 7 Min. : 1.0
## 1st Qu.:2022-03-02 1st Qu.: 2390 1st Qu.: 70.0
## Median :2022-05-11 Median : 4868 Median :135.0
## Mean :2022-05-03 Mean : 5652 Mean :161.8
## 3rd Qu.:2022-07-04 3rd Qu.: 8027 3rd Qu.:228.8
## Max. :2022-08-31 Max. :22050 Max. :709.0
##
describe(sales)
## Warning in FUN(newX[, i], ...): no non-missing arguments to min; returning Inf
## Warning in FUN(newX[, i], ...): no non-missing arguments to max; returning -Inf
## vars n mean sd median trimmed mad min max range
## SalesPerson* 1 1094 12.94 7.17 13.0 12.93 8.90 1 25 24
## Country* 2 1094 3.44 1.73 3.0 3.42 2.97 1 6 5
## Product* 3 1094 11.70 6.48 12.0 11.74 8.90 1 22 21
## Date 4 1094 NaN NA NA NaN NA Inf -Inf -Inf
## Amount 5 1094 5652.31 4102.44 4868.5 5221.58 4052.69 7 22050 22043
## Boxes.Shipped 6 1094 161.80 121.54 135.0 147.30 111.19 1 709 708
## skew kurtosis se
## SalesPerson* 0.00 -1.22 0.22
## Country* 0.03 -1.30 0.05
## Product* -0.04 -1.23 0.20
## Date NA NA NA
## Amount 0.89 0.44 124.03
## Boxes.Shipped 1.11 1.15 3.67
skim(sales)
| Name | sales |
| Number of rows | 1094 |
| Number of columns | 6 |
| _______________________ | |
| Column type frequency: | |
| Date | 1 |
| factor | 3 |
| numeric | 2 |
| ________________________ | |
| Group variables | None |
Variable type: Date
| skim_variable | n_missing | complete_rate | min | max | median | n_unique |
|---|---|---|---|---|---|---|
| Date | 0 | 1 | 2022-01-03 | 2022-08-31 | 2022-05-11 | 168 |
Variable type: factor
| skim_variable | n_missing | complete_rate | ordered | n_unique | top_counts |
|---|---|---|---|---|---|
| SalesPerson | 0 | 1 | FALSE | 25 | Kel: 54, Bri: 53, Van: 51, Bev: 50 |
| Country | 0 | 1 | FALSE | 6 | Aus: 205, Ind: 184, USA: 179, UK: 178 |
| Product | 0 | 1 | FALSE | 22 | 50%: 60, Ecl: 60, Smo: 59, Whi: 58 |
Variable type: numeric
| skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|
| Amount | 0 | 1 | 5652.31 | 4102.44 | 7 | 2390.5 | 4868.5 | 8027.25 | 22050 | ▇▆▂▁▁ |
| Boxes.Shipped | 0 | 1 | 161.80 | 121.54 | 1 | 70.0 | 135.0 | 228.75 | 709 | ▇▅▂▁▁ |
#Mean Median SD
mean(sales$Amount)
## [1] 5652.308
median(sales$Amount)
## [1] 4868.5
sd(sales$Amount)
## [1] 4102.442
var(sales$Amount)
## [1] 16830030
max(sales$Amount)
## [1] 22050
min(sales$Amount)
## [1] 7
range(sales$Amount)
## [1] 7 22050
#Frequency Tables
##Country
##Product
##Sales Person
#Histograms
##Amount
ggplot(sales,aes(Amount))+
geom_histogram(fill="steelblue",bins=30)+
theme_minimal()
##Boxes
ggplot(sales,aes(Boxes.Shipped))+
geom_histogram(fill="orange",bins=30)+
theme_minimal()
##Density Plot
ggplot(sales,aes(Amount))+
geom_density(fill="skyblue",alpha=.6)
##Boxplots
ggplot(sales,aes(y=Amount))+
geom_boxplot(fill="red")
###By Country
ggplot(sales,aes(Country,Amount,fill=Country))+
geom_boxplot()
###By Product
ggplot(sales,aes(Product,Amount,fill=Product))+
geom_boxplot()+
theme(axis.text.x=element_text(angle=45,hjust=1))
##Scatter Plot
ggplot(sales,aes(Boxes.Shipped,Amount))+
geom_point(color="blue")+
geom_smooth(method="lm")
## `geom_smooth()` using formula = 'y ~ x'
##Correlation
numeric_data <- sales %>%
select(Amount,Boxes.Shipped)
cor(numeric_data)
## Amount Boxes.Shipped
## Amount 1.00000000 -0.01882685
## Boxes.Shipped -0.01882685 1.00000000
corrplot(cor(numeric_data),
method="color",
addCoef.col="black")
##Correlation Matrix
ggpairs(numeric_data)
###Sales by Country
country_sales <- sales %>%
group_by(Country) %>%
summarise(TotalSales=sum(Amount))
country_sales
## # A tibble: 6 × 2
## Country TotalSales
## <fct> <dbl>
## 1 Australia 1137367
## 2 Canada 962899
## 3 India 1045800
## 4 New Zealand 950418
## 5 UK 1051792
## 6 USA 1035349
##Bar Chart
ggplot(country_sales,
aes(reorder(Country,TotalSales),
TotalSales,
fill=Country))+
geom_col()+
coord_flip()
###Sales by Product
product_sales <- sales %>%
group_by(Product) %>%
summarise(TotalSales=sum(Amount))
ggplot(product_sales,
aes(reorder(Product,TotalSales),
TotalSales,
fill=Product))+
geom_col()+
coord_flip()
###Sales by Sales Person
person_sales <- sales %>%
group_by(SalesPerson) %>%
summarise(TotalSales=sum(Amount))
person_sales <- sales %>%
group_by(SalesPerson) %>%
summarise(TotalSales=sum(Amount))
###Monthly Sales
sales$Month <- month(sales$Date,label=TRUE)
monthly_sales <- sales %>%
group_by(Month)%>%
summarise(TotalSales=sum(Amount))
ggplot(monthly_sales,
aes(Month,TotalSales,group=1))+
geom_line(size=1.2)+
geom_point(size=3)
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once per session.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
###Monthly Boxes
monthly_boxes <- sales %>%
group_by(Month)%>%
summarise(Boxes=sum(Boxes.Shipped))
monthly_boxes <- sales %>%
group_by(Month)%>%
summarise(Boxes=sum(Boxes.Shipped))
##Pie Chart
ggplot(country_sales,
aes("",TotalSales,fill=Country))+
geom_bar(stat="identity",width=1)+
coord_polar("y")
##Heatmap
heat <- sales %>%
group_by(Country,Product)%>%
summarise(Sales=sum(Amount))
## `summarise()` has regrouped the output.
## ℹ Summaries were computed grouped by Country and Product.
## ℹ Output is grouped by Country.
## ℹ Use `summarise(.groups = "drop_last")` to silence this message.
## ℹ Use `summarise(.by = c(Country, Product))` for per-operation grouping
## (`?dplyr::dplyr_by`) instead.
ggplot(heat,
aes(Product,
Country,
fill=Sales))+
geom_tile()
##IQR Method
Q1 <- quantile(sales$Amount,.25)
Q3 <- quantile(sales$Amount,.75)
IQR_value <- IQR(sales$Amount)
Lower <- Q1-1.5*IQR_value
Upper <- Q3+1.5*IQR_value
sales %>%
filter(Amount<Lower | Amount>Upper)
## SalesPerson Country Product Date Amount
## 1 Brien Boise Canada 99% Dark & Pure 2022-05-18 16793
## 2 Van Tuxwell Australia Organic Choco Syrup 2022-08-10 19453
## 3 Kelci Walkden USA Manuka Honey Choco 2022-02-16 17318
## 4 Van Tuxwell India Organic Choco Syrup 2022-05-16 19929
## 5 Marney O'Breen UK Smooth Sliky Salty 2022-05-13 18991
## 6 Ches Bonnell India Organic Choco Syrup 2022-03-08 16569
## 7 Dotty Strutley USA Caramel Stuffed Bars 2022-04-15 16982
## 8 Jan Morforth New Zealand Mint Chip Choco 2022-06-30 18340
## 9 Ches Bonnell India Peanut Butter Cubes 2022-01-27 22050
## 10 Curtice Advani India Smooth Sliky Salty 2022-04-19 19327
## 11 Jan Morforth Australia Mint Chip Choco 2022-02-22 17626
## 12 Rafaelita Blaksland New Zealand Eclairs 2022-02-07 19481
## 13 Brien Boise India 85% Dark Bars 2022-08-09 18032
## 14 Kelci Walkden Canada After Nines 2022-01-13 16702
## 15 Dennison Crosswaite USA Baker's Choco Chips 2022-08-11 17465
## 16 Kaine Padly UK After Nines 2022-01-21 18697
## Boxes.Shipped Month
## 1 416 May
## 2 14 Aug
## 3 87 Feb
## 4 174 May
## 5 88 May
## 6 99 Mar
## 7 76 Apr
## 8 285 Jun
## 9 208 Jan
## 10 135 Apr
## 11 103 Feb
## 12 51 Feb
## 13 205 Aug
## 14 198 Jan
## 15 271 Aug
## 16 176 Jan
##Regression
model <- lm(Amount~Boxes.Shipped,data=sales)
summary(model)
##
## Call:
## lm(formula = Amount ~ Boxes.Shipped, data = sales)
##
## Residuals:
## Min 1Q Median 3Q Max
## -5694.7 -3246.1 -769.4 2345.9 16427.1
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 5755.1237 206.6254 27.853 <2e-16 ***
## Boxes.Shipped -0.6355 1.0212 -0.622 0.534
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 4104 on 1092 degrees of freedom
## Multiple R-squared: 0.0003545, Adjusted R-squared: -0.000561
## F-statistic: 0.3872 on 1 and 1092 DF, p-value: 0.5339