Lab 4: Cleaning Web Data for Visualization

Dataset description
Tweets carrying the #covid19 hashtag, collected daily between 24 July and 30 August 2020, with the raw tweet text and the posting account's metadata. No sentiment labels ship with the data — sentiment here is computed, not read off the file.
Data source
Gabriel Preda, COVID19 Tweets (179,108 tweets), sampled down to 1,040 rows and then cleaned and scored by clean_tweets.py.
Records
1,040 tweets sampled from the source, 1,035 surviving cleaning, each carrying a sentiment estimate.
Sentiment
Estimated with cardiffnlp/twitter-roberta-base-sentiment-latest, a RoBERTa Transformer fine-tuned on social-media text. These are model estimates, not ground-truth labels.

Does it matter who is doing the typing?

Every tweet records the client it was posted from. Most come from a phone or from the Twitter website. A smaller share arrived through newsroom and marketing toolings: TweetDeck, or scheduling suites like Hootsuite and Buffer, where a tweet is drafted, approved and queued before being posted.

Loading the data…

Each bar is one posting route, split into the share of its tweets the model calls negative, neutral and positive. Negative shares run left of the centre line and positive shares run right, so the further a bar leans, the more one-sided that route is. Routes are ordered by mean sentiment score, with most negative at the top. Overall, the tweet sources are shown in the table below, with their corresponding counts.

Show these numbers as a table

Analysis

The data is 1,040 tweets carrying #covid19, sampled 40 per day across the 26 collection days of a 179,108-tweet archive gathered in July and August 2020. Cleaning cost 5 rows: 4 near-duplicate campaign templates reposted with fresh tracking links, and 1 non-English tweet, leaving 1,035 scored tweets. No sentiment ships with the data, so every tweet was scored with RoBERTa, and the chart plots the resulting shares diverging from a common centre.

The negative share falls steadily as the route gets more institutional: 39% of tweets typed on a phone, 32% from the Twitter website, 21% from TweetDeck, 14% from a scheduling suite. Mean score follows it from −0.20 to +0.01. The other end of the bar barely moves — positive runs 9–14% on all four routes. Organisational tweets are not cheerier than personal ones; they trade anger for neutrality, which is what drafted-and-queued copy sounds like.

Optional extension — where the model is unsure

The model returns a probability for each of the three classes, and the label is simply the largest of the three. That number is worth looking at on its own. A tweet called negative at 0.99 and one called negative at 0.41 with the two ends of the range differing, but arriving at dataset while being identical.

Loading…

How sure the model was of the label it picked, across every scored tweet. The lowest possible value is about 0.33, where all three classes are tied; anything under 0.50 means the winning class did not even carry half the probability.

The tweets it found hardest

These are the tweets where the top two classes came closest to a tie — where the label is near enough to a coin flip that it should not be trusted on its own. Reading them shows what the model struggles with.

How the data was cleaned

clean_tweets.py then does the work, printing a report of every decision as it goes. Tweets with no text are dropped, because they cannot be scored. Missing locations and hashtags are left missing rather than filled with zeros. In this dataset, duplicate instances were rare, but the same campaign text was posted repeatedly with a different tracking link on the end, so tweets were also compared with links. That caught the near-copies which would otherwise let a single petition template dominate a sentiment total. is_retweet is False on every row, so it separates nothing and is dropped.

The source column is the interesting one. It holds 71 distinct client strings, including one written with a Greek omicron in place of a Latin O, several carrying trailing spaces or stray full stops, and a long tail of one-off corporate apps. Lookalike letters are folded onto their Latin equivalents, whitespace and punctuation are normalised, and the result is collapsed onto seven posting routes — exact matching for the official clients, keyword rules for everything else. user_location is free text with thousands of spellings; a keyword pass recovers a country where it can and leaves the rest missing rather than guessing.

One caveat the data forces. The collector capped itself at roughly 7,500 tweets a day and missed several days entirely, so the number of tweets on a given day reflects the collection process rather than how much the world was tweeting.

← Back to home