This report is part of the Data Science Capstone project. The final goal of this capstone is to build a predictive text model (similar to SwiftKey’s keyboard prediction) and deploy it as a Shiny web application.
This milestone report demonstrates that:
The dataset consists of text collected from three sources: blogs,
news articles, and Twitter. We load the English (en_US)
files.
library(stringi)
library(ggplot2)
library(dplyr)
library(tm)
library(tokenizers)
library(wordcloud)
library(knitr)
# Download and unzip data (run once)
url <- "https://d396qusza40orc.cloudfront.net/dsscapstone/dataset/Coursera-SwiftKey.zip"
download.file(url, destfile = "Coursera-SwiftKey.zip")
unzip("Coursera-SwiftKey.zip")
blogs_path <- "final/en_US/en_US.blogs.txt"
news_path <- "final/en_US/en_US.news.txt"
twitter_path <- "final/en_US/en_US.twitter.txt"
con <- file(blogs_path, "r")
blogs <- readLines(con, encoding = "UTF-8", skipNul = TRUE)
close(con)
con <- file(news_path, "r")
news <- readLines(con, encoding = "UTF-8", skipNul = TRUE)
close(con)
con <- file(twitter_path, "r")
twitter <- readLines(con, encoding = "UTF-8", skipNul = TRUE)
close(con)
We compute file size (MB), number of lines, number of words, and the length of the longest line for each of the three files.
file_size_mb <- function(path) round(file.info(path)$size / (1024^2), 2)
summary_df <- data.frame(
File = c("en_US.blogs.txt", "en_US.news.txt", "en_US.twitter.txt"),
Size_MB = c(file_size_mb(blogs_path), file_size_mb(news_path), file_size_mb(twitter_path)),
Lines = c(length(blogs), length(news), length(twitter)),
Words = c(sum(stri_count_words(blogs)),
sum(stri_count_words(news)),
sum(stri_count_words(twitter))),
Longest_Line_Chars = c(max(nchar(blogs)), max(nchar(news)), max(nchar(twitter)))
)
kable(summary_df, caption = "Summary Statistics of the Three Data Sets")
| File | Size_MB | Lines | Words | Longest_Line_Chars |
|---|---|---|---|---|
| en_US.blogs.txt | 200.42 | 899288 | 37546806 | 40833 |
| en_US.news.txt | 196.28 | 1010206 | 34761151 | 11384 |
| en_US.twitter.txt | 159.36 | 2360148 | 30096690 | 140 |
ggplot(summary_df, aes(x = File, y = Lines, fill = File)) +
geom_bar(stat = "identity") +
labs(title = "Number of Lines per Data Set", y = "Number of Lines", x = "") +
theme_minimal() +
theme(legend.position = "none")
ggplot(summary_df, aes(x = File, y = Words, fill = File)) +
geom_bar(stat = "identity") +
labs(title = "Number of Words per Data Set", y = "Number of Words", x = "") +
theme_minimal() +
theme(legend.position = "none")
Observation: The Twitter file has the most lines (short tweets), while the Blogs file has the longest individual lines (long-form writing), and the News file sits in between with more structured sentence lengths.
Since the full data sets are large (hundreds of MB), we work with a random sample (e.g., 1% of lines from each source) to keep processing fast during exploratory analysis. The final model will be trained on a larger, appropriately sized sample.
set.seed(1234)
sample_pct <- 0.01
sample_blogs <- sample(blogs, length(blogs) * sample_pct)
sample_news <- sample(news, length(news) * sample_pct)
sample_twitter <- sample(twitter, length(twitter) * sample_pct)
sample_data <- c(sample_blogs, sample_news, sample_twitter)
length(sample_data)
## [1] 42695
Basic text cleaning: lowercasing, removing numbers, punctuation,
extra whitespace, and profanity filtering (a simple bad-words list can
be used with tm::removeWords).
corpus <- VCorpus(VectorSource(sample_data))
corpus <- tm_map(corpus, content_transformer(tolower))
corpus <- tm_map(corpus, removeNumbers)
corpus <- tm_map(corpus, removePunctuation)
corpus <- tm_map(corpus, stripWhitespace)
To understand word patterns, we build unigram, bigram, and
trigram frequency tables using RWeka.
# Extract cleaned text back out of the corpus
clean_text <- sapply(corpus, as.character)
get_ngram_freq <- function(text, n, top_n = 15) {
ngrams <- tokenize_ngrams(text, n = n, simplify = TRUE)
ngrams <- unlist(ngrams)
freq_table <- sort(table(ngrams), decreasing = TRUE)
data.frame(term = names(freq_table)[1:top_n],
frequency = as.integer(freq_table)[1:top_n])
}
unigram_freq <- get_ngram_freq(clean_text, n = 1)
bigram_freq <- get_ngram_freq(clean_text, n = 2)
trigram_freq <- get_ngram_freq(clean_text, n = 3)
ggplot(unigram_freq, aes(x = reorder(term, frequency), y = frequency)) +
geom_bar(stat = "identity", fill = "steelblue") +
coord_flip() +
labs(title = "Top 15 Most Frequent Words", x = "Word", y = "Frequency") +
theme_minimal()
ggplot(bigram_freq, aes(x = reorder(term, frequency), y = frequency)) +
geom_bar(stat = "identity", fill = "darkorange") +
coord_flip() +
labs(title = "Top 15 Most Frequent Bigrams", x = "Bigram", y = "Frequency") +
theme_minimal()
ggplot(trigram_freq, aes(x = reorder(term, frequency), y = frequency)) +
geom_bar(stat = "identity", fill = "darkgreen") +
coord_flip() +
labs(title = "Top 15 Most Frequent Trigrams", x = "Trigram", y = "Frequency") +
theme_minimal()
(Written for a non-technical audience.)
The final product will be a simple web app where a user types a few words, and the app suggests the most likely next word — similar to predictive text on a smartphone keyboard.
How we will build it:
Next steps: - Finalize n-gram tables using a larger sample of the data. - Build and test the back-off prediction model. - Evaluate accuracy and response speed. - Deploy the final model inside a Shiny app with a clean, simple UI.
The data has been successfully downloaded, loaded, sampled, and explored. Basic summary statistics and n-gram frequency analysis have revealed useful patterns in the text that will guide the development of the predictive text algorithm and Shiny application in the next phase of this project.