Visualization of
Data
# Load the necessary libraries
library(tm)
## Warning: package 'tm' was built under R version 4.3.3
## Loading required package: NLP
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
## Warning: package 'topicmodels' was built under R version 4.3.3
## Warning: package 'wordcloud' was built under R version 4.3.3
## Loading required package: RColorBrewer
##
## Attaching package: 'ggplot2'
## The following object is masked from 'package:NLP':
##
## annotate
## Warning: package 'lubridate' was built under R version 4.3.3
##
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
##
## date, intersect, setdiff, union
## Warning: package 'tidytext' was built under R version 4.3.3
library(tidyr)
library(sentimentr)
## Warning: package 'sentimentr' was built under R version 4.3.3
## Warning: package 'LDAvis' was built under R version 4.3.3
# Convert titles to character type
news_data$titles <- as.character(news_data$titles)
# Create a corpus from the titles
titles <- Corpus(VectorSource(news_data$titles))
# Define custom stopwords
custom_stopwords <- c("pro", "us", "will", "is", "the", "to", "of", "in",
"for", "with", "on", "at", "by", "an", "be", "as", "that",
"from", "this", "it", "are", "was", "has", "have", "had",
"were", "their", "his", "her", "he", "she", "they",
"them", "our", "we", "you", "your", "and", "or", "whole")
# Text cleaning function
clean_text <- function(corpus) {
corpus <- tm_map(corpus, content_transformer(tolower)) # Convert to lowercase
corpus <- tm_map(corpus, removePunctuation) # Remove punctuation
corpus <- tm_map(corpus, removeNumbers) # Remove numbers
corpus <- tm_map(corpus, removeWords, custom_stopwords) # Remove common and custom stopwords
corpus <- tm_map(corpus, stripWhitespace) # Remove extra whitespace
return(corpus)
}
# Apply the cleaning function
clean_corpus <- clean_text(titles)
## Warning in tm_map.SimpleCorpus(corpus, content_transformer(tolower)):
## transformation drops documents
## Warning in tm_map.SimpleCorpus(corpus, removePunctuation): transformation drops
## documents
## Warning in tm_map.SimpleCorpus(corpus, removeNumbers): transformation drops
## documents
## Warning in tm_map.SimpleCorpus(corpus, removeWords, custom_stopwords):
## transformation drops documents
## Warning in tm_map.SimpleCorpus(corpus, stripWhitespace): transformation drops
## documents
# Convert corpus to a tidy text format
titles_content <- sapply(clean_corpus, as.character)
words <- unlist(strsplit(titles_content, "\\s+"))
words <- words[words != ""]
# Create a data frame for tidy text analysis
titles_df <- data.frame(text = titles_content, stringsAsFactors = FALSE)
Top Keywords
# Plot top 10 keywords with the highest TF-IDF
tf_idf_top20 <- head(tf_idf, 10)
top_keywords <- tf_idf_top20 %>% mutate(word = reorder(word, tf_idf))
# Create plot with purple color and attractive background
ggplot(top_keywords, aes(x = word, y = tf_idf, fill = tf_idf)) +
geom_col(show.legend = FALSE) +
coord_flip() +
scale_fill_gradient(low = "#E0BBE4", high = "#957DAD") +
labs(title = "Top Keywords", x = "Keywords", y = "TF-IDF") +
theme_minimal(base_size = 15) +
theme(
plot.title = element_text(hjust = 0.5, size = 17, face = "bold", color = "#351C4D"),
axis.title.x = element_text(size = 10, face = "bold", color = "#351C4D"),
axis.title.y = element_text(size = 10, face = "bold", color = "#351C4D"),
axis.text.x = element_text(size = 12, color = "#351C4D"),
axis.text.y = element_text(size = 12, color = "#351C4D"),
plot.background = element_rect(fill = "#F4EAFF"),
panel.grid.major = element_line(color = "#F4EAFF"),
panel.grid.minor = element_blank()
)

WordCloud Top
Keywords
library(ggplot2)
library(ggwordcloud)
## Warning: package 'ggwordcloud' was built under R version 4.3.3
# Creating word cloud based on TF-IDF
wordcloud_data <- tf_idf %>%
select(word, tf_idf) %>%
arrange(desc(tf_idf)) %>%
head(50)
# Creating a word cloud with ggwordcloud
ggplot(wordcloud_data, aes(label = word, size = tf_idf, color = tf_idf)) +
geom_text_wordcloud(area_corr_power = 1) +
scale_size_area(max_size = 8) +
scale_color_gradient(low = "#0071C9", high = "#823430") +
theme_minimal() +
theme(
plot.background = element_rect(fill = "#D2B4DE", color = NA),
panel.background = element_rect(fill = "#D2B4DE", color = NA),
plot.title = element_text(hjust = 0.5, size = 20, face = "bold", color = "#41008F"),
plot.margin = margin(8, 8, 8, 8)
) +
labs(title = "Top Keywords") +
theme(
panel.grid.major = element_line(size = 0.5, linetype = 'solid', colour = "gray90"),
panel.grid.minor = element_blank(),
panel.background = element_rect(fill = "#F4EAFF")
)
## Warning in geom_text_wordcloud(area_corr_power = 1): Ignoring unknown
## parameters: `area_corr_power`
## Warning: The `size` argument of `element_line()` is deprecated as of ggplot2 3.4.0.
## ℹ Please use the `linewidth` argument instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

Topic Modeling
# Topic Modeling with LDA
dtm <- DocumentTermMatrix(clean_corpus)
lda_model <- LDA(dtm, k = 5, control = list(seed = 1234))
# Extract topics and terms
topics <- tidy(lda_model, matrix = "beta")
# Topic visualization
top_terms <- topics %>%
group_by(topic) %>%
top_n(10, beta) %>%
ungroup() %>%
arrange(topic, -beta)
# Visualization with ggplot2
top_terms %>%
mutate(term = reorder_within(term, beta, topic)) %>%
ggplot(aes(term, beta, fill = factor(topic))) +
geom_col(show.legend = FALSE) +
facet_wrap(~ topic, scales = "free") +
coord_flip() +
scale_x_reordered() +
labs(title = "Top 10 Terms in Each Topic", x = "Terms", y = "Beta") +
theme_minimal(base_size = 15) +
theme(
plot.title = element_text(hjust = 0.5, size = 17, face = "bold"),
axis.title.x = element_text(size = 5, face = "bold"),
axis.title.y = element_text(size = 5, face = "bold"),
axis.text.x = element_text(size = 5),
axis.text.y = element_text(size = 5)
)

topic_terms <- topics %>%
group_by(topic) %>%
top_n(10, beta) %>%
ungroup() %>%
arrange(topic, -beta) %>%
mutate(term = reorder(term, beta))
Topic Modeling
Category
# Defining topic categories or labels
# For example, after looking at the top words, we assign the following labels:
topic_labels <- c("Technology", "Environment", "Economy", "Education", "Health")
# Display labels for each topic
top_terms <- top_terms %>%
mutate(topic_label = factor(topic_labels[topic], levels = topic_labels))
# Visualization with topic labels
top_terms %>%
mutate(term = reorder_within(term, beta, topic_label)) %>%
ggplot(aes(term, beta, fill = factor(topic_label))) +
geom_col(show.legend = FALSE) +
facet_wrap(~ topic_label, scales = "free") +
coord_flip() +
scale_x_reordered() +
labs(title = "Top 10 Terms in Each Topic", x = "Terms", y = "Beta") +
theme_minimal(base_size = 15) +
theme(
plot.title = element_text(hjust = 0.5, size = 17, face = "bold"),
axis.title.x = element_text(size = 5, face = "bold"),
axis.title.y = element_text(size = 5, face = "bold"),
axis.text.x = element_text(size = 5),
axis.text.y = element_text(size = 5)
)

Analisis Sentimen
Headlines
# Convert data into frame data
titles_df <- data.frame(id = 1:length(titles_content), text = titles_content, stringsAsFactors = FALSE)
# Tokenization of text
tidy_titles <- titles_df %>%
unnest_tokens(word, text)
# Using Bing's sentiment lexicon
bing_sentiments <- get_sentiments("bing")
# Combining text with Bing's sentiment lexicon
sentiments <- tidy_titles %>%
left_join(bing_sentiments, by = "word")
# Replace NA with "neutral"
sentiments$sentiment[is.na(sentiments$sentiment)] <- "neutral"
# Count sentiment per title
sentiment_per_title <- sentiments %>%
group_by(id) %>%
summarize(sentiment_score = sum(ifelse(sentiment == "positive", 1, ifelse(sentiment == "negative", -1, 0))))
# Determining overall sentiment per title
sentiment_per_title <- sentiment_per_title %>%
mutate(sentiment = case_when(
sentiment_score > 0 ~ "positive",
sentiment_score < 0 ~ "negative",
TRUE ~ "neutral"
))
# Count the number of titles per sentiment
sentiment_summary <- sentiment_per_title %>%
count(sentiment)
print(sentiment_summary)
## # A tibble: 3 × 2
## sentiment n
## <chr> <int>
## 1 negative 38
## 2 neutral 45
## 3 positive 12
# Sentiment result plot
ggplot(sentiment_summary, aes(x = sentiment, y = n, fill = sentiment)) +
geom_bar(stat = "identity", show.legend = FALSE) +
scale_fill_manual(values = c("positive" = "#8E44AD", "negative" = "#9B59B6", "neutral" = "#D2B4DE")) +
labs(title = "Sentiment Analysis of Yahoo News Headlines",
x = "Sentiment",
y = "Count") +
theme_minimal(base_size = 15) +
theme(
plot.title = element_text(hjust = 0.5, face = "bold", size = 15, color = "#4A235A"),
axis.title.x = element_text(face = "bold", size = 15, color = "#4A235A"),
axis.title.y = element_text(face = "bold", size = 15, color = "#4A235A"),
axis.text = element_text(size = 12, color = "#4A235A"),
panel.grid.major = element_line(color = "white"),
panel.grid.minor = element_blank(),
panel.background = element_rect(fill = "white"),
plot.background = element_rect(fill = "#F4EAFF", color = "#4A235A", size = 5),
plot.margin = unit(c(2, 2, 2, 2), "cm")
)
## Warning: The `size` argument of `element_rect()` is deprecated as of ggplot2 3.4.0.
## ℹ Please use the `linewidth` argument instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

Analisis News
Headline Trends over Time
# Load data
news_data <- read.csv("C:/Users/ASUS/Downloads/yahoo_news.csv", sep = ";")
# Convert the 'dates' column to Date format
news_data$dates <- dmy(news_data$dates)
# Counts the number of articles published daily
date_freq <- news_data %>%
group_by(dates) %>%
summarise(count = n())
# Display frequency analysis results
print(head(date_freq))
## # A tibble: 6 × 2
## dates count
## <date> <int>
## 1 2024-05-29 7
## 2 2024-05-30 5
## 3 2024-05-31 5
## 4 2024-06-01 7
## 5 2024-06-02 7
## 6 2024-06-03 6
library(ggplot2)
# Create a visualization plot
ggplot(date_freq, aes(x = dates, y = count)) +
geom_line(color = "#9B59B6", size = 1.5) + # Purple line
geom_point(color = "#823430", size = 3) + # Red points
scale_x_date(date_labels = "%d-%m-%Y", date_breaks = "1 day") + # Format date axis
labs(title = "News Headline Trends over Time",
x = "Tanggal", y = "Jumlah Artikel") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1, size = 8, color = "#800080"), # Rotate x-axis labels
axis.text.y = element_text(size = 8, color = "#800080"), # Adjust y-axis labels
axis.title = element_text(size = 10, color = "#800080"), # Adjust axis titles
plot.title = element_text(size = 14, color = "#800080", face = "bold", hjust = 0.5), # Adjust plot title
panel.background = element_rect(fill = "#F4EAFF"), # Set background color
plot.background = element_rect(fill = "#F4EAFF")) # Set plot background color
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

Analisis N-Gram
# Define custom stopwords
custom_stopwords <- c("pro", "us", "will", "is", "the", "to", "of", "in", "for", "with",
"on", "at", "by", "an", "be", "as", "that", "s", "the", "from",
"this", "it", "are", "was", "has", "have", "had", "were", "their",
"his", "her", "he", "she", "they", "them", "our", "we", "you",
"your", "and", "or", "whole","a","not", "more")
# Function to clean up the text
clean_text <- function(text) {
text <- tolower(text) # Konversi ke huruf kecil
text <- removePunctuation(text) # Hapus tanda baca
text <- removeNumbers(text) # Hapus angka
text <- removeWords(text, custom_stopwords) # Hapus stopwords khusus
text <- stripWhitespace(text) # Hapus spasi berlebihan
return(text)
}
# Apply text cleanup on contents column
news_data$clean_contents <- sapply(news_data$contents, clean_text)
# Tokenization of content into bigrams
bigrams <- news_data %>%
unnest_tokens(bigram, clean_contents, token = "ngrams", n = 2)
# Counting the frequency of bigrams
bigram_freq <- bigrams %>%
count(bigram, sort = TRUE)
# Display the 10 most frequently occurring bigrams
print(head(bigram_freq, 10))
## bigram n
## 1 coast guard 5
## 2 las vegas 5
## 3 oil gas 4
## 4 arab emirates 3
## 5 chief paco 3
## 6 fresno police 3
## 7 gas industry 3
## 8 hours after 3
## 9 last two 3
## 10 last year 3
Most Frequent Bigrams
in Article Content
ggplot(bigram_freq %>% head(10), aes(x = reorder(bigram, n), y = n, fill = n)) +
geom_bar(stat = "identity", show.legend = FALSE) +
coord_flip() +
scale_fill_gradient(low = "#9B59B6", high = "#800080") +
labs(title = "Most Frequent Bigrams in Article Content", x = "Bigram", y = "Frekuensi") +
theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5, size = 17, face = "bold", color = "#800080"),
axis.title.x = element_text(size = 12, face = "bold", color = "#800080"),
axis.title.y = element_text(size = 12, face = "bold", color = "#800080"),
axis.text.x = element_text(size = 10, color = "#800080"),
axis.text.y = element_text(size = 10, color = "#800080"),
plot.background = element_rect(fill = "#F4EAFF", color = "#800080", size = 1, linetype = "solid"),
panel.background = element_rect(fill = "#F4EAFF", color = "#800080", size = 1, linetype = "solid"),
panel.grid.major = element_line(color = "#F4EAFF"),
panel.grid.minor = element_blank()
)

Sentiment Bigrams in
Article Content
bing_sentiments <- get_sentiments("bing")
bigrams_separated <- bigrams %>%
separate(bigram, into = c("word1", "word2"), sep = " ")
bigram_sentiments <- bigrams_separated %>%
left_join(bing_sentiments, by = c("word1" = "word")) %>%
rename(sentiment1 = sentiment) %>%
left_join(bing_sentiments, by = c("word2" = "word")) %>%
rename(sentiment2 = sentiment)
# Replace NA with "neutral"
bigram_sentiments <- bigram_sentiments %>%
mutate(sentiment1 = ifelse(is.na(sentiment1), "neutral", sentiment1),
sentiment2 = ifelse(is.na(sentiment2), "neutral", sentiment2))
# Calculate sentiment score per bigram
bigram_sentiments <- bigram_sentiments %>%
mutate(sentiment_score = case_when(
sentiment1 == "positive" & sentiment2 == "positive" ~ 2,
sentiment1 == "negative" & sentiment2 == "negative" ~ -2,
sentiment1 == "positive" & sentiment2 == "negative" ~ 0,
sentiment1 == "negative" & sentiment2 == "positive" ~ 0,
sentiment1 == "positive" | sentiment2 == "positive" ~ 1,
sentiment1 == "negative" | sentiment2 == "negative" ~ -1,
TRUE ~ 0
))
# Determining overall sentiment per bigram
bigram_sentiments <- bigram_sentiments %>%
mutate(overall_sentiment = case_when(
sentiment_score > 0 ~ "positive",
sentiment_score < 0 ~ "negative",
TRUE ~ "neutral"
))
# Count the number of bigrams per sentiment
sentiment_summary <- bigram_sentiments %>%
count(overall_sentiment)
# Show sentiment summaries
print(sentiment_summary)
## overall_sentiment n
## 1 negative 239
## 2 neutral 2321
## 3 positive 141
# Plot sentiment summary
ggplot(sentiment_summary, aes(x = overall_sentiment, y = n, fill = overall_sentiment)) +
geom_bar(stat = "identity", show.legend = FALSE) +
scale_fill_manual(values = c("positive" = "#800080", "negative" = "#E0BBE4", "neutral" = "#9B59B6")) +
labs(title = "Sentiment Bigrams in Article Content", x = "Sentiment", y = "Number of Bigrams") +
theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5, size = 17, face = "bold", color = "#800080"),
axis.title.x = element_text(size = 12, face = "bold", color = "#800080"),
axis.title.y = element_text(size = 12, face = "bold", color = "#800080"),
axis.text.x = element_text(size = 10, color = "#800080"),
axis.text.y = element_text(size = 10, color = "#800080"),
plot.background = element_rect(fill = "#F4EAFF", color = NA),
panel.background = element_rect(fill = "#F4EAFF", color = NA),
panel.grid.major = element_line(color = "#D3CCE3"),
panel.grid.minor = element_blank()
)
