Code
full_text_tidy <- eo_data |>
select(document_number, title, publication_date, signing_date, full_text) |>
unnest_tokens(word, full_text)full_text_tidy <- eo_data |>
select(document_number, title, publication_date, signing_date, full_text) |>
unnest_tokens(word, full_text)full_text_tidy <- full_text_tidy %>%
mutate(word = str_extract(word, "[a-z']+"))full_text_tidy <- full_text_tidy %>%
anti_join(stop_words)
full_text_tidy# A tibble: 173,215 × 5
document_number title publication_date signing_date word
<chr> <chr> <date> <date> <chr>
1 2016-30101 Relating to the Implemen… 2016-12-13 2016-12-08 title
2 2016-30101 Relating to the Implemen… 2016-12-13 2016-12-08 <NA>
3 2016-30101 Relating to the Implemen… 2016-12-13 2016-12-08 pres…
4 2016-30101 Relating to the Implemen… 2016-12-13 2016-12-08 exec…
5 2016-30101 Relating to the Implemen… 2016-12-13 2016-12-08 <NA>
6 2016-30101 Relating to the Implemen… 2016-12-13 2016-12-08 dece…
7 2016-30101 Relating to the Implemen… 2016-12-13 2016-12-08 <NA>
8 2016-30101 Relating to the Implemen… 2016-12-13 2016-12-08 rela…
9 2016-30101 Relating to the Implemen… 2016-12-13 2016-12-08 impl…
10 2016-30101 Relating to the Implemen… 2016-12-13 2016-12-08 conv…
# ℹ 173,205 more rows
meaningless_words <- tibble(word=c("thereof", "section", "sec", "ii", "iii", "iv", "vi", "vii", "viii"))
full_text_tidy <- full_text_tidy %>%
anti_join(meaningless_words)fulltext_freq <- full_text_tidy %>%
filter(!is.na(word)) %>%
count(word, sort=T)
head(fulltext_freq, 20)# A tibble: 20 × 2
word n
<chr> <int>
1 united 2068
2 executive 1994
3 agencies 1523
4 agency 1463
5 secretary 1381
6 federal 1379
7 president 1298
8 law 1214
9 national 1187
10 department 1090
11 government 1058
12 including 1008
13 act 979
14 information 974
15 council 951
16 security 933
17 person 883
18 director 775
19 office 747
20 authority 726
fulltext_freq %>%
group_by(word) %>%
summarise(n = sum(n)) %>%
filter(n>800) %>%
ggplot(aes(x=n, y=reorder(word, n), fill=n)) +
geom_col(fill = "cadetblue")+
labs(
x= "Frequency",
y= "Word",
title = str_wrap("Most Frequent Words in President Obama's Executive Orders", width = 50)
) +
theme_minimal()fulltext_idf <- full_text_tidy %>%
filter(word != "na") %>%
count(title, word, sort = TRUE) %>%
bind_tf_idf(word, title, n)
fulltext_idf %>%
select(title, word, tf_idf) %>%
arrange(desc(tf_idf))# A tibble: 66,736 × 3
title word tf_idf
<chr> <chr> <dbl>
1 Accelerating Improvements in HIV Prevention and Care in the Uni… hiv 0.338
2 Interagency Group on Insular Areas igia 0.321
3 Correction page 0.307
4 Advancing the Global Health Security Agenda To Achieve a World … ghsa 0.296
5 Amendment to Executive Order 11155, Awards for Special Capabili… scho… 0.264
6 Chesapeake Bay Protection and Restoration ches… 0.258
7 President's Council of Advisors on Science and Technology pcast 0.257
8 Establishment of the President's Economic Recovery Advisory Boa… perab 0.251
9 Hostage Recovery Activities host… 0.245
10 Amendments to Executive Order 12777 liab… 0.241
# ℹ 66,726 more rows
#| fig-width: 14
#| fig-height: 14
fulltext_idf %>%
filter(title %in% c("Interagency Group on Insular Areas", "President's Council of Advisors on Science and Technology", "Hostage Recovery Activities", "Northern Bering Sea Climate Resilience", "Safeguarding the Nation From the Impacts of Invasive Species", "Implementing the National HIV/AIDS Strategy for the United States for 2015-2020")) %>%
group_by(title) %>%
arrange(desc(tf_idf)) %>%
top_n(5, tf_idf) %>%
ggplot(aes(x=tf_idf, y=reorder(word, tf_idf), fill = title)) +
geom_col(show.legend = F) +
labs(x="TF_IDF", y= "Word") +
facet_wrap(~title, scales = "free", labeller = label_wrap_gen(width = 17)) +
theme_minimal()eo_structured <- eo_data %>%
select(document_number, title, signing_date, full_text) %>%
filter(!is.na(full_text)) %>%
mutate(
sections = str_split(full_text, "(?=Sec\\.\\s*\\d|Section\\s+\\d)")
) %>%
unnest(sections) %>%
mutate(
section_text = str_trim(sections),
section_num = row_number()
) %>%
filter(section_text != "")eo_bigrams <- eo_data %>%
select(document_number, title, signing_date, full_text) %>%
filter(!is.na(full_text)) %>%
filter(year(signing_date) == 2015) %>%
unnest_tokens(bigram, full_text, token = "ngrams", n = 2)eo_bigrams %>%
count(bigram) %>%
arrange(desc(n))# A tibble: 15,910 × 2
bigram n
<chr> <int>
1 of the 571
2 this order 382
3 of this 238
4 to the 219
5 united states 208
6 the united 179
7 executive order 135
8 by the 118
9 with the 116
10 shall be 99
# ℹ 15,900 more rows
eo_bigrams %>%
drop_na(bigram)# A tibble: 37,665 × 4
document_number title signing_date bigram
<chr> <chr> <date> <chr>
1 2015-05677 Blocking Property and Suspending Entry o… 2015-03-08 title…
2 2015-05677 Blocking Property and Suspending Entry o… 2015-03-08 3 the
3 2015-05677 Blocking Property and Suspending Entry o… 2015-03-08 the p…
4 2015-05677 Blocking Property and Suspending Entry o… 2015-03-08 presi…
5 2015-05677 Blocking Property and Suspending Entry o… 2015-03-08 execu…
6 2015-05677 Blocking Property and Suspending Entry o… 2015-03-08 order…
7 2015-05677 Blocking Property and Suspending Entry o… 2015-03-08 13692…
8 2015-05677 Blocking Property and Suspending Entry o… 2015-03-08 of ma…
9 2015-05677 Blocking Property and Suspending Entry o… 2015-03-08 march…
10 2015-05677 Blocking Property and Suspending Entry o… 2015-03-08 8 201…
# ℹ 37,655 more rows
eo_bigrams <- eo_bigrams %>%
separate(col = bigram,
into = c("word1", "word2"),
sep = " ",
remove = F)
eo_bigrams_stop <- eo_bigrams %>%
filter(!word1 %in% stop_words$word & !word2 %in% stop_words$word) %>%
filter(!str_detect(bigram, "^sec(tion)?\\s+\\d")) %>%
filter(!str_detect(bigram, "^title\\s+\\d")) %>%
filter(!str_detect(word1, "^\\d+$") & !str_detect(word2, "^\\d+$")) %>%
filter(!str_detect(bigram, "^fr\\s+"))
eo_bigrams_stop %>%
count(bigram, sort = T)# A tibble: 4,283 × 2
bigram n
<chr> <int>
1 national security 55
2 billing code 34
3 federal government 32
4 white house 32
5 authority vested 29
6 applicable law 28
7 greenhouse gas 28
8 departments agencies 26
9 homeland security 26
10 steering committee 25
# ℹ 4,273 more rows
eo_bigrams_stop %>%
count(bigram, sort = T) %>%
filter(n>20) %>%
ggplot(aes(x=reorder(bigram, n),
y = n,
fill = n)) +
geom_col(fill = "sienna1") +
labs(x = NULL, y = "frequency",
title = str_wrap("Most Frequent Bigrams in President Obama's 2015 Executive Orders", width = 50)) +
coord_flip() +
theme_minimal()eo_bigrams_stop %>%
filter(str_detect(bigram, "government")) %>%
distinct(bigram)# A tibble: 57 × 1
bigram
<chr>
1 antigovernment protests
2 antigovernment protestors
3 term government
4 government consistent
5 government effort
6 government industry
7 federal government
8 government approach
9 government strategy
10 representing government
# ℹ 47 more rows
eo_bigrams_tfidf <- eo_bigrams_stop %>%
count(document_number, bigram) %>%
bind_tf_idf(bigram, document_number, n)
eo_bigrams_tfidf %>%
arrange(desc(tf_idf))# A tibble: 5,294 × 6
document_number bigram n tf idf tf_idf
<chr> <chr> <int> <dbl> <dbl> <dbl>
1 2015-15828 presidential scholars 2 0.118 3.37 0.396
2 2015-01255 controlled equipment 23 0.0888 3.37 0.299
3 2015-29498 emergency board 7 0.109 2.67 0.292
4 2015-31749 thursday december 3 0.0811 3.37 0.273
5 2015-16334 advisory board 12 0.12 2.27 0.272
6 2015-25744 security medal 7 0.0805 3.37 0.271
7 2015-32582 attached hereto 6 0.08 3.37 0.269
8 2015-15495 courts martial 3 0.0769 3.37 0.259
9 2015-15495 martial united 3 0.0769 3.37 0.259
10 2015-18292 u.s trade 9 0.0744 3.37 0.250
# ℹ 5,284 more rows
set.seed(148)
selected_docs <- sample(unique(eo_bigrams_tfidf$document_number), 8)
eo_bigrams_tfidf %>%
filter(document_number %in% selected_docs) %>%
group_by(document_number) %>%
slice_max(tf_idf, n = 4) %>%
ungroup() %>%
ggplot() +
aes(x = tf_idf,
y = fct_reorder(bigram, tf_idf),
fill = document_number) +
geom_col(show.legend = F) +
facet_wrap(~document_number, scales = "free") +
labs(x = "TF_IDF", y = NULL) +
theme_minimal()eo2015_graph <- eo_bigrams_stop %>%
count(word1, word2) %>%
filter(n > 15) %>%
graph_from_data_frame()
set.seed(1049)
a <- grid::arrow(type = "closed", length = unit(.13, "inches"))
ggraph(eo2015_graph, layout = "fr") +
geom_edge_link(aes(edge_alpha = n),
show.legend = F,
arrow = a, end_cap = circle(.01, "inches")) +
geom_node_point(color = "blue3", size = 3) +
geom_node_text(aes(label = name), vjust = 0.5, hjust = 0.5, repel = T) +
theme_void() +
labs(title = "Bigrams (two-word combinations) in Obama's 2015 Executive Orders")
I chose to look at President Obama’s Executive Orders because last semester I was looking at AI Policy and browsed through a lot of the EOs released by Donald Trump, and I was curious to see the key words and ideas in Obama’s EOs. Executive Orders provide insight into the issues a president chooses to address through unilateral authority. This project analyzes the full set of executive orders issued by President Barack Obama during his two terms, from 2009 to 2017. The Federal Register API served as the primary data source because it provides structured and publicly accessible records of executive actions. While the word frequency and tf-idf analyses use the full corpus, the n-gram analysis focuses on executive orders issued in 2015. This narrower scope provides a snapshot of Obama’s second-term agenda during a period marked by activity in areas such as climate policy, national security, and government administration.
The word frequency analysis highlights the highly procedural nature of executive orders. Terms such as “federal,” “agency,” “secretary,” and “authority” appear frequently across the corpus, reflecting the fact that executive orders are primarily directives to government agencies rather than public-facing political statements. While useful, simple word counts reveal relatively little about the specific policy content of individual orders. Something I found particularly interesting in the analysis was that there was less of an overlap between the most common single words and the bigrams than I anticipated. I was not expecting the word section, because I filtered out all the section combinations, as they were acting as stop words as opposed to content, but I was expecting a greater overlap in some of the other words. For the bigrams, I was surprised that “United States” was not a combination that appeared over 20 times throughout the 2015 EOs, especially considering “united” was the second most used word in all of the EOs. I’m assuming that reference to the nation was done so in a broader way, but it was still interesting to note.
A limitation that I noticed was that when filtering for 2015 EOs, it was more difficult to sort words by their TF-IDF. Since there were so many fewer words compared to the entire dataset, there was less variety in the TF-IDF scores, which made graphing a bit more complicated. Because of this, I am inclined to prioritize larger sets of data because it gives more variety and more concrete analysis. Of course, large datasets have their issues, such as having to manually wade through some of the data, but I found in this assignment that working with the entire corpora was easier than just the 2015 data.
LLM Statement: When scraping the .txt files from the dataset, I was running into issues with Federal Government request access. To figure out how to remedy this, I copy and pasted the request access note and asked ClaudeAI how I could get around this problem. ClaudeAI suggested a for loop and the use of the federal registrar website, as opposed to the dataset itself. ClaudeAI also gave me the code used from lines 25 to 56 to get around the issue. I looked over the code and when I ran it, it worked as intended. I also used ClaudeAI for lines 60 to 64, as I could not figure out how to combine my .txt files into one document to use for tidytext analysis. For this, I told Claude AI my problem and asked for a few different ways to go about combining the files. I decided on the one I ended up using because it was the most straightforward and would be the easiest to use for the purposes of the assignment.