The Superstore Dataset is a real-world sales dataset that contains information about customer orders from a retail company.
It includes details on products, sales, profits, discounts, shipping, and customer demographics across different regions and categories.
The purpose of analyzing this dataset is to gain insights into sales performance, profitability, and customer behavior, and to identify trends that can improve business decisions.
The Superstore Giant is seeking knowledge in understanding what works best for them. They would like to understand which products, regions, categories and customer segments they should target or avoid.
library(ggplot2) #For visualization
library(dplyr) #for data manipulation
##
## 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) #for date and time
##
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
##
## date, intersect, setdiff, union
library(tidyr) #for cleaning data
Importing the data using read.csv
store <- read.csv("C:\\Users\\USER-PC\\Desktop\\R PROJECT\\Sample - Superstore.csv")
View(store)
head(store)
## Row.ID Order.ID Order.Date Ship.Date Ship.Mode Customer.ID
## 1 1 CA-2016-152156 11/8/2016 11/11/2016 Second Class CG-12520
## 2 2 CA-2016-152156 11/8/2016 11/11/2016 Second Class CG-12520
## 3 3 CA-2016-138688 6/12/2016 6/16/2016 Second Class DV-13045
## 4 4 US-2015-108966 10/11/2015 10/18/2015 Standard Class SO-20335
## 5 5 US-2015-108966 10/11/2015 10/18/2015 Standard Class SO-20335
## 6 6 CA-2014-115812 6/9/2014 6/14/2014 Standard Class BH-11710
## Customer.Name Segment Country City State
## 1 Claire Gute Consumer United States Henderson Kentucky
## 2 Claire Gute Consumer United States Henderson Kentucky
## 3 Darrin Van Huff Corporate United States Los Angeles California
## 4 Sean O'Donnell Consumer United States Fort Lauderdale Florida
## 5 Sean O'Donnell Consumer United States Fort Lauderdale Florida
## 6 Brosina Hoffman Consumer United States Los Angeles California
## Postal.Code Region Product.ID Category Sub.Category
## 1 42420 South FUR-BO-10001798 Furniture Bookcases
## 2 42420 South FUR-CH-10000454 Furniture Chairs
## 3 90036 West OFF-LA-10000240 Office Supplies Labels
## 4 33311 South FUR-TA-10000577 Furniture Tables
## 5 33311 South OFF-ST-10000760 Office Supplies Storage
## 6 90032 West FUR-FU-10001487 Furniture Furnishings
## Product.Name Sales
## 1 Bush Somerset Collection Bookcase 261.9600
## 2 Hon Deluxe Fabric Upholstered Stacking Chairs, Rounded Back 731.9400
## 3 Self-Adhesive Address Labels for Typewriters by Universal 14.6200
## 4 Bretford CR4500 Series Slim Rectangular Table 957.5775
## 5 Eldon Fold 'N Roll Cart System 22.3680
## 6 Eldon Expressions Wood and Plastic Desk Accessories, Cherry Wood 48.8600
## Quantity Discount Profit
## 1 2 0.00 41.9136
## 2 3 0.00 219.5820
## 3 2 0.00 6.8714
## 4 5 0.45 -383.0310
## 5 2 0.20 2.5164
## 6 7 0.00 14.1694
str(store)
## 'data.frame': 9994 obs. of 21 variables:
## $ Row.ID : int 1 2 3 4 5 6 7 8 9 10 ...
## $ Order.ID : chr "CA-2016-152156" "CA-2016-152156" "CA-2016-138688" "US-2015-108966" ...
## $ Order.Date : chr "11/8/2016" "11/8/2016" "6/12/2016" "10/11/2015" ...
## $ Ship.Date : chr "11/11/2016" "11/11/2016" "6/16/2016" "10/18/2015" ...
## $ Ship.Mode : chr "Second Class" "Second Class" "Second Class" "Standard Class" ...
## $ Customer.ID : chr "CG-12520" "CG-12520" "DV-13045" "SO-20335" ...
## $ Customer.Name: chr "Claire Gute" "Claire Gute" "Darrin Van Huff" "Sean O'Donnell" ...
## $ Segment : chr "Consumer" "Consumer" "Corporate" "Consumer" ...
## $ Country : chr "United States" "United States" "United States" "United States" ...
## $ City : chr "Henderson" "Henderson" "Los Angeles" "Fort Lauderdale" ...
## $ State : chr "Kentucky" "Kentucky" "California" "Florida" ...
## $ Postal.Code : int 42420 42420 90036 33311 33311 90032 90032 90032 90032 90032 ...
## $ Region : chr "South" "South" "West" "South" ...
## $ Product.ID : chr "FUR-BO-10001798" "FUR-CH-10000454" "OFF-LA-10000240" "FUR-TA-10000577" ...
## $ Category : chr "Furniture" "Furniture" "Office Supplies" "Furniture" ...
## $ Sub.Category : chr "Bookcases" "Chairs" "Labels" "Tables" ...
## $ Product.Name : chr "Bush Somerset Collection Bookcase" "Hon Deluxe Fabric Upholstered Stacking Chairs, Rounded Back" "Self-Adhesive Address Labels for Typewriters by Universal" "Bretford CR4500 Series Slim Rectangular Table" ...
## $ Sales : num 262 731.9 14.6 957.6 22.4 ...
## $ Quantity : int 2 3 2 5 2 7 4 6 3 5 ...
## $ Discount : num 0 0 0 0.45 0.2 0 0 0.2 0.2 0 ...
## $ Profit : num 41.91 219.58 6.87 -383.03 2.52 ...
summary(store)
## Row.ID Order.ID Order.Date Ship.Date
## Min. : 1 Length:9994 Length:9994 Length:9994
## 1st Qu.:2499 Class :character Class :character Class :character
## Median :4998 Mode :character Mode :character Mode :character
## Mean :4998
## 3rd Qu.:7496
## Max. :9994
## Ship.Mode Customer.ID Customer.Name Segment
## Length:9994 Length:9994 Length:9994 Length:9994
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
## Country City State Postal.Code
## Length:9994 Length:9994 Length:9994 Min. : 1040
## Class :character Class :character Class :character 1st Qu.:23223
## Mode :character Mode :character Mode :character Median :56431
## Mean :55190
## 3rd Qu.:90008
## Max. :99301
## Region Product.ID Category Sub.Category
## Length:9994 Length:9994 Length:9994 Length:9994
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
## Product.Name Sales Quantity Discount
## Length:9994 Min. : 0.444 Min. : 1.00 Min. :0.0000
## Class :character 1st Qu.: 17.280 1st Qu.: 2.00 1st Qu.:0.0000
## Mode :character Median : 54.490 Median : 3.00 Median :0.2000
## Mean : 229.858 Mean : 3.79 Mean :0.1562
## 3rd Qu.: 209.940 3rd Qu.: 5.00 3rd Qu.:0.2000
## Max. :22638.480 Max. :14.00 Max. :0.8000
## Profit
## Min. :-6599.978
## 1st Qu.: 1.729
## Median : 8.666
## Mean : 28.657
## 3rd Qu.: 29.364
## Max. : 8399.976
I viewed the dataset and i also checked for the structure and a quick overview of the dataset
names(store)
## [1] "Row.ID" "Order.ID" "Order.Date" "Ship.Date"
## [5] "Ship.Mode" "Customer.ID" "Customer.Name" "Segment"
## [9] "Country" "City" "State" "Postal.Code"
## [13] "Region" "Product.ID" "Category" "Sub.Category"
## [17] "Product.Name" "Sales" "Quantity" "Discount"
## [21] "Profit"
colSums(is.na(store))
## Row.ID Order.ID Order.Date Ship.Date Ship.Mode
## 0 0 0 0 0
## Customer.ID Customer.Name Segment Country City
## 0 0 0 0 0
## State Postal.Code Region Product.ID Category
## 0 0 0 0 0
## Sub.Category Product.Name Sales Quantity Discount
## 0 0 0 0 0
## Profit
## 0
names(store)
## [1] "Row.ID" "Order.ID" "Order.Date" "Ship.Date"
## [5] "Ship.Mode" "Customer.ID" "Customer.Name" "Segment"
## [9] "Country" "City" "State" "Postal.Code"
## [13] "Region" "Product.ID" "Category" "Sub.Category"
## [17] "Product.Name" "Sales" "Quantity" "Discount"
## [21] "Profit"
#To check if the date columns are properly Format
#first, we check the current date type
dim(store)
## [1] 9994 21
I checked for the column names of the dataset
I Checked for MISSING VALUES and the data set has no missing values The dimension is 9994 rows and 21 columns
str(store$Order.Date)
## chr [1:9994] "11/8/2016" "11/8/2016" "6/12/2016" "10/11/2015" "10/11/2015" ...
str(store$Ship.Date)
## chr [1:9994] "11/11/2016" "11/11/2016" "6/16/2016" "10/18/2015" ...
store$Order.Date <- as.Date(store$Order.Date, format = "%m/%d/%Y")
store$Ship.Date <- as.Date(store$Ship.Date, format = "%m/%d/%Y")
str(store$Order.Date)
## Date[1:9994], format: "2016-11-08" "2016-11-08" "2016-06-12" "2015-10-11" "2015-10-11" ...
sum(duplicated(store))
## [1] 0
store$Category <- as.factor(store$Category)
store$Sub.Category <- as.factor(store$Sub.Category)
store$Region <- as.factor(store$Region)
store$Segment <- as.factor(store$Segment)
store$Product.Name<-as.factor(store$Product.Name)
The Order and ship Date column is a character vector, not a date type yet. So i Converted it to a proper Date format After changing the date to date format and also arranging it, I realized that some date were recorded multiple times, and when i checked through the dataset, I saw that they were different products entirely.
I also Checked if there was a duplicate rows, there was none. This shows that every row in the Superstore data is unique across all columns.
summary(store[, c("Sales", "Profit", "Discount")])
## Sales Profit Discount
## Min. : 0.444 Min. :-6599.978 Min. :0.0000
## 1st Qu.: 17.280 1st Qu.: 1.729 1st Qu.:0.0000
## Median : 54.490 Median : 8.666 Median :0.2000
## Mean : 229.858 Mean : 28.657 Mean :0.1562
## 3rd Qu.: 209.940 3rd Qu.: 29.364 3rd Qu.:0.2000
## Max. :22638.480 Max. : 8399.976 Max. :0.8000
store %>% filter(Discount < 0 | Discount > 1)
## [1] Row.ID Order.ID Order.Date Ship.Date Ship.Mode
## [6] Customer.ID Customer.Name Segment Country City
## [11] State Postal.Code Region Product.ID Category
## [16] Sub.Category Product.Name Sales Quantity Discount
## [21] Profit
## <0 rows> (or 0-length row.names)
unique(store$State)
## [1] "Kentucky" "California" "Florida"
## [4] "North Carolina" "Washington" "Texas"
## [7] "Wisconsin" "Utah" "Nebraska"
## [10] "Pennsylvania" "Illinois" "Minnesota"
## [13] "Michigan" "Delaware" "Indiana"
## [16] "New York" "Arizona" "Virginia"
## [19] "Tennessee" "Alabama" "South Carolina"
## [22] "Oregon" "Colorado" "Iowa"
## [25] "Ohio" "Missouri" "Oklahoma"
## [28] "New Mexico" "Louisiana" "Connecticut"
## [31] "New Jersey" "Massachusetts" "Georgia"
## [34] "Nevada" "Rhode Island" "Mississippi"
## [37] "Arkansas" "Montana" "New Hampshire"
## [40] "Maryland" "District of Columbia" "Kansas"
## [43] "Vermont" "Maine" "South Dakota"
## [46] "Idaho" "North Dakota" "Wyoming"
## [49] "West Virginia"
unique(store$Region)
## [1] South West Central East
## Levels: Central East South West
unique(store$Category)
## [1] Furniture Office Supplies Technology
## Levels: Furniture Office Supplies Technology
unique(store$Sub.Category)
## [1] Bookcases Chairs Labels Tables Storage Furnishings
## [7] Art Phones Binders Appliances Paper Accessories
## [13] Envelopes Fasteners Supplies Machines Copiers
## 17 Levels: Accessories Appliances Art Binders Bookcases Chairs ... Tables
The mean higher than the median means there are a few very large sales pulling the average up The $22,638 is very high,though it could be a big furniture order The -$6,599 is a loss, and in business, losses can definetely happen, Also, all discounts are between 0 and 1 which is fine I also checked if there are rows where Discount is less than 0 or greater than 1 because When Discount > 0, it means the customer was given a price reduction, and that directly affects Sales Revenue and Profit I checked if there are inconsistent values in categorical fields (e.g., “CA” vs “California”)
QUESTION 1 what is the total orders, categories, products, and sub-categories that are in the data set?
store %>%
summarise(
Total_Sales = sum(Sales),
Total_Profit = sum(Profit),
Total_Quantity = sum(Quantity))
## Total_Sales Total_Profit Total_Quantity
## 1 2297201 286397 37873
This code shows the Total sales, Total Profit and the Total Quantities sold.
QUESTION 2 what is the daily sales?
daily_sales <- store %>% group_by(Order.Date) %>% summarise( Total_Sales = sum(Sales),
Total_Profit = sum(Profit), Total_Orders = n() ) %>% arrange(Order.Date)
daily_sales
## # A tibble: 1,237 × 4
## Order.Date Total_Sales Total_Profit Total_Orders
## <date> <dbl> <dbl> <int>
## 1 2014-01-03 16.4 5.55 1
## 2 2014-01-04 288. -66.0 3
## 3 2014-01-05 19.5 4.88 1
## 4 2014-01-06 4407. 1358. 9
## 5 2014-01-07 87.2 -72.0 2
## 6 2014-01-09 40.5 10.9 2
## 7 2014-01-10 54.8 22.7 2
## 8 2014-01-11 9.94 3.08 1
## 9 2014-01-13 3554. 674. 11
## 10 2014-01-14 62.0 -53.3 1
## # ℹ 1,227 more rows
This code shows the Daily sales and profits fluctuate considerably. Most days with higher sales also tend to have higher profits
n() counts how many rows (orders) there are for that day
QUESTION 3 what is the yearly sales?
yearly_sales <- store %>%
mutate(Year = year(Order.Date)) %>%
group_by(Year) %>%
summarise(
Total_Sales = sum(Sales),
Total_Profit = sum(Profit),
Total_Orders = n()
) %>%
arrange(Year)
head(yearly_sales)
## # A tibble: 4 × 4
## Year Total_Sales Total_Profit Total_Orders
## <dbl> <dbl> <dbl> <int>
## 1 2014 484247. 49544. 1993
## 2 2015 470533. 61619. 2102
## 3 2016 609206. 81795. 2587
## 4 2017 733215. 93439. 3312
visualizing Total Sales by Year
ggplot(yearly_sales, aes(x = factor(Year), y = Total_Sales, fill = factor(Year))) +
geom_col(show.legend = FALSE) +
geom_text(aes(label = round(Total_Sales, 0)), vjust = -0.3, size = 3.5) +
labs(
title = "Total Sales by Year",
x = "Year",
y = "Total Sales"
) +
theme_minimal()
This chart shows that 2017 has the highest sales.
QUESTION 3 what is the average discount, sales and profit per order?
avg_per_order <- store %>%
group_by(Order.ID) %>%
summarise(
Order_Sales = sum(Sales),
Order_Profit = sum(Profit),
Order_Discount = mean(Discount)
) %>%
summarise(
Avg_Sales_Per_Order = mean(Order_Sales),
Avg_Profit_Per_Order = mean(Order_Profit),
Avg_Discount_Per_Order = mean(Order_Discount)
)
avg_per_order
## # A tibble: 1 × 3
## Avg_Sales_Per_Order Avg_Profit_Per_Order Avg_Discount_Per_Order
## <dbl> <dbl> <dbl>
## 1 459. 57.2 0.157
This code shows that on an average, each order generates about $459 in sales, $57 in profit, and receives an average discount of 15.7%.
QUESTION 4 What category generates the most sales and profit?
category_summary <- store %>%
group_by(Category) %>%
summarise(
Total_Sales = sum(Sales),
Total_Profit = sum(Profit)
) %>%
arrange(desc(Total_Sales)) # sort by highest sales
category_summary
## # A tibble: 3 × 3
## Category Total_Sales Total_Profit
## <fct> <dbl> <dbl>
## 1 Technology 836154. 145455.
## 2 Furniture 742000. 18451.
## 3 Office Supplies 719047. 122491.
visualizing sales by category
ggplot(category_summary, aes(x = reorder(Category, -Total_Sales), y = Total_Sales, fill = Category)) +
geom_col(show.legend = FALSE) +
geom_text(aes(label = round(Total_Sales, 0)), vjust = -0.3, size = 3) +
labs(
title = "Total Sales by Product Category",
x = "Product Category",
y = "Total Sales"
) +
theme_minimal()
This shows that technology was the most bought product and the total sale of across was in million
visualizing profits by category
ggplot(category_summary, aes(x = reorder(Category, -Total_Profit), y = Total_Profit, fill = Category)) +
geom_col(show.legend = FALSE) +
geom_text(aes(label = round(Total_Profit, 0)), vjust = -0.3, size = 3.5) +
labs(
title = "Total Profit by Product Category",
x = "Product Category",
y = "Total Profit"
) +
theme_minimal()
This also shows that Technology has the highest profit to the company.
QUESTION 5 What subcategory generates the most sales and profits
subcategory_summary <- store %>%
group_by(Sub.Category) %>%
summarise(
Total_Sales = sum(Sales),
Total_Profit = sum(Profit)
) %>%
arrange(desc(Total_Sales)) # sort by highest sales
subcategory_summary
## # A tibble: 17 × 3
## Sub.Category Total_Sales Total_Profit
## <fct> <dbl> <dbl>
## 1 Phones 330007. 44516.
## 2 Chairs 328449. 26590.
## 3 Storage 223844. 21279.
## 4 Tables 206966. -17725.
## 5 Binders 203413. 30222.
## 6 Machines 189239. 3385.
## 7 Accessories 167380. 41937.
## 8 Copiers 149528. 55618.
## 9 Bookcases 114880. -3473.
## 10 Appliances 107532. 18138.
## 11 Furnishings 91705. 13059.
## 12 Paper 78479. 34054.
## 13 Supplies 46674. -1189.
## 14 Art 27119. 6528.
## 15 Envelopes 16476. 6964.
## 16 Labels 12486. 5546.
## 17 Fasteners 3024. 950.
visualizing Sales by Sub category
ggplot(subcategory_summary, aes(x = reorder(Sub.Category, -Total_Sales), y = Total_Sales, fill = Sub.Category)) +
geom_col(show.legend = FALSE) +
coord_flip()+
geom_text(aes(label = round(Total_Sales, 0)), hjust = -0.2, size = 3) +
labs(
title = "Total Sales by Product Sub Category",
x = "Product Sub Category",
y = "Total Sales"
) +
theme_minimal()
From this chart, the Sub category with the highest sales are Phones and
the Category is Technology is the Category with the highest sales while
the product with the lowest sales are Fasteners
visualizing profits by Sub category
ggplot(subcategory_summary, aes(x = reorder(Sub.Category, -Total_Profit), y = Total_Profit, fill = Sub.Category)) +
geom_col(show.legend = FALSE) +
coord_flip()+
geom_text(aes(label = round(Total_Profit, 0)), hjust = -0.2, size = 3) +
labs(
title = "Total Profit by Product Sub Category",
x = "Product Sub Category",
y = "Total Profit"
) +
theme_minimal()
QUESTION 6 which region records the highest profit margin?
region_margin <- store %>%
group_by(Region) %>%
summarise(
Total_Sales = sum(Sales),
Total_Profit = sum(Profit)
) %>%
mutate(Profit_Margin = (Total_Profit / Total_Sales) * 100) %>%
arrange(desc(Profit_Margin)) # highest margin first
region_margin
## # A tibble: 4 × 4
## Region Total_Sales Total_Profit Profit_Margin
## <fct> <dbl> <dbl> <dbl>
## 1 West 725458. 108418. 14.9
## 2 East 678781. 91523. 13.5
## 3 South 391722. 46749. 11.9
## 4 Central 501240. 39706. 7.92
visualizing the profit margin by region
ggplot(region_margin, aes(x = Region, y = Total_Sales, fill = Region)) +
geom_bar(stat = "identity") +
geom_text(aes(label = round(Total_Sales, 0)), vjust = -0.3, size = 3)+
labs(
title = "Total Sales by Region",
x = "Region",
y = "Total Sales ($)"
) +
theme_minimal() +
theme(legend.position = "none")
The bar chart shows that the West and East regions recorded the highest total sales, while the South region had the lowest.
state_margin <- store %>%
group_by(State) %>%
summarise(
Total_Sales = sum(Sales),
Total_Profit = sum(Profit)
) %>%
mutate(Profit_Margin = (Total_Profit / Total_Sales) * 100) %>%
arrange(desc(Profit_Margin)) # highest margin first
state_margin
## # A tibble: 49 × 4
## State Total_Sales Total_Profit Profit_Margin
## <chr> <dbl> <dbl> <dbl>
## 1 District of Columbia 2865. 1060. 37.0
## 2 Delaware 27451. 9977. 36.3
## 3 Minnesota 29863. 10823. 36.2
## 4 Maine 1271. 454. 35.8
## 5 Arkansas 11678. 4009. 34.3
## 6 Indiana 53555. 18383. 34.3
## 7 Georgia 49096. 16250. 33.1
## 8 Montana 5589. 1833. 32.8
## 9 Rhode Island 22628. 7286. 32.2
## 10 Michigan 76270. 24463. 32.1
## # ℹ 39 more rows
Top 10 states that recorded the highest sales
top_states <- store %>%
group_by(State) %>%
summarise(
Total_Sales = sum(Sales),
Total_Profit = sum(Profit)
) %>%
arrange(desc(Total_Sales)) %>% # sort by sales
slice_head(n = 10) # pick top 10
top_states
## # A tibble: 10 × 3
## State Total_Sales Total_Profit
## <chr> <dbl> <dbl>
## 1 California 457688. 76381.
## 2 New York 310876. 74039.
## 3 Texas 170188. -25729.
## 4 Washington 138641. 33403.
## 5 Pennsylvania 116512. -15560.
## 6 Florida 89474. -3399.
## 7 Illinois 80166. -12608.
## 8 Ohio 78258. -16971.
## 9 Michigan 76270. 24463.
## 10 Virginia 70637. 18598.
ggplot(top_states, aes(x = reorder(State, Total_Sales), y = Total_Sales, fill = State)) +
geom_col() +
coord_flip() + # flip coordinates for better readability
labs(
title = "Top 10 States by Total Sales",
x = "State",
y = "Total Sales"
) +
theme_minimal()
This shows that California leads in total sales by a significant margin, followed by New York and Texas, and this indicates that these three states are the top revenue contributors among the top 10 states.
Question 7:How do discount rates differ across product categories in the Superstore dataset?
ggplot(store, aes(x = Category, y = Discount, fill = Category)) +
geom_boxplot() +
labs(title = "Distribution of Discounts by Category",
x = "Category",
y = "Discount Rate") +
theme_minimal()
ggplot(daily_sales, aes(x = Total_Sales)) +
geom_histogram(bins = 100, fill = "skyblue", color = "white") +
labs(
title = "Distribution of Daily Total Sales",
x = "Total Sales per Day (USD)",
y = "Frequency"
) +
theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5, face = "bold", size = 14)
)
This is the right skewed distribution because the tail is Longer on the right side and also, the Mean > Median.
ggplot(yearly_sales, aes(x = Year, y = Total_Sales)) +
geom_line(color = "red", size = 1) +
geom_point(color="black",size=3)+
labs(
title = "Yearly Sales Trend Over Time",
x = "Order Date",
y = "Total Sales"
) +
theme_minimal()
## 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.