Keyword: starbucks.
This report converts Starbucks_Bluesky_Analysis.xlsx
into executable R Markdown. The 10 supplied summaries, engagement
counts, and sentiment labels are embedded below, so the sample analysis
runs without Excel files or internet access. Install
rmarkdown and knitr once, open this file in
RStudio, and click Knit.
The sample has no documented collection dates, post timestamps, reply counts, or random sampling procedure. It is not a complete Day 1 or Day 3 collection. The summaries and technology-related claims have not been independently verified. Results below describe only the supplied records.
To add the workbook’s completed collection data later, place the
Excel file beside this Rmd, install readxl, and set
workbook in the YAML above to
"Starbucks_Bluesky_Analysis.xlsx". The comparison reads the
Collections tab. This script analyzes data; it does not
collect new Bluesky posts.
Original sentiment labels are preserved. Review whether each label expresses sentiment toward Starbucks. In particular, the positive label for the post comparing someone’s appearance to the Starbucks logo needs full-text context.
posts <- data.frame(
handle = c(
"@pattymo.com",
"@clarajeffery.bsky.social",
"@otsumamiboy.blacksky.app",
"@hypervisible.blacksky.app",
"@dixonij.bsky.social",
"@carlquintanilla.bsky.social",
"@sbworkersunited.org",
"@jamiedcville.bsky.social",
"@hhaelines.bsky.social",
"@paleofuture.bsky.social"
),
text = c(
"Me looking at the shitty little brownie behind the Starbucks display case",
"never enters a Starbucks again (AI recognition reaction)",
"Separate from the nefarious Price Profiling purposes of Starbucks...",
"Invasive surveillance for modest convenience...",
"The best time to stop going to Starbucks was yesterday.",
"STARBUCKS CEO: We are also exploring experiences that recognize...",
"Listen to Angie Nixon: Stand with workers and BOYCOTT STARBUCKS!",
"I'm gonna boycott Starbucks even more now.",
"she kind of looks like the starbucks logo",
"Remember the creepy shopping scene from Minority Report?"
),
likes = c(
514,
315,
318,
240,
224,
201,
186,
117,
100,
79
),
reposts = c(
9,
65,
38,
93,
59,
98,
47,
1,
10,
29
),
sentiment = c(
"Neutral",
"Negative",
"Negative",
"Negative",
"Negative",
"Neutral",
"Negative",
"Negative",
"Positive",
"Negative"
),
stringsAsFactors = FALSE
)
# Engagement follows the assignment's definition; replies are excluded.
posts$engagement <- posts$likes + posts$reposts
stopifnot(nrow(posts) == 10L,
all(posts$sentiment %in% c("Negative", "Neutral", "Positive")))
knitr::kable(posts, caption = "Ten supplied Starbucks records")
| handle | text | likes | reposts | sentiment | engagement |
|---|---|---|---|---|---|
| @pattymo.com | Me looking at the shitty little brownie behind the Starbucks display case | 514 | 9 | Neutral | 523 |
| @clarajeffery.bsky.social | never enters a Starbucks again (AI recognition reaction) | 315 | 65 | Negative | 380 |
| @otsumamiboy.blacksky.app | Separate from the nefarious Price Profiling purposes of Starbucks… | 318 | 38 | Negative | 356 |
| @hypervisible.blacksky.app | Invasive surveillance for modest convenience… | 240 | 93 | Negative | 333 |
| @dixonij.bsky.social | The best time to stop going to Starbucks was yesterday. | 224 | 59 | Negative | 283 |
| @carlquintanilla.bsky.social | STARBUCKS CEO: We are also exploring experiences that recognize… | 201 | 98 | Neutral | 299 |
| @sbworkersunited.org | Listen to Angie Nixon: Stand with workers and BOYCOTT STARBUCKS! | 186 | 47 | Negative | 233 |
| @jamiedcville.bsky.social | I’m gonna boycott Starbucks even more now. | 117 | 1 | Negative | 118 |
| @hhaelines.bsky.social | she kind of looks like the starbucks logo | 100 | 10 | Positive | 110 |
| @paleofuture.bsky.social | Remember the creepy shopping scene from Minority Report? | 79 | 29 | Negative | 108 |
# Original row order breaks any engagement ties consistently.
ranked_posts <- posts[order(-posts$engagement, seq_len(nrow(posts))), ]
top_five <- head(ranked_posts, 5)
top_five <- data.frame(
rank = seq_len(nrow(top_five)),
top_five[, c("handle", "likes", "reposts", "engagement", "sentiment")],
row.names = NULL
)
knitr::kable(top_five, caption = "Top five among the supplied records")
| rank | handle | likes | reposts | engagement | sentiment |
|---|---|---|---|---|---|
| 1 | @pattymo.com | 514 | 9 | 523 | Neutral |
| 2 | @clarajeffery.bsky.social | 315 | 65 | 380 | Negative |
| 3 | @otsumamiboy.blacksky.app | 318 | 38 | 356 | Negative |
| 4 | @hypervisible.blacksky.app | 240 | 93 | 333 | Negative |
| 5 | @carlquintanilla.bsky.social | 201 | 98 | 299 | Neutral |
The highest-engagement supplied post is humorous. Several other leading summaries discuss privacy and recognition technology. This small sample does not establish that humor generally spreads faster than news or that engagement implies agreement with a post.
sentiment_levels <- c("Negative", "Neutral", "Positive")
sentiment_counts <- table(factor(posts$sentiment, levels = sentiment_levels))
sentiment_summary <- data.frame(
sentiment = sentiment_levels,
count = as.integer(sentiment_counts)
)
sentiment_summary$share <- sentiment_summary$count / sum(sentiment_summary$count)
sentiment_display <- sentiment_summary
sentiment_display$share <- sprintf("%.0f%%", 100 * sentiment_display$share)
knitr::kable(sentiment_display,
col.names = c("Sentiment", "Posts", "Percentage"),
caption = "Supplied manual labels; random selection is undocumented")
| Sentiment | Posts | Percentage |
|---|---|---|
| Negative | 7 | 70% |
| Neutral | 2 | 20% |
| Positive | 1 | 10% |
Sentiment labels in the supplied 10-post sample
Under the supplied labels, 7 of 10 records are negative (70%). This is a description of the supplied sample, not an estimate of all Starbucks customers’ opinions.
The supplied summaries suggest three topics for review against the full posts:
author_counts <- sort(table(posts$handle), decreasing = TRUE)
author_summary <- data.frame(handle = names(author_counts),
posts = as.integer(author_counts))
knitr::kable(author_summary, caption = "Author counts in the supplied sample")
| handle | posts |
|---|---|
| @carlquintanilla.bsky.social | 1 |
| @clarajeffery.bsky.social | 1 |
| @dixonij.bsky.social | 1 |
| @hhaelines.bsky.social | 1 |
| @hypervisible.blacksky.app | 1 |
| @jamiedcville.bsky.social | 1 |
| @otsumamiboy.blacksky.app | 1 |
| @paleofuture.bsky.social | 1 |
| @pattymo.com | 1 |
| @sbworkersunited.org | 1 |
Each handle appears once in the supplied sample. Account dominance in the full conversation cannot be assessed without the complete collections.
This section uses only actual rows entered in the workbook’s
Collections tab. Blank reserved rows are excluded. Missing
collections and unreviewed sentiment remain unavailable rather than
being treated as zero sentiment.
collections <- NULL
if (!nzchar(params$workbook)) {
cat("No collection workbook selected. Day 1 and Day 3 results are unavailable.")
} else {
if (!file.exists(params$workbook)) {
stop("Workbook not found. Place it beside this Rmd or provide its full path.")
}
if (!requireNamespace("readxl", quietly = TRUE)) {
stop("Install readxl first: install.packages('readxl')")
}
collections <- as.data.frame(readxl::read_excel(
params$workbook, sheet = "Collections", skip = 5,
na = c("", "NA"), .name_repair = "minimal"
))
required <- c("Collection", "Author handle", "Manual sentiment")
if (!all(required %in% names(collections))) {
stop("Collections must include: Collection, Author handle, Manual sentiment.")
}
keep <- !is.na(collections[["Author handle"]]) &
nzchar(trimws(collections[["Author handle"]]))
collections <- collections[keep, , drop = FALSE]
collections[["Collection"]] <- trimws(collections[["Collection"]])
collections[["Manual sentiment"]] <- tolower(trimws(collections[["Manual sentiment"]]))
unexpected_days <- setdiff(unique(collections[["Collection"]]), c("Day 1", "Day 3"))
if (length(unexpected_days)) stop("Use Collection labels Day 1 or Day 3.")
labels <- collections[["Manual sentiment"]]
if (any(!is.na(labels) & nzchar(labels) &
!labels %in% c("negative", "neutral", "positive"))) {
stop("Manual sentiment must be blank, Negative, Neutral, or Positive.")
}
}
## No collection workbook selected. Day 1 and Day 3 results are unavailable.
if (!is.null(collections)) {
summarize_day <- function(day) {
d <- collections[collections[["Collection"]] == day, , drop = FALSE]
tags <- d[["Manual sentiment"]]
labeled <- tags %in% c("negative", "neutral", "positive")
counts <- vapply(c("negative", "neutral", "positive"),
function(x) sum(tags == x, na.rm = TRUE), numeric(1))
shares <- if (sum(labeled) > 0) counts / sum(labeled) else rep(NA_real_, 3)
c(nrow(d), sum(labeled), shares)
}
first <- summarize_day("Day 1")
second <- summarize_day("Day 3")
delta <- if (first[1] > 0 && second[1] > 0) second - first else rep(NA_real_, 5)
fmt <- function(x, change = FALSE) {
result <- character(5)
result[1:2] <- ifelse(is.na(x[1:2]), "Unavailable", sprintf("%.0f", x[1:2]))
result[3:5] <- ifelse(is.na(x[3:5]), "Unavailable",
sprintf(if (change) "%.1f pp" else "%.1f%%", 100 * x[3:5]))
result
}
comparison <- data.frame(
Metric = c("Posts entered", "Sentiment-labeled posts", "Negative share",
"Neutral share", "Positive share"),
`Day 1` = fmt(first), `Day 3` = fmt(second),
Change = fmt(delta, TRUE), check.names = FALSE
)
print(knitr::kable(comparison, caption = "Day 3 minus Day 1; pp = percentage points"))
if (first[1] < 100 || second[1] < 100) {
cat("\n\nAt least one collection has fewer than the required 100 posts.\n")
}
if (any(c(first[2], second[2]) < 10 | c(first[2], second[2]) > 20)) {
cat("\n\nUse a documented random sample of 10–20 manually labeled posts per collection.\n")
}
}
Collect both datasets with the same keyword and comparable settings. Record collection dates, timezone, post timestamps, and reply counts. Preserve IDs or URLs to identify repeated posts. Two fixed-size 100-post pulls alone cannot establish a percentage change in conversation volume; compare equivalent time windows. Different post ages also affect accumulated engagement.
Use Excel for the required analysis evidence and screenshot. The R report provides a reproducible companion to that work. Before submission, add:
#MKTG4000fiasco, as
directed by the assignment.No Day 3 findings, collection dates, or external research conclusions have been invented for this report.