# don't show warnings while knitting
knitr::opts_chunk$set(warning = FALSE, message = FALSE)
library(ggplot2)
library(sqldf)
# constants
my_working_dir_pc <- "C:\\Users\\Alex\\OneDrive - Lehigh University\\GBUS461\\Data\\Sephora\\Sephora"
myFile_products <- "products.csv"
myFile_reviews <- "reviews.csv"
myFile_authors <- "authors.csv"
# import data
setwd(my_working_dir_pc)
products <- read.csv(myFile_products)
reviews <- read.csv(myFile_reviews)
authors <- read.csv(myFile_authors)
sqldf::sqldf("
SELECT *
FROM products p INNER JOIN reviews r ON p.product_id = r.product_id
INNER JOIN authors a ON r.author_id = r.author_id
LIMIT 100
")
# table row count and review count
# check to ensure they are the same
sqldf("
SELECT COUNT(review_id), COUNT(*), COUNT(DISTINCT review_id)
FROM reviews
")
# without "DISTINCT" it does not matter which
# column you use (previously review_id, now author_id)
# it shows the table row count.
# why is counting up DISTINCT authors < table row count?
sqldf("
SELECT COUNT(author_id), COUNT(DISTINCT author_id)
FROM reviews
")
# review count by rating, order = ascending
sqldf("
SELECT rating, COUNT(review_id) AS NumReviews
FROM reviews
GROUP BY rating
ORDER BY rating
")
# review count by rating, order = descending
sqldf("
SELECT rating, COUNT(review_id) AS NumReviews
FROM reviews
GROUP BY rating
ORDER BY rating DESC
")
# convert to date data type, and from the date, calculate month and year
reviews$submission_date <- as.Date(reviews$submission_date, format = "%m/%d/%y")
reviews$ReviewDate <- format(reviews$submission_date, "%d")
reviews$ReviewMonthInt <- as.integer(format(reviews$submission_date, "%m"))
reviews$ReviewMonth <- format(reviews$submission_date, "%b")
reviews$ReviewYear <- format(reviews$submission_date, "%y")
# count and group by
sqldf("
SELECT ReviewMonth, COUNT(review_id) AS NumReviews
FROM reviews
GROUP BY ReviewMonth
ORDER BY ReviewMonthInt
")
# where clause
sqldf("
SELECT ReviewMonth, COUNT(review_id) AS NumReviews
FROM reviews
WHERE ReviewYear = 21 OR ReviewMonthInt BETWEEN 2 AND 6
GROUP BY ReviewMonth
ORDER BY ReviewMonthInt
")
# r bar chart by identity
reviewsByMonth <- sqldf("
SELECT ReviewMonth, ReviewMonthInt, COUNT(review_id) AS Reviews
FROM reviews
WHERE ReviewYear BETWEEN 15 AND 22
GROUP BY ReviewMonth, ReviewMonthInt
ORDER BY ReviewMonthInt
")
barEx <- ggplot(data = reviewsByMonth, aes(x = reorder(ReviewMonth, ReviewMonthInt), y = Reviews)) +
geom_bar(stat = "identity") +
labs(title = "Luxury Brands Engagement by Month", x = "Review Month", y = "# of Reviews")
barEx

# same bar chart by count
barExC <- ggplot(data = reviews[reviews$ReviewYear > 14 & reviews$ReviewYear < 23,],
aes(x = reorder(ReviewMonth, ReviewMonthInt))) + geom_bar(stat = "count") +
labs(title = "Luxury Brands Engagement by Month", x = "Review Month", y = "# of Reviews")
barExC

# average bar chart
avgRatingsByMonth <- sqldf::sqldf("
SELECT ReviewMonth, ReviewMonthInt, AVG(rating) AS AvgRatings
FROM reviews
WHERE ReviewYear BETWEEN 15 AND 22
GROUP BY ReviewMonth, ReviewMonthInt
ORDER BY ReviewMonthInt
")
library(ggplot2)
ggplot(data = avgRatingsByMonth, aes(x = reorder(ReviewMonth, ReviewMonthInt), y = AvgRatings)) +
geom_bar(stat = "identity") +
labs(title = "Luxury Brands Average Rating by Month", x = "Review Month", y = "Average Rating")

# average bar chart with "summary"
ggplot(data = reviews[reviews$ReviewYear > 14 & reviews$ReviewYear < 23,],
aes(x = reorder(ReviewMonth, ReviewMonthInt), y = rating)) + geom_bar(stat = "summary", fun = mean) +
labs(title = "Luxury Brands Average Rating by Month", x = "Review Month", y = "Average Rating")

# generate data for bar chart by identity
d <- sqldf::sqldf("
SELECT primary_category, COUNT(DISTINCT brand_id) AS Brands
FROM products WHERE primary_category < 'H'
GROUP BY primary_category ORDER BY primary_category
")
barExA <- ggplot(data = d, aes(x = primary_category, y = Brands)) +
geom_bar(stat = "identity") +
labs(title = "Brands by Category", x = "Primary Category", y = "# of Brands")
barExAC <- ggplot(data = products[products$primary_category < 'H',], aes(x = primary_category)) +
geom_bar(stat = "count") +
labs(title = "Products by Category", x = "Primary Category", y = "# of Products")
gridExtra::grid.arrange(barExA, barExAC, nrow=1, ncol=2)

# histogram versus bar chart
reviews$submission_date <- as.Date(reviews$submission_date, format = "%m/%d/%y")
reviews$ReviewDate <- format(reviews$submission_date, "%d")
reviews$ReviewMonthInt <- as.integer(format(reviews$submission_date, "%m"))
reviews$ReviewMonth <- format(reviews$submission_date, "%b")
reviews$ReviewYear <- format(reviews$submission_date, "%y")
reviewsByMonth <- sqldf::sqldf("
SELECT ReviewMonth, ReviewMonthInt, COUNT(review_id) AS Reviews
FROM reviews
WHERE ReviewYear BETWEEN 15 AND 22
GROUP BY ReviewMonth, ReviewMonthInt
ORDER BY ReviewMonthInt
")
library(ggplot2)
barEx <- ggplot(data = reviewsByMonth, aes(x = reorder(ReviewMonth, ReviewMonthInt), y = Reviews)) +
geom_bar(stat = "identity") +
labs(title = "Luxury Brands Engagement by Month", x = "Review Month", y = "# of Reviews")
hisEx <- ggplot(data = reviews, aes(rating)) +
geom_histogram(binwidth = .5) +
labs(title = "Luxury Brands Ratings Distribution", x = "Review Ratings (out of 5 Stars)", y = "# of Reviews")
gridExtra::grid.arrange(hisEx, barEx, nrow=1, ncol=2)

# histogram: all products' average lifetime ratings
ggplot(data = products[is.na(products$rating) == FALSE,], aes(rating)) +
geom_histogram(binwidth = .1) +
labs(title = "Ratings Distribution of All Products", x = "Average Ratings (out of 5 Stars)", y = "# of Products")

#Discussion: possible next steps of analysis?
# histogram: all products' prices
ggplot(data = products[is.na(products$price_usd) == FALSE,], aes(price_usd)) +
geom_histogram(binwidth = 5) +
labs(title = "Price Distribution of All Products", x = "Prices (USD)", y = "# of Products")

#Discussion: possible next steps of analysis?
# bar chart that shows product count by category
# same bar chart by count
bar_PCategories <- ggplot(data = products,
aes(x = primary_category)) + geom_bar(stat = "count") +
labs(title = "Number of Products in Each Category on Sephora.com", x = "Primary Category", y = "# of Products")
bar_PCategories

ggplot(data = reviewsByMonth, aes(x = reorder(ReviewMonth, ReviewMonthInt), y = Reviews)) +
geom_bar(stat = "identity") + coord_flip() +
labs(title = "Luxury Brands Engagement by Month", x = "Review Month", y = "# of Reviews")

# average prices of skincare products by highlights
# before we plot the bar chart, we need to generate its input
# for each product, we need to do text processing to split the highlights column
# and correspond the results to the product id
# once we figure out how to do it for 1 product, we loop through the entire
# table and do this processing for all products
# create an empty table called "highlight_table"
# it has two columns, product id and a single highlight
# if a product has multiple highlights, say, 2, it will show up as 2 rows
# in this table
highlight_table <- data.frame(product_id = character(), highlight = character())
# product_id is column 1 in the products table
# highlights is column 21 in the products table
colProductId <- 1
colHighlights <- 21
for (i in 1:nrow(products)){
#for (i in 1:1) {
current_highlights <- products[i,colHighlights] #get the highlights from products
if(nchar(current_highlights) > 0){ #keep going only if highlights is not blank
current_product_id <- products[i,colProductId] #get the product id
current_highlights <- gsub("\\[|\\]|'", "", current_highlights)
#print(current_product_id)
#Split the input using the comma as a separator
current_highlights <- unlist(strsplit(current_highlights, ", "))
highlight_table <- rbind(highlight_table, data.frame(product_id = current_product_id, highlight = current_highlights))
}
}
# after we have created and populated the highlights table
# take a look and do some simple counts
# E.g, how many products have highlights? 6287
# and how many highlights are there?
sqldf("
SELECT COUNT(DISTINCT product_id) products, COUNT(DISTINCT highlight) highlights
FROM highlight_table
")
# now, let's merge the highlight_table with the products table
# and calculate the average by each highlight
skincareHighlightsPrices <- sqldf::sqldf("
SELECT h.highlight AS Highlight, AVG(price_usd) AS AvgPrice
FROM highlight_table h INNER JOIN products p
ON h.product_id = p.product_id
WHERE primary_category = 'Skincare'
GROUP BY h.highlight
HAVING COUNT(DISTINCT h.product_id) > 10
ORDER BY AVG(price_usd) DESC
LIMIT 50
")
# average prices of skincare products by highlights, bar chart
# vertical bar chart
bar_HighlightPrices <- ggplot(data = skincareHighlightsPrices, aes(x = reorder(Highlight, AvgPrice), y = AvgPrice)) +
geom_bar(stat = "identity") + coord_flip() +
labs(title = "Top 50 Highlights of Skincare Products by Avg. Price", x = "Highlight", y = "Avg. Product Price")
#ggsave("bar_HighlightPrices.pdf") # saves the graph to a PDF file
bar_HighlightPrices

# average ratings of skincare products by highlights, bar chart
# now, let's merge the highlight_table with the products table
# and calculate the average by each highlight
skincareHighlightsRatings <- sqldf::sqldf("
SELECT h.highlight AS Highlight, AVG(rating) AS AvgRating
FROM highlight_table h INNER JOIN products p
ON h.product_id = p.product_id
WHERE primary_category = 'Skincare'
GROUP BY h.highlight
HAVING COUNT(DISTINCT h.product_id) > 10
ORDER BY AVG(rating) DESC
LIMIT 50
")
# vertical bar chart
bar_HighlightRatings <- ggplot(data = skincareHighlightsRatings, aes(x = reorder(Highlight, AvgRating), y = AvgRating)) +
geom_bar(stat = "identity") + coord_flip() +
labs(title = "Top 50 Highlights of Skincare Products by Avg. Rating", x = "Highlight", y = "Avg. Product Rating")
bar_HighlightRatings

# operators
sqldf::sqldf("
SELECT COUNT(a.author_id), COUNT(DISTINCT a.author_id)
FROM reviews r INNER JOIN authors a
ON r.author_id = a.author_id
WHERE (r.rating >= 4 AND r.helpfulness > 0.5)
OR a.skin_tone = 'light'
")
# operator NOT IN
sqldf::sqldf("
SELECT COUNT(author_id)
FROM authors
WHERE skin_tone NOT IN ('light', 'medium')
")
# grouped bar chart
d <- sqldf::sqldf("
SELECT skin_tone, rating,COUNT(r.review_id) AS Reviews
FROM reviews r
INNER JOIN authors a ON r.author_id = a.author_id
GROUP BY skin_tone, rating
")
ggplot(data = d, aes(x = rating, y = Reviews, fill = skin_tone)) + geom_bar(stat = "identity", position = position_dodge()) +
labs(title = "Reviews by Rating and Skin Tone", x = "Review Rating", y = "# of Reviews", fill = "Skin Tone")

# stacked bar chart
d <- sqldf::sqldf("
SELECT skin_tone, rating,COUNT(r.review_id) AS Reviews
FROM reviews r
INNER JOIN authors a ON r.author_id = a.author_id
GROUP BY skin_tone, rating
")
ggplot(data = d, aes(x = rating, y = Reviews, fill = skin_tone)) + geom_bar(stat = "identity") +
labs(title = "Reviews by Rating and Skin Tone", x = "Review Rating", y = "# of Reviews", fill = "Skin Tone")

# stacked bar chart that shows percentages
d1 <- sqldf::sqldf("SELECT skin_tone, rating,COUNT(r.review_id) AS Reviews
FROM reviews r INNER JOIN authors a ON r.author_id = a.author_id
GROUP BY skin_tone, rating")
d2 <- sqldf::sqldf("
SELECT rating,COUNT(r.review_id) AS Reviews
FROM reviews r
INNER JOIN authors a ON r.author_id = a.author_id
GROUP BY rating")
d <- sqldf::sqldf("SELECT d1.skin_tone, d1.rating, d1.Reviews*1.0/d2.Reviews AS PercReviews
FROM d1 INNER JOIN d2 ON d1.rating = d2.rating")
ggplot(data = d, aes(x = rating, y = PercReviews, fill = skin_tone)) + geom_bar(stat = "identity") +
labs(title = "% Reviews by Rating and Skin Tone", x = "Review Rating", y = "% of Reviews", fill = "Skin Tone")

# pie charts, 2D and 3D
totalReviews <- nrow(reviews)
sqlStr <- paste("SELECT rating, COUNT(review_id)*1.0/", as.character(totalReviews), sep="")
sqlStr <- paste(sqlStr, "AS Reviews FROM reviews GROUP BY rating")
d <- sqldf::sqldf(sqlStr)
library(plotrix) #for plotting 3D pie charts
#native R function
pie(d$Reviews, labels = d$rating)

# pie chart with ggplot
ggplot(d, aes(x = "", y = Reviews, fill = rating)) +
geom_col() + geom_text(aes(label = round(Reviews,3)),
position = position_stack(vjust = 0.5)) +
coord_polar(theta = "y") +
labs(title = "% Reviews by Rating", fill = "Rating")

# pie chart with ggplot
ggplot(d, aes(x = rating, y = Reviews, fill = rating)) +
geom_col() + geom_text(aes(label = round(Reviews,3)),
position = position_stack(vjust = 0.5)) + coord_flip() +
labs(title = "% Reviews by Rating", y = "% Reviews", fill = "Rating")

ggplot(d, aes(rating, Reviews)) +
geom_bar(stat = "identity") +
coord_flip() +
labs(title = "Reviews by Rating", x = "# of Reviews", y = "Rating")

# generate data
d <- sqldf::sqldf("
SELECT ReviewYear, rating, COUNT(review_id) aS Reviews
FROM reviews WHERE ReviewYear BETWEEN 15 AND 22 GROUP BY ReviewYear, rating
")
# area chart
ggplot(data=d, aes(x=as.integer(ReviewYear), y=Reviews, fill=as.character(rating))) + geom_area() +
labs(title = "Reviews by Rating", x = "Year", y = "# of Reviews", fill = "Rating")

# area chart with year breaks
ggplot(data=d, aes(x=as.integer(ReviewYear), y=Reviews, fill=as.character(rating))) + geom_area() +
labs(title = "Reviews by Rating", x = "Year", y = "# of Reviews", fill = "Rating") +
scale_x_continuous(breaks=c(15,16,17,18,19,20,21,22))

# line chart
ggplot(data=d[d$rating==5,], aes(x=as.integer(ReviewYear), y=Reviews)) + geom_line() + geom_point() +
scale_x_continuous(breaks=c(15,16,17,18,19,20,21,22)) +
labs(title = "5-Star Reviews by Year", x = "Year", y = "# of Reviews", fill = "Rating")

# multiple-series line chart
ggplot(data=d, aes(x=as.integer(ReviewYear), y=Reviews, group=as.character(rating))) +
geom_line(aes(linetype=as.character(rating))) + geom_point() +
scale_x_continuous(breaks=c(15,16,17,18,19,20,21,22)) +
labs(title = "Yearly Reviews by Rating", x = "Year", y = "# of Reviews") +
theme(legend.position="bottom") + theme(legend.title = element_blank())

# boxplot
ggplot(data=products[products$price_usd < 1000,], aes(x="",y=price_usd)) + geom_boxplot() +
labs(title = "Product Price Distribution", x = "", y = "Price")

# horizontal boxplot
ggplot(data=products[products$price_usd < 1000,], aes(x="",y=price_usd)) + geom_boxplot() +
labs(title = "Product Price Distribution", x = "", y = "Price") + coord_flip()

ggplot(data=products[products$price_usd < 1000,], aes(x=primary_category,y=price_usd)) + geom_boxplot() +
labs(title = "Product Price Distribution", x = "Primary Category", y = "Price") + coord_flip()

#AF original code
# generate data
d1 <- sqldf::sqldf("
SELECT ReviewYear, rating, COUNT(review_id) aS Reviews
FROM reviews WHERE ReviewYear BETWEEN 15 AND 23 GROUP BY ReviewYear, rating
")
d2 <- sqldf::sqldf("
SELECT ReviewYear, COUNT(review_id) as Reviews
FROM reviews
WHERE ReviewYear BETWEEN 15 AND 23
GROUP BY ReviewYear
")
d <- sqldf::sqldf("
SELECT d1.ReviewYear, d1.rating, d1.Reviews*1.0/d2.Reviews as PercReviews
FROM d1 INNER JOIN d2 on d1.ReviewYear = d2.ReviewYear
")
# area chart with year breaks
ggplot(data=d, aes(x=as.integer(ReviewYear), y=PercReviews, fill=as.character(rating))) + geom_area() +
labs(title = "Reviews by Rating", x = "Year", y = "# of Reviews", fill = "Rating") +
scale_x_continuous(breaks=c(15,16,17,18,19,20,21,22))

head(reviews)
reviews$ReviewDayOfWeek <- weekdays(reviews$submission_date)
d <- sqldf::sqldf("
SELECT ReviewDayOfWeek as day, AVG(rating) as avgRating
FROM reviews
GROUP BY day
")
ggplot(data = d, aes(x = day, y = avgRating, fill = day)) + geom_bar(stat = "identity", position = position_dodge()) +
labs(title = "Average Rating by Day", x = "Day of Week", y = "Average Rating", fill = "Day of Week")

d <- sqldf::sqldf("
SELECT ReviewDayOfWeek as day, COUNT(review_id) as numberOfReviews
FROM reviews
GROUP BY day
")
ggplot(data = d, aes(x = day, y = numberOfReviews, fill = day)) + geom_bar(stat = "identity", position = position_dodge()) +
labs(title = "Number of Reviews on Each Day", x = "Day of Week", y = "# of Reviews", fill = "Day of Week")

ggplot(data=reviews, aes(x=ReviewDayOfWeek,y=rating, fill = ReviewDayOfWeek)) + geom_boxplot() +
labs(title = "Rating Distribution by Day", x = "Day of Week", y = "Average Rating")

ggplot(products, aes(x = price_usd, y = rating)) +
geom_point() +
geom_smooth(method = "lm", se = FALSE) +
xlim(0,500)

t.test(products$rating, products$price_usd)
##
## Welch Two Sample t-test
##
## data: products$rating and products$price_usd
## t = -81.498, df = 8494.6, p-value < 2.2e-16
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
## -48.60265 -46.31952
## sample estimates:
## mean of x mean of y
## 4.194513 51.655595