In today’s digital age, product reviews have become crucial for businesses to gather consumer feedback, greatly influencing business strategy and customer relationship management. For global retailers like Sephora, understanding consumer sentiment through these reviews is vital. This study utilizes Sephora’s product review data to conduct sentiment and text analysis, extracting significant patterns and emotions expressed by consumers. By analyzing this data, the study aims to assist Sephora in enhancing its product offerings, boosting customer satisfaction, and aligning strategies with consumer preferences and expectations.
Sentiment Analysis: This involves categorizing customer reviews into positive, negative, or neutral sentiments. The aim is to gauge overall customer satisfaction and pinpoint areas for improvement. By tracking sentiment trends, Sephora can effectively manage its brand reputation and address consumer needs.
Text Analysis: This focuses on topic modeling and extracting keywords from the review text. The goal is to uncover prevalent themes and concerns voiced by customers. Identifying these topics helps Sephora understand which product features or service attributes are favored by customers, guiding product development and marketing strategies.
Product data content has 27 attributes. The attributes are as the following:
Review data content has 15 attributes. The attributes are as the following:
library(caret)
library(dplyr)
library(ggplot2)
library(LiblineaR)
library(lubridate)
library(NLP)
library(purrr)
library(quanteda)
library(quanteda.textmodels)
library(RCurl)
library(readxl)
library(slam)
library(SnowballC)
library(SparseM)
library(stringr)
library(text2vec)
library(tm)
library(tokenizers)
library(topicmodels)
library(wordcloud)
library(XML)
product <- read.csv("product_info.csv")
reviews_0_250 <- read.csv("reviews_0-250.csv")
reviews_250_500 <- read.csv("reviews_250-500.csv")
reviews_500_750 <- read.csv("reviews_500-750.csv")
reviews_750_1250 <- read.csv("reviews_750-1250.csv")
reviews_1250_end <- read.csv("reviews_1250-end.csv")
# Show same columns for 2 dataset
common_columns <- intersect(colnames(product),colnames(reviews_0_250))
print(common_columns)
## [1] "product_id" "product_name" "brand_name" "rating" "price_usd"
# Merge all reviews
all_reviews_raw <- rbind(reviews_0_250, reviews_250_500, reviews_500_750, reviews_750_1250, reviews_1250_end)
# Show the dimension
dim(all_reviews_raw)
## [1] 1094411 19
# Identifying columns in 'product' not present in 'all_reviews_raw'
cols_to_use <- setdiff(names(product), names(all_reviews_raw))
# Adding 'product_id' to the list of columns to use
cols_to_use <- c(cols_to_use, "product_id")
# Merging the data frames
merged_outer_join <- merge(all_reviews_raw, product[cols_to_use], by = "product_id", all = TRUE)
# Removing the 'X' column by setting it to NULL
merged_outer_join$X <- NULL
# Show the dimensions of the raw/merged data frame
dim(all_reviews_raw)
## [1] 1094411 19
dim(product)
## [1] 8494 27
dim(merged_outer_join)
## [1] 1100554 40
# Data types of each column
data_types <- sapply(merged_outer_join, class)
# Counts unique values in each column
unique_value_counts <- sapply(merged_outer_join, function(x) length(unique(na.omit(x))))
# Convert common incorrect NA representations to actual NA values
merged_outer_join[merged_outer_join == ""] <- NA # Assuming empty strings should be treated as NA
# Calculate the number of missing values per column
missing_counts <- colSums(is.na(merged_outer_join))
# Calculate the percentage of missing values
total_rows <- nrow(merged_outer_join)
missing_percentage <- (missing_counts / total_rows) * 100
# Creating the initial summary table
summary_table <- data.frame(
Column = names(data_types),
DataType = data_types,
TotalObservations = rep(total_rows, length(missing_counts)), # Adding a column for total observations
UniqueValueCount = unique_value_counts,
MissingCount = missing_counts,
MissingPercentage = sprintf("%.2f%%", missing_percentage) # Adding percentage as a formatted string
)
# Sorting the summary table by MissingCount in descending order
summary_table <- summary_table[order(-summary_table$MissingCount),]
# Print the sorted summary table
print(summary_table)
## Column DataType TotalObservations
## variation_desc variation_desc character 1100554
## sale_price_usd sale_price_usd numeric 1100554
## value_price_usd value_price_usd numeric 1100554
## child_max_price child_max_price numeric 1100554
## child_min_price child_min_price numeric 1100554
## helpfulness helpfulness numeric 1100554
## review_title review_title character 1100554
## hair_color hair_color character 1100554
## eye_color eye_color character 1100554
## skin_tone skin_tone character 1100554
## is_recommended is_recommended numeric 1100554
## tertiary_category tertiary_category character 1100554
## skin_type skin_type character 1100554
## highlights highlights character 1100554
## variation_value variation_value character 1100554
## variation_type variation_type character 1100554
## size size character 1100554
## ingredients ingredients character 1100554
## review_text review_text character 1100554
## author_id author_id character 1100554
## rating rating integer 1100554
## total_feedback_count total_feedback_count integer 1100554
## total_neg_feedback_count total_neg_feedback_count integer 1100554
## total_pos_feedback_count total_pos_feedback_count integer 1100554
## submission_time submission_time character 1100554
## product_name product_name character 1100554
## brand_name brand_name character 1100554
## price_usd price_usd numeric 1100554
## reviews reviews integer 1100554
## secondary_category secondary_category character 1100554
## product_id product_id character 1100554
## brand_id brand_id integer 1100554
## loves_count loves_count integer 1100554
## limited_edition limited_edition integer 1100554
## new new integer 1100554
## online_only online_only integer 1100554
## out_of_stock out_of_stock integer 1100554
## sephora_exclusive sephora_exclusive integer 1100554
## primary_category primary_category character 1100554
## child_count child_count integer 1100554
## UniqueValueCount MissingCount MissingPercentage
## variation_desc 936 1091034 99.13%
## sale_price_usd 88 1090576 99.09%
## value_price_usd 174 1069365 97.17%
## child_max_price 222 644796 58.59%
## child_min_price 208 644796 58.59%
## helpfulness 3767 567735 51.59%
## review_title 364106 316797 28.79%
## hair_color 8 232911 21.16%
## eye_color 7 215771 19.61%
## skin_tone 15 176682 16.05%
## is_recommended 2 174131 15.82%
## tertiary_category 119 161894 14.71%
## skin_type 5 117700 10.69%
## highlights 4418 115729 10.52%
## variation_value 2730 64644 5.87%
## variation_type 8 52560 4.78%
## size 2056 44661 4.06%
## ingredients 6539 22843 2.08%
## review_text 969420 7587 0.69%
## author_id 503216 6143 0.56%
## rating 5 6143 0.56%
## total_feedback_count 676 6143 0.56%
## total_neg_feedback_count 259 6143 0.56%
## total_pos_feedback_count 590 6143 0.56%
## submission_time 5317 6143 0.56%
## product_name 2334 6143 0.56%
## brand_name 142 6143 0.56%
## price_usd 221 6143 0.56%
## reviews 1556 278 0.03%
## secondary_category 42 8 0.00%
## product_id 8494 0 0.00%
## brand_id 304 0 0.00%
## loves_count 7436 0 0.00%
## limited_edition 2 0 0.00%
## new 2 0 0.00%
## online_only 2 0 0.00%
## out_of_stock 2 0 0.00%
## sephora_exclusive 2 0 0.00%
## primary_category 9 0 0.00%
## child_count 55 0 0.00%
# Identify columns with more than 50% missing values, excluding the 'helpfulness' column
columns_to_drop <- names(missing_percentage[missing_percentage > 50 & names(missing_percentage) != "helpfulness"])
# Drop these columns from the dataframe
merged_clean <- merged_outer_join[, !(names(merged_outer_join) %in% columns_to_drop)]
# Remove rows with any missing values
merged_clean <- na.omit(merged_clean)
# Display the cleaned dataframe dimensions
dim(merged_clean)
## [1] 216776 35
# Display descriptive statistics of the data frame
print(summary(merged_clean))
## product_id author_id rating is_recommended
## Length:216776 Length:216776 Min. :1.000 Min. :0.0000
## Class :character Class :character 1st Qu.:3.000 1st Qu.:1.0000
## Mode :character Mode :character Median :5.000 Median :1.0000
## Mean :4.067 Mean :0.7651
## 3rd Qu.:5.000 3rd Qu.:1.0000
## Max. :5.000 Max. :1.0000
## helpfulness total_feedback_count total_neg_feedback_count
## Min. :0.0000 Min. : 1.000 Min. : 0.000
## 1st Qu.:0.6667 1st Qu.: 2.000 1st Qu.: 0.000
## Median :0.9242 Median : 4.000 Median : 1.000
## Mean :0.7739 Mean : 9.472 Mean : 1.964
## 3rd Qu.:1.0000 3rd Qu.: 9.000 3rd Qu.: 2.000
## Max. :1.0000 Max. :3851.000 Max. :500.000
## total_pos_feedback_count submission_time review_text
## Min. : 0.000 Length:216776 Length:216776
## 1st Qu.: 1.000 Class :character Class :character
## Median : 3.000 Mode :character Mode :character
## Mean : 7.508
## 3rd Qu.: 7.000
## Max. :3481.000
## review_title skin_tone eye_color skin_type
## Length:216776 Length:216776 Length:216776 Length:216776
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
## hair_color product_name brand_name price_usd
## Length:216776 Length:216776 Length:216776 Min. : 3.00
## Class :character Class :character Class :character 1st Qu.: 28.00
## Mode :character Mode :character Mode :character Median : 42.00
## Mean : 49.83
## 3rd Qu.: 64.00
## Max. :425.00
## brand_id loves_count reviews size
## Min. :1063 Min. : 0 Min. : 1 Length:216776
## 1st Qu.:5987 1st Qu.: 24567 1st Qu.: 477 Class :character
## Median :6189 Median : 63782 Median :1121 Mode :character
## Mean :5946 Mean :122074 Mean :1742
## 3rd Qu.:6285 3rd Qu.:167407 3rd Qu.:2449
## Max. :8017 Max. :763168 Max. :8733
## variation_type variation_value ingredients limited_edition
## Length:216776 Length:216776 Length:216776 Min. :0.00000
## Class :character Class :character Class :character 1st Qu.:0.00000
## Mode :character Mode :character Mode :character Median :0.00000
## Mean :0.01874
## 3rd Qu.:0.00000
## Max. :1.00000
## new online_only out_of_stock sephora_exclusive
## Min. :0.00000 Min. :0.00000 Min. :0.00000 Min. :0.0000
## 1st Qu.:0.00000 1st Qu.:0.00000 1st Qu.:0.00000 1st Qu.:0.0000
## Median :0.00000 Median :0.00000 Median :0.00000 Median :0.0000
## Mean :0.01208 Mean :0.06424 Mean :0.01782 Mean :0.4233
## 3rd Qu.:0.00000 3rd Qu.:0.00000 3rd Qu.:0.00000 3rd Qu.:1.0000
## Max. :1.00000 Max. :1.00000 Max. :1.00000 Max. :1.0000
## highlights primary_category secondary_category tertiary_category
## Length:216776 Length:216776 Length:216776 Length:216776
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
## child_count
## Min. : 0.0000
## 1st Qu.: 0.0000
## Median : 0.0000
## Mean : 0.8277
## 3rd Qu.: 1.0000
## Max. :13.0000
# Print the dimensions of the data frame
print(dim(merged_clean))
## [1] 216776 35
# Export the data frame to a CSV file
write.csv(merged_clean, "merged_clean.csv", row.names = FALSE)
## Status Percentages
## 1 Recommended 76.51
## 2 Not_Recommended 23.49
The pie chart above illustrates the distribution of the “is_recommended” column. The pie chart shows that approximately 76.51%, which represents a significant majority recommended the products that they purchased in Sephora. On the other hand, 23.49% of the users did not recommend the products that they purchased in Sephora.
The bar chart above displays the “Top 10 Recommended Products” based on users’ responses. The product “Niacinamide 10% + Zinc 1% Oil Control Serum” has the highest number of recommendations among the top 10 products. This suggests that it is the most popular or effective product among the ones considered. It leads significantly with over 2381 recommendations, surpassing the second-place product by more than 649 recommendations.
“Superfood Antioxidant Cleanser” and “Salicylic Acid Acne Healing Dots” also have a high number of recommendations, indicating that they are well-liked by users. two of the products have gained more than 1700 recommendations each.
The bar chart highlights the “Top 10 Most Expensive Products” at Sephora. The top 10 products clustered between 300 and 450 USD which show a small price difference among these items. The Concentrate Serum” appears as the most expensive product with the price 425 USD. It lead the second most expensive product, “The Regenerating Serum” by only 10 USD. Interestingly, serums dominate the list with four entries, suggesting that they generally come with a higher price tag compared to other types of products.
The bar chart above shows the “Top 10 Cheapest Products” among all the products in Sephora. The top 10 cheapest products are all below 10 USD making them highly affordable for the public. “Clean Charcoal Nose Strip” appears to be the most affordable among all the products with a price at 3 USD. Masks are noted as the most common among the cheapest products with 7 out of the 10 entries being various types of masks.
The scatter plot illustrates the correlation between the prices of products and their favourity among customers. The plot reveals a lack of a strong relationship between a product’s price and its favourity. A significant number of products are clustered towards the lower spectrum of both price and popularity, indicating that irrespective of their popularity, most products are priced reasonably. Interestingly, there are several products having a high favourity are still priced low. This could imply that these products are widely favored but remain cost-effective.
## [1] "The submission data involves the following years:"
## [1] 2017 2018 2019 2020 2021 2022 2023
## # A tibble: 7 × 4
## year total_feedback_count total_neg_feedback_count total_pos_feedback_count
## <dbl> <int> <int> <int>
## 1 2017 76890 11925 64965
## 2 2018 317599 49581 268018
## 3 2019 277088 48241 228847
## 4 2020 671909 147861 524048
## 5 2021 426967 99320 327647
## 6 2022 257723 62188 195535
## 7 2023 25189 6710 18479
The line graph shows the trend of feedback counts over the years. These three variables (“Total Feedback Count”, “Total Negative Feedback Count” and “Total Positive Feedback Count”) show a high degree of correlation as they all follow a similar directional trend. Apart from that, the volume of feedback reaches its peak in 2020 with an impressive record of over 650,000. Besides that, the volume of positive feedback consistently surpasses negative feedback each year, which is a favorable indicator for Sephora’s performance and customer satisfaction.
# 5.1 Data Selection
# Import data
df <- read.csv("merged_clean.csv")
# Select the text, label and rating columns, rename & preview
df <- df[, c('review_text', 'is_recommended', 'rating')]
colnames(df) <- c('text', 'label', 'rating')
head(df)
## text
## 1 I wanted to try these to help with the texture of my skin, as well as visible pores. I am floored by the results. My pores shrank, and my skin is probably the smoothest it’s been in years! It even helped with dryness!
## 2 I’ve only been using this product for a few weeks and so far I’m seeing great results! The pats are super thick and the peach Bellini smell is yummy! You can see all the oil that is absorbed by the pads. Love it so far!
## 3  Ever since I’ve started using this, I’ve had no blackheads whatsoever. I couldn’t be happier and it is now is staple in my skin care routine. I highly recommend this product.
## 4 I am in love with these must have acne pads. Anytime I feel a zit or pimple come on, I wipe it down with one of these, and the next day it’s either gone, or tiny. I use these after micro blading to keep my face from breaking out, and it works wonders! One container lasted me 4 months. I will never go without these, they’re a must have!
## 5 I’m so thankful that I discovered this product. I developed sebaceous hyperplasia on my forehead. I’ve had in-office treatment ( electrocauterization ) done and they just come back. My dermatologist prescribed me Tret 0.05% and it did nothing. I recently purchased these pads and my forehead is clearing up. I think that the combo of Tret 0.05% and these pads is what finally did the trick. Thank freaking goodness!
## 6 Very potent but wow my skin is so soft and i have no more texture. Acne is actually gone. Recommend using one pad a day just so your skin doesn’t strip and use a good moisturizer !!
## label rating
## 1 1 5
## 2 1 5
## 3 1 5
## 4 1 5
## 5 1 5
## 6 1 5
# 5.2 Handling imbalanced data
# Summary of positive (1) and negative (0) reviews in count and percentage
table(df$label)
##
## 0 1
## 50910 165866
positive_percentage <- round(table(df$label)[2] / nrow(df) * 100, 2)
negative_percentage <- round(table(df$label)[1] / nrow(df) * 100, 2)
cat("Positive labels percentage:", positive_percentage, "%\n")
## Positive labels percentage: 76.51 %
cat("Negative labels percentage:", negative_percentage, "%\n")
## Negative labels percentage: 23.49 %
# Balancing the dataset
df_neg <- df[df$label == 0, ]
df_pos <- df[df$label == 1, ]
df_pos_sampled <- df_pos[sample(nrow(df_pos), nrow(df_neg)), ]
# Checking label counts in negative and positive subsets
table(df_neg$label)
##
## 0
## 50910
table(df_pos_sampled$label)
##
## 1
## 50910
# Concatenating and shuffling to get final usable dataset
df_balanced <- rbind(df_pos_sampled, df_neg)
df_balanced <- df_balanced[sample(nrow(df_balanced)), ]
head(df_balanced)
## text
## 208408 This is the Vitamin C serum we have all been searching for!! All the benefits of a Vitamin C serum, but it actually plays well under makeup! It absorbs fast without drying out or leaving a film on the skin. Not only that, it elevates my look and makes my makeup look and stay put even better. Doesn’t dry out my normal-dry skin, and no surprise breakouts. Just happy glowing skin! I love that I can turn a dull skin day into a fantastic one with just a half pump of this magical face elixir! A little goes a long way, so the serum is as cost effective as it is functional, which we all know is a rarity when it comes to quality skincare. The packaging is almost as amazing as the serum itself! Never seen anything like it before. The “cap“ isn’t a cap. The top twists (2 clicks) to reveal the pump. No more lost caps or accidental product waste (from accidental pumps, from losing the cap lol). Can’t say enough amazing things about this Vitamin C Brightening serum! Highly recommend!!
## 62292 Great dupe for Glossier’s super bounce! A little thicker but I love it.
## 141026 This is a super serum all right. Super terrible serum. It is beyond me why these skincare companies, with all the research on ingredients and what works for skin, still insist upon adding cheap, toxic, irritating ingredients to their products. Has anyone bothered to read the ingredient list? Second ingredient -Dimethicone. Alright. Not terrible. It’s a filler, but being second means that’s most all this serum is. Filler. THIRD -and this is what dropped my jaw. Witch hazel. Witch hazel is a plant derivative that when derived from bark, the ingredient is left with skin irritating, sensitizing ALCOHOL. I would NEVER put this anywhere near my face. Orange peel oil?! It is used as fragrance (bad for skin ) and it is very sensitizing on skin. The toted hyaluronic acid... it’s halfway down in the ingredient list, after all the fillers and muck so there isn’t much of it in it. I am appalled by this serum.
## 167536 This product has no identifiable smell so it’s easy to use on your skin. What I don’t like is how the consistency is just like water so it will drip quickly. When I pat it on my face it leave my hands super sticky. My face right now is sticky. I just put my chin to my shoulder and felt my skin do the stretchy thing when it’s stuck to something. The application is in the bottle pretty snugly so if you tip it over i doubt any will spill out. I imagine it’s that way because of its watery consistency. I definitely don’t see this lasting very long if you have to use twice a day and you have to use more because it’s so thin and spreads weird. I don’t think I’ll buy it again. But maybe it will make my skin the best it’s ever been. If it does, I’ll post another review of edit this one.
## 19749 I received this product for free in exchange for my honest review. I have been fighting adult acne for years. I’m 25 years old, and with fair skin that scars easily, I am even more self conscious about any breakouts. I’ve been using this cleanser for about 3 or 4 weeks now. This is the first cleanser that has made a visible difference in my skin! My breakouts were smoothed and diminished immediately, and I’ve even seen a change in my scarring. It’s just strong enough to give the skin a good chemical exfoliation, but not so strong that it dries you out. It leaves my skin fresh and smooth! Love! Will definitely be a repeat purchase until I banish acne and scarring for good!
## 188213 It leaves my skin feeling more moisturized and clean at the same time.
## label rating
## 208408 1 5
## 62292 1 5
## 141026 0 1
## 167536 0 2
## 19749 1 5
## 188213 1 5
# Print percentage of both labels present in the balanced dataset
positive_percentage_balanced <- round(table(df_balanced$label)[2] / nrow(df_balanced) * 100, 2)
negative_percentage_balanced <- round(table(df_balanced$label)[1] / nrow(df_balanced) * 100, 2)
cat("Positive labels percentage:", positive_percentage_balanced, "%\n")
## Positive labels percentage: 50 %
cat("Negative labels percentage:", negative_percentage_balanced, "%\n")
## Negative labels percentage: 50 %
# 5.3 Text Pre-processing
preprocess_text <- function(text) {
# Create a corpus
corpus <- Corpus(VectorSource(text))
# Convert text to lowercase
corpus <- tm_map(corpus, content_transformer(tolower))
# Remove punctuation
corpus <- tm_map(corpus, removePunctuation)
# Remove numbers
corpus <- tm_map(corpus, removeNumbers)
# Remove stop words
corpus <- tm_map(corpus, removeWords, stopwords("english"))
# Strip whitespace
corpus <- tm_map(corpus, stripWhitespace)
# Remove special characters (non-alphabetic)
corpus <- tm_map(corpus, content_transformer(function(x) gsub("[^[:alpha:][:space:]]*", "", x)))
# Convert corpus to a character vector
cleaned_text <- sapply(corpus, as.character)
return(cleaned_text)
}
# Preprocess text data & generate a new column
df_balanced$cleaned_text <- preprocess_text(df_balanced$text)
# View text before & after preprocessing
cat('Before preprocessing \n', df_balanced$text[2], '\n')
## Before preprocessing
## Great dupe for Glossier’s super bounce! A little thicker but I love it.
cat('After preprocessing \n', df_balanced$cleaned_text[2], '\n')
## After preprocessing
## great dupe glossiers super bounce little thicker love
# 5.4 Split the data into training and testing sets (80-20)
set.seed(123) # Set seed for reproducibility
train_indices <- sample(1:nrow(df_balanced), 0.8 * nrow(df_balanced))
train_data <- df_balanced[train_indices, ]
test_data <- df_balanced[-train_indices, ]
# 5.5 Vectorize text data using TF-IDF
# Create a corpus for the balanced dataset using the cleaned text
corpus <- corpus(df_balanced, text_field = "cleaned_text")
# Tokenize the corpus
tokens <- tokens(corpus, what = "word", remove_punct = TRUE, remove_symbols = TRUE, remove_numbers = TRUE, remove_url = TRUE)
# Create the Document-Feature Matrix (DFM) from tokens and apply TF-IDF weighting
dfm <- dfm(tokens) %>%
dfm_tfidf()
# Split the DFM into training and testing sets based on indices
train_dfm <- dfm[train_indices, ]
test_dfm <- dfm[-train_indices, ]
# Ensure the labels are factors for classification
train_labels <- as.factor(df_balanced$label[train_indices])
test_labels <- as.factor(df_balanced$label[-train_indices])
# 5.6 Train and test using the Naive Bayes Model
# Train Naive Bayes model using the quanteda.textmodels package
naive_bayes_model <- textmodel_nb(train_dfm, train_labels)
# Predict on the test set using the quanteda.textmodels package
predictions <- predict(naive_bayes_model, newdata = test_dfm)
# 5.7 Evaluation of the Naive Bayes model
# Evaluate the model using confusion matrix
confusion_matrix <- table(Predicted = predictions, Actual = test_labels)
# Print confusion matrix
print(confusion_matrix)
## Actual
## Predicted 0 1
## 0 8792 1409
## 1 1370 8793
# Calculate accuracy, precision, recall, and F1 score
accuracy <- sum(diag(confusion_matrix)) / sum(confusion_matrix)
precision <- confusion_matrix[2, 2] / sum(confusion_matrix[2, ])
recall <- confusion_matrix[2, 2] / sum(confusion_matrix[, 2])
f1_score <- 2 * precision * recall / (precision + recall)
cat("Accuracy:", round(accuracy, 2), "\n")
## Accuracy: 0.86
cat("Precision:", round(precision, 2), "\n")
## Precision: 0.87
cat("Recall:", round(recall, 2), "\n")
## Recall: 0.86
cat("F1 Score:", round(f1_score, 2), "\n")
## F1 Score: 0.86
# Import Raw Data
product_try <- read.csv("merged_clean.csv")
# Filter the data based on specific conditions
top1_cleanser_notrecommended <- product_try %>%
filter(
product_name == 'Superfood Antioxidant Cleanser',
is_recommended == 0
)
top1_cleanser_recommended <- product_try %>%
filter(
product_name == 'Superfood Antioxidant Cleanser',
is_recommended == 1
)
bottom1_cleanser_recommended <- product_try %>%
filter(
product_name == 'Melting Cleansing Balm Cleanse + Balance',
is_recommended == 1
)
bottom1_cleanser_notrecommended <- product_try %>%
filter(
product_name == 'Melting Cleansing Balm Cleanse + Balance',
is_recommended == 0
)
perform_lda <- function(data, product_name, is_recommended, num_topics = 5) {
# Ensure the review_text column is of character type
data$review_text <- as.character(data$review_text)
# Check the filtered data
print(paste("Number of reviews for", product_name, "(Recommended:", is_recommended, "):", nrow(data)))
if (nrow(data) > 0) {
print(head(data$review_text))
} else {
stop("No reviews found for the specified product and conditions.")
}
# LDA START
# Create a corpus
corpus <- Corpus(VectorSource(data$review_text))
# Check if corpus is empty
if (length(corpus) == 0) {
stop("Corpus is empty. Please check the input data and transformations.")
}
# Convert text to lowercase
corpus <- tm_map(corpus, content_transformer(tolower))
# Remove punctuation
corpus <- tm_map(corpus, removePunctuation)
# Remove numbers
corpus <- tm_map(corpus, removeNumbers)
# Remove stop words
corpus <- tm_map(corpus, removeWords, stopwords("english"))
# Strip whitespace
corpus <- tm_map(corpus, stripWhitespace)
# Remove special characters (non-alphabetic)
corpus <- tm_map(corpus, content_transformer(function(x) gsub("[^[:alpha:][:space:]]*", "", x)))
# Inspect the corpus after preprocessing
inspect_corpus <- function(corpus, n = 5) {
if (length(corpus) < n) {
n <- length(corpus)
}
for (i in 1:n) {
cat(paste("Document", i, ":", "\n"))
cat(content(corpus[[i]]), "\n\n")
}
}
inspect_corpus(corpus)
# Create a Document-Term Matrix
dtm <- DocumentTermMatrix(corpus)
print(paste("Number of documents in DTM:", nrow(dtm)))
print(paste("Number of terms in DTM:", ncol(dtm)))
# Check if the DTM is empty
if (nrow(dtm) == 0 || ncol(dtm) == 0) {
stop("Document-Term Matrix is empty. Please check the input data and transformations.")
}
# Build LDA model
lda_model <- LDA(dtm, k = num_topics, control = list(seed = 1234))
# Extract terms from the model
terms <- terms(lda_model, 10)
cat("LDA Terms for", product_name, "(Recommended:", is_recommended, "):\n")
print(terms)
}
# Apply the function to each dataset
perform_lda(top1_cleanser_notrecommended, "Superfood Antioxidant Cleanser", 0)
## [1] "Number of reviews for Superfood Antioxidant Cleanser (Recommended: 0 ): 603"
## [1] "somehow, the price of this isn’t the worst part about it- it’s the fact that it straight up doesn’t work for me, even though you can’t find a list for combination skin without seeing this wash. i don’t get it. i’ve tried it twice now (not two times, two bottles worth) both alone in the morning, alone at night, with a double cleanse + any other way you could use a face wash and most of the time i found residue/makeup on my cotton pad and saw little to no results. the smell is amazing. if it were made into a candle or air freshener, i’d happily buy it."
## [2] "My skin hated this, it left my face red and tight. Gifted to my cousin, it worked much better for her. Really wanted to like this product but I am clearly sensitive to an ingredient or two."
## [3] "I wanted to love this product due to the glowing reviews. I used it for two weeks and my whole forehead broke out terribly. Within 3 days of not using my skin started immediately getting better."
## [4] "I really wanted to love it, but it dried my skin and I got a few pimple right after using it. Happy I only got the mini won’t be getting again."
## [5] "I don’t write many reviews but I had to for this. I hate this. I’ve tried to like it but it keeps making me break out so bad. :(("
## [6] "So the cleanser definitely made my face feel cool and clean after each use, but I would break out like crazy every time so after a few uses I was forced to stop using. It seems great, smells clean, but definitely not for everyone."
## Document 1 :
## somehow price isnt worst part s fact straight doesnt work even though cant find list combination skin without seeing wash dont get ve tried twice now two times two bottles worth alone morning alone night double cleanse way use face wash time found residuemakeup cotton pad saw little results smell amazing made candle air freshener d happily buy
##
## Document 2 :
## skin hated left face red tight gifted cousin worked much better really wanted like product clearly sensitive ingredient two
##
## Document 3 :
## wanted love product due glowing reviews used two weeks whole forehead broke terribly within days using skin started immediately getting better
##
## Document 4 :
## really wanted love dried skin got pimple right using happy got mini wont getting
##
## Document 5 :
## dont write many reviews hate ve tried like keeps making break bad
##
## [1] "Number of documents in DTM: 603"
## [1] "Number of terms in DTM: 2461"
## LDA Terms for Superfood Antioxidant Cleanser (Recommended: 0 ):
## Topic 1 Topic 2 Topic 3 Topic 4 Topic 5
## [1,] "skin" "skin" "cleanser" "face" "skin"
## [2,] "cleanser" "product" "like" "using" "dry"
## [3,] "using" "like" "feel" "product" "cleanser"
## [4,] "use" "cleanser" "didnt" "skin" "product"
## [5,] "product" "face" "just" "just" "really"
## [6,] "bottle" "using" "good" "cleanser" "face"
## [7,] "also" "use" "love" "like" "oily"
## [8,] "didnt" "didnt" "dont" "really" "price"
## [9,] "face" "just" "dry" "dont" "like"
## [10,] "acne" "feel" "skin" "bottle" "didnt"
perform_lda(top1_cleanser_recommended, "Superfood Antioxidant Cleanser", 1)
## [1] "Number of reviews for Superfood Antioxidant Cleanser (Recommended: 1 ): 1732"
## [1] "I have used this on and off for a few years now. It didn’t do too much for my oily-ness but I did notice my pores appear larger when I’m not using this. No wow factor tho"
## [2] "Love this product, leaves my face feeling fresh and clean."
## [3] "I’ve used this for a few years now after searching for a product that works on my temperamental combination skin. I find this cleanses well without feeling like it’s stripping my skin, and when followed with moisturizing products I find it’s the perfect first step in my am/pm skincare. Also love that you can buy the refillable size!"
## [4] "I LOVE THIS FACE WASH. It smells so nice, subtle of course. It washes my skin well and doesnt leave it feeling dry and I have super dry skin with psoriasis. But dang why does it have to be so expensive!? I literally have to treat myself to get this face wash. It works great but for the price there should be chunks of gold in there or something."
## [5] "For the price it was a no for me. It gave the same results as a 12$ Walmart face wash. I will finish it because I paid for it but will not waste the money again."
## [6] "After using this for myself, i feel as thought this is probably overpriced. it’s a good cleanser but i noticed better results from a cleanser that was significantly cheaper. i do like the smell though."
## Document 1 :
## used years now didnt much oilyness notice pores appear larger m using wow factor tho
##
## Document 2 :
## love product leaves face feeling fresh clean
##
## Document 3 :
## ve used years now searching product works temperamental combination skin find cleanses well without feeling like s stripping skin followed moisturizing products find s perfect first step ampm skincare also love can buy refillable size
##
## Document 4 :
## love face wash smells nice subtle course washes skin well doesnt leave feeling dry super dry skin psoriasis dang expensive literally treat get face wash works great price chunks gold something
##
## Document 5 :
## price gave results walmart face wash will finish paid will waste money
##
## [1] "Number of documents in DTM: 1732"
## [1] "Number of terms in DTM: 4293"
## LDA Terms for Superfood Antioxidant Cleanser (Recommended: 1 ):
## Topic 1 Topic 2 Topic 3 Topic 4 Topic 5
## [1,] "skin" "skin" "product" "cleanser" "skin"
## [2,] "face" "use" "skin" "love" "cleanser"
## [3,] "dry" "face" "cleanser" "skin" "face"
## [4,] "love" "like" "like" "feel" "clean"
## [5,] "use" "using" "using" "clean" "love"
## [6,] "acne" "makeup" "dry" "acne" "like"
## [7,] "cleanser" "just" "use" "size" "smells"
## [8,] "clean" "also" "doesnt" "using" "great"
## [9,] "one" "cleanser" "really" "will" "really"
## [10,] "great" "product" "leaves" "feeling" "just"
perform_lda(bottom1_cleanser_recommended, "Melting Cleansing Balm Cleanse + Balance", 1)
## [1] "Number of reviews for Melting Cleansing Balm Cleanse + Balance (Recommended: 1 ): 145"
## [1] "i really like it! before using it, I was doing a single wash but the double wash has made a difference! i don’t have a ton of experience with cleansing balms and perhaps I like this one because it’s the first one I tried. it feels very nice on the skin and has a subtle scent that is nice as well. I won’t give it 5 stars cause I’m not raving over it but I still think it’s a great option and I’m happy with the price and quality overall!"
## [2] "Generally a really good product. Helps to effectively remove makeup. Would not recommend for oily skin. But overall decent. Needed to apply more than indicated on the package."
## [3] "The best cleansing balm ever, doesn’t sting eyes and is super hydrating and not stripping. 100% recommend :)"
## [4] "Ever since I got this, this is almost the only thing I use to take off my makeup. Formula is quite nice and convenient packaging. Really comparable to the more high end cleansing balms. It’s most likely discontinued now which is shocking and sad!"
## [5] "Okay so here is the rundown on this product: If you’re looking for a luxurious cleansing balm that breaks down makeup really well and leaves your skin feeling refreshed and clean after one cleanse, this isn’t the product for you. HOWEVER, for the price along with the amount of product you get, it is a decent product. I use it to remove my makeup after a night out and always follow with a second cleanse. My skin feels very clean and makeup free after! Does it have a nice smell and feel luxurious on my face? No. But it’s honestly a great bang for your buck and I’ll probably buy it again because it hasn’t broken me out! Overall rating: 4/5"
## [6] "The first time I bought it I would have given it 5 stars.Dig in, apply to dry face, rinse off. It was amazing at removing all my makeup without a mess.I ordered it again after having to wait for it to come back in stock. I believe the product has been watered down, as it is now quite sloppy to apply/use. The entire consistency is just... runnier than the last one was. A lot runnier.I’m not buying it again."
## Document 1 :
## really like using single wash double wash made difference dont ton experience cleansing balms perhaps like one s first one tried feels nice skin subtle scent nice well wont give stars cause m raving still think s great option m happy price quality overall
##
## Document 2 :
## generally really good product helps effectively remove makeup recommend oily skin overall decent needed apply indicated package
##
## Document 3 :
## best cleansing balm ever doesnt sting eyes super hydrating stripping recommend
##
## Document 4 :
## ever since got almost thing use take makeup formula quite nice convenient packaging really comparable high end cleansing balms s likely discontinued now shocking sad
##
## Document 5 :
## okay rundown product re looking luxurious cleansing balm breaks makeup really well leaves skin feeling refreshed clean one cleanse isnt product however price along amount product get decent product use remove makeup night always follow second cleanse skin feels clean makeup free nice smell feel luxurious face s honestly great bang buck ll probably buy hasnt broken overall rating
##
## [1] "Number of documents in DTM: 145"
## [1] "Number of terms in DTM: 1058"
## LDA Terms for Melting Cleansing Balm Cleanse + Balance (Recommended: 1 ):
## Topic 1 Topic 2 Topic 3 Topic 4 Topic 5
## [1,] "cleansing" "makeup" "makeup" "skin" "balm"
## [2,] "balm" "face" "skin" "makeup" "makeup"
## [3,] "makeup" "farmacy" "use" "price" "cleansing"
## [4,] "skin" "get" "like" "balm" "skin"
## [5,] "like" "balm" "product" "oil" "clean"
## [6,] "one" "one" "face" "cleansing" "doesnt"
## [7,] "product" "product" "love" "doesnt" "love"
## [8,] "love" "just" "really" "well" "little"
## [9,] "balms" "love" "cleansing" "like" "way"
## [10,] "sephora" "cleansing" "cleanse" "leaves" "definitely"
perform_lda(bottom1_cleanser_notrecommended, "Melting Cleansing Balm Cleanse + Balance", 0)
## [1] "Number of reviews for Melting Cleansing Balm Cleanse + Balance (Recommended: 0 ): 80"
## [1] "This wasn’t my favorite cleansing balm. It does not melt and turn into a creamy balm/oil like others. It says thick and lotiony and didn’t do a good job at breaking my makeup down. Also left a film on my skin. Pass on this one!"
## [2] "This doesn’t remove regular mascara as seamlessly as other cleansing balms and oils I’ve tried. Crumbly bits stay on your eyes and it takes a lot of rubbing which can cause lash fallout or skin irritation. It washes off okay, but you definitely need to double cleanse to feel “clean“. I think if you can spare a few extra dollars, the physicians formula cleansing balm is much much better. This is also personal preference but I really wish this wasn’t in a tub – a squeeze tube is so much more hygienic and easy to portion control"
## [3] "I tried this because I liked their previous cleansing oil and cleansing oil in balm, both which are no longer for sale.I didn’t really like the texture when applying, but I didn’t think much of it because it’s meant to be removed.After washing with warm water, it still felt filmy even though it says specifically it won’t, but I still didn’t think much of it because I was using it in a double cleanse, as I will wash again with a water-based cleanser.After finishing the double “cleanse“, it did not feel clean at all. I had to double cleanse again, after using the water-based cleanser again, before my face felt close to normal. So 5x cleansing, which is ridiculous. This is the only product I’ve tried from this store that I wanted to return, but of course now I can’t because of the shortened return window. I did try adding jojoba oil when applying this and it seems to wash away much easier now (only double cleansing once). So at least not a total waste of money, I guess."
## [4] "Vaseline is better than this. Really disappointed in this product which will now just sit in my bathroom to rot."
## [5] "Let me start this review by saying I don’t wear makeup so i can’t attest to its makeup removing ability. i use this as the first cleanse in my double cleanse to remove sunscreen but it’s just ok. it has a very strong scent that i don’t like (and i don’t even mind fragrance in products) and doesn’t really “melt” as the name would suggest. It does leave a weird film on your face after rinsing. Makes your eyes cloudy after rubbing it on your face. Even for the price, I think you’d be better off just buying a more expensive one that’s better. It manages to get my sunscreen off decently well but i’d imagine you it would be tough to get makeup off with it. Overall, i’d say find another cleansing balm/oil if you are looking for one."
## [6] "Bought this as a cheaper substitute for Farmacy Green Clean but wouldn’t repurchase. The cleansing power of it works well but the consistency is much different than green clean. Feels more like a thick gel than balm and takes a while warm up in your fingers before working into the skin, which makes it feel like I’m pulling my skin by using this. Not worth returning. As long as I have the budget for Farmacy I’ll stick with that."
## Document 1 :
## wasnt favorite cleansing balm melt turn creamy balmoil like others says thick lotiony didnt good job breaking makeup also left film skin pass one
##
## Document 2 :
## doesnt remove regular mascara seamlessly cleansing balms oils ve tried crumbly bits stay eyes takes lot rubbing can cause lash fallout skin irritation washes okay definitely need double cleanse feel clean think can spare extra dollars physicians formula cleansing balm much much better also personal preference really wish wasnt tub squeeze tube much hygienic easy portion control
##
## Document 3 :
## tried liked previous cleansing oil cleansing oil balm longer salei didnt really like texture applying didnt think much s meant removedafter washing warm water still felt filmy even though says specifically wont still didnt think much using double cleanse will wash waterbased cleanserafter finishing double cleanse feel clean double cleanse using waterbased cleanser face felt close normal x cleansing ridiculous product ve tried store wanted return course now cant shortened return window try adding jojoba oil applying seems wash away much easier now double cleansing least total waste money guess
##
## Document 4 :
## vaseline better really disappointed product will now just sit bathroom rot
##
## Document 5 :
## let start review saying dont wear makeup cant attest makeup removing ability use first cleanse double cleanse remove sunscreen s just ok strong scent dont like dont even mind fragrance products doesnt really melt name suggest leave weird film face rinsing makes eyes cloudy rubbing face even price think d better just buying expensive one s better manages get sunscreen decently well d imagine tough get makeup overall d say find another cleansing balmoil looking one
##
## [1] "Number of documents in DTM: 80"
## [1] "Number of terms in DTM: 729"
## LDA Terms for Melting Cleansing Balm Cleanse + Balance (Recommended: 0 ):
## Topic 1 Topic 2 Topic 3 Topic 4 Topic 5
## [1,] "like" "skin" "cleansing" "skin" "makeup"
## [2,] "cleansing" "balm" "really" "like" "balm"
## [3,] "balm" "using" "like" "face" "like"
## [4,] "product" "also" "makeup" "makeup" "well"
## [5,] "balms" "product" "doesnt" "doesnt" "doesnt"
## [6,] "skin" "mascara" "face" "feels" "cleansing"
## [7,] "eyes" "left" "definitely" "balm" "get"
## [8,] "mascara" "well" "product" "texture" "dont"
## [9,] "face" "try" "skin" "clean" "thick"
## [10,] "work" "cleansing" "cleanser" "thick" "eyes"
In the LDA model of text analysis, this study selected the product “Superfood Antioxidant Cleanser” with the most reviews in the Sephora skincare category and the product “Melting Cleansing Balm Cleanse + Balance” with fewer reviews as the analysis objects. By comparing user reviews of these two products, this study aimed to explore the factors that influence the number of user reviews and analyze keywords that frequently appear in positive reviews.
First and foremost, for “Superfood Antioxidant Cleanser”, the top 50 keywords in its positive reviews include “skin”, “face”, “love”, “like”, “clean”, etc., with a total of 1,732 positive reviews. These positive reviews mainly express users’ satisfaction with the cleansing effect of the cleanser and its gentleness on the skin. As for negative reviews, there were 603 in total. These reviews mainly focused on skin discomfort that the product may cause, such as dryness, allergies, or failure to achieve the expected cleaning effect.
Next, for “Melting Cleansing Balm Cleanse + Balance”, the top 50 keywords in the positive reviews include “cleansing”, “makeup”, “balm”, “price”, “skin”, etc., with a total of 145 positive reviews. The positive reviews from users mainly emphasized the product’s effectiveness in removing makeup and cleaning, while there were 80 negative reviews, which mainly pointed out some shortcomings of the product, such as incomplete makeup removal, poor use feeling or residual feeling.
The study analyzed product reviews and information from multiple datasets, revealing a high level of customer satisfaction. The top 1 recommended and most expensive products were identified, with “Niacinamide 10% + Zinc 1% Oil Control Serum” being the most recommended. The Naive Bayes model achieved an accuracy of 0.89, with precision and recall scores of 0.88 and 0.90, respectively. Topic modeling was used to uncover prevalent themes in both recommended and not recommended reviews. Future recommendations include exploring advanced machine learning algorithms, a comprehensive approach to feature engineering, and expanding the dataset with more diverse reviews. This analysis provided deeper insights into customer sentiments and product-specific issues.