1. Purpose

This lab compares public Bluesky posts about Xavier Becerra and Steve Hilton, the two candidates named in the assignment. I use the Bing sentiment lexicon to measure positive and negative language, check the results against my own labels, and make an experimental prediction. This is a classroom exercise; the percentages are a social-media proxy, not a representative voter poll.

2. AI prompt and instruction

I also supplied screenshots of the assignment, the previous lab’s atrrr code, and my Posit Cloud setup. Those instructions specified Bluesky data collection, removing unrelated Hilton posts, using tidytext and the Bing lexicon, manually labeling 10–20 random posts, reporting validation accuracy, selecting a prediction rule, bootstrapping, and publishing only aggregate results. ChatGPT could not access the classmate’s page, so this report follows the assignment screenshots. The analysis uses one row per post, with an internal post ID, candidate, and text. My manual labels are entered separately.

3. Data collection and cleaning audit

I search for each candidate’s full name using the same time window, English language filter, latest-first order, and a limit of 1,000 posts per candidate. The collection starts October 1, 2026, and ends at retrieval time or the start of election day in California, whichever is earlier. The API’s search results are a convenience sample and might not include every relevant post.

Retrieved: 2026-10-29 23:38:14 UTC
Search window (UTC): 2026-10-01T00:00:00Z to 2026-10-29T23:38:09Z (end exclusive).

Search results returned
Candidate Returned
Xavier Becerra 640
Steve Hilton 1056
Sequential cleaning audit
Step Removed Remaining
Search results before cleaning 0 1696
Remove missing IDs or blank text 10 1686
Remove duplicate post IDs across searches 161 1525
Remove Paris Hilton / Hilton Hotel matches 0 1525
Remove posts without either full name in the text 452 1073
Remove posts naming both candidates (ambiguous target) 120 953
Remove repeated text after stripping links and mentions 77 876

The cleaning removes duplicate IDs, repeated text, unrelated Hilton matches, and posts whose text does not name either candidate. I exclude posts naming both candidates because a single whole-post score cannot reliably identify which candidate the language targets. URLs and account mentions are removed before scoring. These filters may also remove some relevant posts. Repeated authors, possible bots, quoted opinions, and errors in language tags remain limitations. No usernames or individual post texts are published.

4. Bing sentiment results

Each positive word contributes +1 and each negative word contributes −1. The post’s score is positive words minus negative words. A positive score receives a positive label, a negative score receives a negative label, and zero receives a neutral label. Posts without any matched words stay in the analysis as neutral; zero can also mean equal positive and negative counts. These labels describe the text’s tone, which may differ from its stance toward the candidate.

Sentiment summary by candidate
Candidate Posts Positive Negative Neutral No matched words Positive rate Mean net score
Steve Hilton 614 164 224 226 141 26.7% -0.21
Xavier Becerra 262 78 48 136 102 29.8% 0.22

5. Manual validation

A simple random sample of up to 20 cleaned posts is selected with a fixed seed. For each post, I label its stance toward the named candidate as positive (supportive), negative (opposed), or neutral (unclear, mixed, or no expressed stance). The labeling tool hides the automatic label so it does not influence my choice. Agreement is the number of identical manual and automatic labels divided by the number of manually labeled posts.

DRAFT — manual validation is not finished. Do not submit this version.

Run source("label_posts.R") in the Console, label the posts, and click Knit again.

6. Prediction rule and bootstrap

I use a positive-share rule based on normalized positive rates. First, I calculate each candidate’s proportion of posts with a positive Bing label. Then I divide each proportion by the sum of the two proportions. This makes the estimated two-candidate vote shares add to 100% and avoids directly using the number of search results as support. The candidate above 50% is the predicted winner; 50–50 is a tie. This conversion is a classroom assumption, not a relationship calibrated using actual voting data.

\[ \widehat{V}_B = \frac{p_B}{p_B+p_H},\qquad \widehat{V}_H = 1-\widehat{V}_B \]

Here, \(p_B\) and \(p_H\) are the positive-post proportions for Becerra and Hilton. If neither has a positive post, this rule cannot produce a prediction.

Predicted winner under this rule: Xavier Becerra.

Candidate Estimated vote share (proxy) 95% bootstrap interval
Xavier Becerra 52.7% 46.8% to 58.1%
Steve Hilton 47.3% 41.9% to 53.2%

I ran 2,000 bootstrap resamples, sampling posts with replacement separately within each candidate’s group and recalculating the shares. 0 resamples had no positive posts for either candidate and were excluded because the rule was undefined.

The interval includes 50%, so the sample does not clearly separate the candidates under this rule.

These intervals describe resampling variability in the collected posts. They are not election margins of error and do not correct platform bias, search truncation, repeated authors, or sentiment-classification errors.

7. Reflection

This analysis shows why social-media attention should not automatically be treated as voter support. Bluesky users are not a random sample of California voters. Some posters may live outside California, may not be eligible to vote, or may participate much more often than other people. Limiting the search to English also excludes other voices. A candidate can receive many mentions because of criticism or news coverage, so more posts do not necessarily mean more support.

The Bing lexicon counts emotional words without fully understanding context. A supportive post can include negative words about an opponent, and sarcasm can make positive words express opposition. The hand-labeling comparison checks this problem, but 10–20 posts provide only a small validation sample. Removing posts about both candidates reduces ambiguity while also removing potentially useful comparisons.

The bootstrap shows how much the estimate changes when the observed posts are resampled. It cannot make the sample representative or turn text tone into voting intention. Posts from the same person may also be correlated, which means a post-level bootstrap can understate uncertainty. I would treat the predicted winner and vote share as an exploratory class result and compare them with representative polling before drawing conclusions about the election.

References