This analysis used text-mining techniques to examine differences in headline language between left-leaning and right-leaning news outlets. Headlines were tokenized into bigrams, which are two-word phrases, and common stop words were removed to focus on substantively meaningful terms. The first visualization compared the most frequently used bigrams within each ideological group. The results demonstrated that left- and right-leaning outlets tended to emphasize different topics and frames, suggesting that media organizations with different ideological orientations highlight different aspects of political news coverage. While some themes appeared across both groups, the most common phrases often reflected distinct priorities and narrative approaches.
The ridgeline plots provided a temporal perspective by tracking the weekly frequency of the most common bigrams for each ideological group. These visualizations showed that the prominence of specific phrases changed over time rather than remaining constant throughout the study period. Spikes in frequency likely corresponded to major political events, controversies, or developments involving Donald Trump. The plots also revealed that certain bigrams remained consistently prominent, while others appeared only briefly in response to specific news events. Together, the visualizations demonstrate how media attention shifts over time and how ideological outlets can differ in the language used to frame political coverage.
AI was used as a coding assistant throughout this assignment.
Specifically, AI helped adapt example text-mining scripts to the
structure of the headlines_lr dataset, modify code for
bigram analysis, create comparison and ridgeline visualizations,
troubleshoot R Markdown knitting issues, and explain coding errors. All
code was reviewed, tested, and executed by the researcher, with AI
serving as a tool to support programming and problem-solving rather than
to generate the analytical conclusions.
############################################################
# Part 1: Compare Top Bigrams by Ideology
############################################################
comparison_bigrams <- headlines_lr %>%
select(title, ideology) %>%
filter(ideology %in% c("Left", "Right")) %>%
unnest_tokens(
bigram,
title,
token = "ngrams",
n = 2
)
comparison_bigrams_clean <- comparison_bigrams %>%
separate(
bigram,
into = c("word1", "word2"),
sep = " "
) %>%
filter(
!word1 %in% stop_words$word,
!word2 %in% stop_words$word
) %>%
unite(
bigram,
word1,
word2,
sep = " "
)
comparison_counts <- comparison_bigrams_clean %>%
count(
ideology,
bigram,
sort = TRUE
)
comparison_chart_data <- comparison_counts %>%
group_by(ideology) %>%
slice_max(
n,
n = 15
) %>%
ungroup()
bigram_frequency_chart <- ggplot(
comparison_chart_data,
aes(
x = reorder_within(
bigram,
n,
ideology
),
y = n,
fill = ideology
)
) +
geom_col(show.legend = FALSE) +
geom_text(
aes(label = n),
hjust = -0.1,
size = 3
) +
coord_flip() +
scale_x_reordered() +
facet_wrap(
~ ideology,
ncol = 1,
scales = "free_y"
) +
scale_fill_manual(
values = c(
"Left" = "steelblue",
"Right" = "firebrick"
)
) +
scale_y_continuous(
expand = expansion(
mult = c(0, .10)
)
) +
labs(
title = "Most Frequent Bigrams in News Headlines",
subtitle = "Comparison of Left-Leaning and Right-Leaning News Outlets",
x = "Bigram",
y = "Frequency"
) +
theme_minimal()
bigram_frequency_chart
############################################################
# Part 2: Left-Leaning Outlet Ridgeline Plot
############################################################
left_headlines <- headlines_lr %>%
filter(ideology == "Left") %>%
mutate(
publish_date = as.Date(publish_date),
WeekStart = floor_date(
publish_date,
unit = "week",
week_start = 1
)
)
left_bigrams <- left_headlines %>%
unnest_tokens(
bigram,
title,
token = "ngrams",
n = 2
)
left_bigrams_clean <- left_bigrams %>%
separate(
bigram,
into = c("word1", "word2"),
sep = " "
) %>%
filter(
!word1 %in% stop_words$word,
!word2 %in% stop_words$word
) %>%
unite(
bigram,
word1,
word2,
sep = " "
)
left_top_bigrams <- left_bigrams_clean %>%
count(
bigram,
sort = TRUE
) %>%
slice_max(
n,
n = 15
)
left_weekly_counts <- left_bigrams_clean %>%
semi_join(
left_top_bigrams,
by = "bigram"
) %>%
count(
bigram,
WeekStart,
name = "weekly_count"
)
left_weekly_counts <- left_weekly_counts %>%
group_by(bigram) %>%
complete(
WeekStart = seq(
min(WeekStart),
max(WeekStart),
by = "week"
),
fill = list(
weekly_count = 0
)
) %>%
ungroup()
left_bigram_order <- left_top_bigrams %>%
arrange(desc(n)) %>%
pull(bigram)
left_weekly_counts <- left_weekly_counts %>%
mutate(
bigram = factor(
bigram,
levels = left_bigram_order
)
)
left_ridgeline <- ggplot(
left_weekly_counts,
aes(
x = WeekStart,
y = weekly_count,
height = weekly_count
)
) +
geom_ridgeline(
fill = "steelblue",
color = "white",
alpha = .8
) +
facet_wrap(
~ bigram,
ncol = 1,
scales = "free_y",
strip.position = "right"
) +
labs(
title = "Weekly Frequency of Top Headline Bigrams in Left-Leaning News Outlets",
x = "Week",
y = NULL
) +
theme_ridges() +
theme(
strip.text.y.right = element_text(
size = 10,
angle = 0
),
axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
panel.spacing = grid::unit(
.15,
"lines"
),
legend.position = "none"
)
left_ridgeline
############################################################
# Part 3: Right-Leaning Outlet Ridgeline Plot
############################################################
right_headlines <- headlines_lr %>%
filter(ideology == "Right") %>%
mutate(
publish_date = as.Date(publish_date),
WeekStart = floor_date(
publish_date,
unit = "week",
week_start = 1
)
)
right_bigrams <- right_headlines %>%
unnest_tokens(
bigram,
title,
token = "ngrams",
n = 2
)
right_bigrams_clean <- right_bigrams %>%
separate(
bigram,
into = c("word1", "word2"),
sep = " "
) %>%
filter(
!word1 %in% stop_words$word,
!word2 %in% stop_words$word
) %>%
unite(
bigram,
word1,
word2,
sep = " "
)
right_top_bigrams <- right_bigrams_clean %>%
count(
bigram,
sort = TRUE
) %>%
slice_max(
n,
n = 15
)
right_weekly_counts <- right_bigrams_clean %>%
semi_join(
right_top_bigrams,
by = "bigram"
) %>%
count(
bigram,
WeekStart,
name = "weekly_count"
)
right_weekly_counts <- right_weekly_counts %>%
group_by(bigram) %>%
complete(
WeekStart = seq(
min(WeekStart),
max(WeekStart),
by = "week"
),
fill = list(
weekly_count = 0
)
) %>%
ungroup()
right_bigram_order <- right_top_bigrams %>%
arrange(desc(n)) %>%
pull(bigram)
right_weekly_counts <- right_weekly_counts %>%
mutate(
bigram = factor(
bigram,
levels = right_bigram_order
)
)
right_ridgeline <- ggplot(
right_weekly_counts,
aes(
x = WeekStart,
y = weekly_count,
height = weekly_count
)
) +
geom_ridgeline(
fill = "firebrick",
color = "white",
alpha = .8
) +
facet_wrap(
~ bigram,
ncol = 1,
scales = "free_y",
strip.position = "right"
) +
labs(
title = "Weekly Frequency of Top Headline Bigrams in Right-Leaning News Outlets",
x = "Week",
y = NULL
) +
theme_ridges() +
theme(
strip.text.y.right = element_text(
size = 10,
angle = 0
),
axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
panel.spacing = grid::unit(
.15,
"lines"
),
legend.position = "none"
)
right_ridgeline