Read it in order: drop snapshots with no recorded category, count how many snapshots each channel logged in each category, then keep only each channel’s single most frequent one.
is.na() finds missing values and ! means “not”; slice_max() keeps the top row by count. The result is one row per channel.
Your turn
Predict: what happens to a channel whose stream snapshots all have a missing game? Trace it through filter() and count().
Does that channel appear in channel_type at all?
What would you want R to do with its messages later: guess, drop, or flag?
Three minutes with a neighbor. We check your prediction in four slides.
%in% asks whether a channel’s dominant category is on the non-gaming list and ! flips it, so is_gaming is TRUE for game channels and FALSE otherwise.
channel_type is now a compact lookup: one row per channel, one column.
Step 3: the join
chat <- chat %>%left_join(channel_type, by ="channel")
A join brings columns from one table onto another by matching a shared key, and left_join() keeps every row of chat while attaching each channel’s is_gaming value.
by = "channel" names the key both tables share.
Joins fail on keys
A join is only as good as the match between its keys
Twitch’s chat protocol writes channel names with a leading “#”
So the same channel can be #bobross in one source and bobross in another
Join tables whose keys disagree and every row fails to match, silently
The v2v data is already normalized, but raw Twitch data would need cleaning first
Always inspect what you derived
chat %>%count(is_gaming)
is_gaming n
1 FALSE 3457
2 TRUE 31309
3 NA 501
count() on a new column is the cheapest check there is, and here it turns up a third value nobody asked for.
The 501 messages
Two channels streamed without ever having a category recorded
Every one of their snapshots had a missing game, so there was no modal category
They never entered channel_type, so the join left their messages as NA
That is the correct outcome, not a bug
The data genuinely does not say. Those 501 messages sit out the comparison rather than being guessed into one side of it.
The table is now tidy: one row per observation, one column per variable, everything the study needs in one place.
Common errors today
Join returns all NA: mismatched keys, so check for a stray “#” or capitalization
object 'channel_type' not found: you ran the join before building the lookup
slice_max errors: you skipped group_by(), so there are no groups to slice within
A category name misspelled in nongaming_categories silently mislabels a channel
Rerun the whole script in a fresh session. If it does not reproduce, something is undocumented.
The package ships the ones specific to this data: run ?v2v::common_errors for the year-50,888 timestamp, the join that matches nothing, and the surprisingly low kappa.
Checkpoint
You should now have:
A channel_type lookup, one row per channel, with is_gaming
A chat table of eight columns including timestamp, message_length, is_gaming
A count(is_gaming) result you have actually looked at
A saved script that rebuilds all of it from the raw data in one run
Sampling Plan and Pilot, and Data Wrangling, are due this week
Before next time
Rerun your script from a clean R session and confirm it reproduces the table
Comment your wrangling decisions. A reviewer will ask why you excluded what you excluded.
Read Chapter 12, Describing the Data
Next week the table finally becomes pictures: chat volume by hour, viewership by category, and message length compared across gaming and non-gaming