library(tidyverse)
library(tidytext)
library(widyr)
library(igraph)
library(ggraph)
library(Matrix)
This analysis uses the YouTube comments scraped in Lab 4, discussing athleisure brands (Lululemon, Gymshark, Fabletics). Each comment is treated as its own “document,” the same way each line was its own document in the tutorial’s SpaceX example. There are 749 comments found.
athleisure_raw <- read_csv("athleisure_comments.csv")
athleisure_text <- athleisure_raw %>%
mutate(id = row_number()) %>%
select(id, text = textOriginal)
athleisure_text
## # A tibble: 749 × 2
## id text
## <int> <chr>
## 1 1 "There's two persons in this world. People who find a shirt that fits …
## 2 2 "Now everyone looks like they are hungover walking out to grab a break…
## 3 3 "I was already feeling sick of popular gym clothes and this gives me e…
## 4 4 "If food doesn't kill you. Your clothing will."
## 5 5 "200 pound \"whales\" in yoga pants scare me."
## 6 6 "😂"
## 7 7 "Obesity."
## 8 8 "Underarmor is better then gymshark"
## 9 9 "I'm not gonna lie: I fell for Gymshark. I haven't supported them the …
## 10 10 "Man, I’m trying to build a public playlist with all of your videos so…
## # ℹ 739 more rows
Real-world text needs cleanup before analysis — punctuation, emojis, and numbers need to be handled, and generic filler words need to be removed as stop words.
Task: Add 1–2 of your own stop words below after reviewing the word counts — look for words that are frequent only because they show up in most comments, not because they carry meaning.
custom_stop_words <- bind_rows(
stop_words,
tibble(word = c("im", "ive", "dont", "didnt", "isnt", "lol", "ve", "don", "isn", "doesn", "video"), lexicon = "custom")
)
athleisure_words <- athleisure_text %>%
mutate(text = str_replace_all(text, "[^[:alnum:][:space:]$]", " ")) %>%
unnest_tokens(word, text, token = "words") %>%
filter(!str_detect(word, "^[0-9]+$")) %>%
anti_join(custom_stop_words, by = "word")
athleisure_words %>% count(word, sort = TRUE) %>% head(15)
## # A tibble: 15 × 2
## word n
## <chr> <int>
## 1 wear 261
## 2 gym 203
## 3 clothes 172
## 4 people 133
## 5 clothing 111
## 6 cotton 95
## 7 wearing 81
## 8 shorts 72
## 9 athleisure 64
## 10 buy 62
## 11 pants 62
## 12 comfortable 61
## 13 athletic 56
## 14 gymshark 52
## 15 stuff 52
athleisure_pairs <- athleisure_words %>%
pairwise_count(word, id, sort = TRUE, upper = FALSE)
athleisure_pairs %>% head(15)
## # A tibble: 15 × 3
## item1 item2 n
## <chr> <chr> <dbl>
## 1 gym wear 61
## 2 clothes wear 46
## 3 gym clothes 45
## 4 clothing wear 40
## 5 wear cotton 33
## 6 people wear 32
## 7 wear athletic 31
## 8 people gym 30
## 9 pants wear 30
## 10 wear athleisure 30
## 11 wear shorts 28
## 12 wear comfortable 28
## 13 wear wearing 26
## 14 wear workout 24
## 15 people clothes 23
top_words <- athleisure_words %>%
count(word, sort = TRUE) %>%
slice_max(n, n = 15) %>%
pull(word)
athleisure_matrix <- athleisure_pairs %>%
filter(item1 %in% top_words, item2 %in% top_words) %>%
bind_rows(athleisure_pairs %>% rename(item1 = item2, item2 = item1) %>%
filter(item1 %in% top_words, item2 %in% top_words)) %>%
cast_sparse(item1, item2, n) %>%
as.matrix()
athleisure_matrix
## wear clothes cotton athletic gym athleisure shorts comfortable
## gym 61 45 20 17 0 17 20 20
## clothes 46 0 21 13 45 16 12 11
## clothing 40 19 15 18 21 16 6 9
## wear 0 46 33 31 61 30 28 28
## people 32 23 7 11 30 19 16 12
## pants 30 8 5 5 15 7 4 9
## cotton 33 21 0 12 20 7 13 12
## athleisure 30 16 7 9 17 0 7 13
## shorts 28 12 13 5 20 7 0 11
## gymshark 10 9 1 1 14 4 4 3
## wearing 26 22 10 10 19 9 10 10
## buy 22 19 12 4 14 9 9 9
## comfortable 28 11 12 6 20 13 11 0
## athletic 31 13 12 0 17 9 5 6
## stuff 18 9 5 5 9 5 8 7
## wearing clothing buy stuff pants gymshark people
## gym 19 21 14 9 15 14 30
## clothes 22 19 19 9 8 9 23
## clothing 20 0 9 14 11 4 23
## wear 26 40 22 18 30 10 32
## people 22 23 11 13 9 5 0
## pants 12 11 6 1 0 2 9
## cotton 10 15 12 5 5 1 7
## athleisure 9 16 9 5 7 4 19
## shorts 10 6 9 8 4 4 16
## gymshark 6 4 7 3 2 0 5
## wearing 0 20 8 5 12 6 22
## buy 8 9 0 7 6 7 11
## comfortable 10 9 9 7 9 3 12
## athletic 10 18 4 5 5 1 11
## stuff 5 14 7 0 1 3 13
Task: Try a different value of n in the
filter below (Question 2 asks about this).
set.seed(580)
athleisure_pairs %>%
filter(n > 5) %>%
graph_from_data_frame() %>%
ggraph(layout = "fr") +
geom_edge_link(aes(edge_alpha = n, edge_width = n, color = n)) +
scale_edge_color_gradient(low = "yellow", high = "darkorange") +
geom_node_point(size = 3, color = "darkgreen") +
geom_node_text(aes(label = name), repel = TRUE, size =4) +
theme_void() +
labs(
title = "Word Co-occurrence Network: Athleisure YouTube Comments",
subtitle = "Edge thickness and color = number of comments containing both words"
)
Question 1: Which additional words did you add to the stopword list manually? Why does it make sense to remove those words as stopwords? Does the final network graph change after removing these additional stopwords? If so, briefly explain how and why.
After I analyzed the word co-occurence network, I added “ve”, “don”, “isn”, “doesn” to the stop words. These words do not provide meaningful insight since these are incomplete. I also added “video” since we are extracting comments from YouTube.
Question 2: Change n in
filter(n > 1) in Step 5 to a different value. What is
your final value of n? Will it give you a better result?
I had to change it to n > 5 because there were too many pairs and it became difficult to analyze. Making this adjustment provided me a cleaner visual. I tried to adjust the threshold to 8 and 10 but I feel like it was removing important insights. For example, I see another brand, nike, being mentioned since it is not one of three brands I extracted. If I adjusted it to 8, it disappears. Since another brand was mentioned, it is good to take note since most likely, the consumer is comparing their products to this brand.
Question 3: On the professor’s BlueSky post, there were two co-occurrence network graphs. Which one do you like better? Why?
I think each graph could provide its own importance so its a bit difficult to decide. The left one with a lower threshold can show a wide variety of patterns and word associations that consumers say, possibly towards the brand and not just a specific product. The one on the right doesn’t really give me much insight other than maybe the customer’s experience with the coffee product.
Question 4: Which word pairs surprised you? Did any positive/enthusiastic words cluster more than critical or skeptical ones?
I did not see any word pairs that were surprising or unsual when it comes to athleisure.
Question 5: Should we treat stock tickers like
$SPCX and SpaceX as the same token instead of
separate ones?
I think $SPCX and SpaceX should be treated
as the same token if it has the same meaning. The only reason I would
possible split them is maybe when it comes to investment where
$SPCX is a digital token, whereas SPCX is the real NASDAQ
stock.
Question 6: What’s the risk of drawing conclusions from a co-occurrence network built on only a small sample of comments? What is a good sample size? How would you communicate that limitation in a business report?
A small sample may provide incorrect insights, since there is not enough words for co-occurences. A lot of times when it comes to YouTube, people make random comments that may not necessarily be related to the brand/product. I think a good sample size would really depend on the company in regards to what questions they want answered. I would communicate this limitation in a business report by giving a disclaimer that this sample does not represent the consumer market as a whole and emphasize that this provides a small insight for further research
Question 7: Is there a way to analyze comments or reviews on topics that interest you in relation to a significant event? If so, how would you identify the event? Why is that event significant, and how might it influence the comments or reviews you analyze?
I think comparing comments before and after a significant event can provide insight about current consumer sentiments. For example, if a company experiences some sort of controversy, it can negatively affect the company’s image where the consumer’s focus is now on that negative event instead of the company’s products/services.