This a summary of the South African Reserve Bank’s Monetary Policy Committe (MPC) statement using bigrams. The statement is avialable from: https://www.resbank.co.za/en/home/publications/statements/mpc-statements
The statement highlights that global economic conditions are uncertain despite easing headline inflation. In addition, it notes that advanced and emerging economies anticipate modest growth, prioritizing inflation targets, fiscal deficits, and debt reduction. Geo-political tensions and climate change add to the uncertainty, impacting supply chains and prices. South Africa’s GDP growth is sluggish due to port, rail, and electricity constraints. Despite some improvements, inflation risks persist, especially regarding fuel and food prices. Global financial conditions are tight, raising risks for economies needing foreign capital like South Africa. Inflation expectations remain high, prompting the MPC to maintain a restrictive policy stance.The Committee recommends strengthening economic conditions by managing debt, enhancing energy supply, and maintaining stable wage growth. Monetary policy effectiveness is crucial for broader economic stability amidst ongoing uncertainties
A bigram network analysis enables us to elucidate the key details of the monetary policy statement.
# loading libraries
library(tidytext)
library(janeaustenr)
library(readr)
library(dplyr)
##
## 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
# loading the monetary policy statement text file, setting a two-word bigram and and removing stopwords
text <- read_csv("mpc_25012024.csv")
## Rows: 1 Columns: 2
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (2): admin1, notes
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
mpc_bigrams <- text %>%
unnest_tokens(bigram, notes, token = "ngrams", n = 2)
# cleaning the text file to remove the for stop words
library(tidyr)
bigrams_separated <- mpc_bigrams %>%
separate(bigram, c("word1", "word2"), sep = " ")
bigrams_filtered <- bigrams_separated %>%
filter(!word1 %in% stop_words$word) %>%
filter(!word2 %in% stop_words$word)
After removing the stopwords, the new MPC bigrams are now as follows:
bigram_counts <- bigrams_filtered %>%
count(word1, word2, sort = TRUE)
bigram_counts
## # A tibble: 281 × 3
## word1 word2 n
## <chr> <chr> <int>
## 1 price inflation 6
## 2 inflation expectations 5
## 3 south africa 5
## 4 core inflation 4
## 5 headline inflation 4
## 6 commodity export 3
## 7 economic outlook 3
## 8 export prices 3
## 9 food price 3
## 10 inflation remains 3
## # ℹ 271 more rows
Using tidyr’s unite() function whic is the opposite of separate() combines the word1 and word2 columns into one. In other analyses, working with recombined words is better; the one-bigram-per-row format is helpful for exploratory analyses of the text.
From the bigrams above, it was evident that inflation was common in the top 5 bigrams. The following unites the bigrams and filters for “inflation”
bigrams_united <- bigrams_filtered %>%
unite(bigram, word1, word2, sep = " ")
bigrams_united
## # A tibble: 332 × 2
## admin1 bigram
## <chr> <chr>
## 1 mpc_jan2024 begins global
## 2 mpc_jan2024 global economic
## 3 mpc_jan2024 economic conditions
## 4 mpc_jan2024 conditions remain
## 5 mpc_jan2024 remain mixed
## 6 mpc_jan2024 outlook uncertain
## 7 mpc_jan2024 headline inflation
## 8 mpc_jan2024 inflation continues
## 9 mpc_jan2024 world core
## 10 mpc_jan2024 core inflation
## # ℹ 322 more rows
bigrams_filtered %>%
filter(word1 == "inflation") %>%
count(word1, word2, sort = TRUE)
## # A tibble: 15 × 3
## word1 word2 n
## <chr> <chr> <int>
## 1 inflation expectations 5
## 2 inflation remains 3
## 3 inflation forecast 2
## 4 inflation rate 2
## 5 inflation continues 1
## 6 inflation increased 1
## 7 inflation low 1
## 8 inflation outlook 1
## 9 inflation remain 1
## 10 inflation remaining 1
## 11 inflation risk 1
## 12 inflation risks 1
## 13 inflation south 1
## 14 inflation targets 1
## 15 inflation trajectory 1
Text analysis on bigrams within the dataset bigrams_united, computes their TF-IDF scores within each administrative region, and then arranges them in descending order based on their importance.
bigram_tf_idf <- bigrams_united %>%
count(admin1, bigram) %>%
bind_tf_idf(bigram, admin1, n) %>%
arrange(desc(tf_idf))
bigram_tf_idf
## # A tibble: 281 × 6
## admin1 bigram n tf idf tf_idf
## <chr> <chr> <int> <dbl> <dbl> <dbl>
## 1 mpc_jan2024 1.1 average 1 0.00301 0 0
## 2 mpc_jan2024 1.3 rising 1 0.00301 0 0
## 3 mpc_jan2024 2022 reducing 1 0.00301 0 0
## 4 mpc_jan2024 2023 medium 1 0.00301 0 0
## 5 mpc_jan2024 2024 achieving 1 0.00301 0 0
## 6 mpc_jan2024 2024 compared 1 0.00301 0 0
## 7 mpc_jan2024 2024 food 1 0.00301 0 0
## 8 mpc_jan2024 2024 services 1 0.00301 0 0
## 9 mpc_jan2024 2026 services 1 0.00301 0 0
## 10 mpc_jan2024 4.8 core 1 0.00301 0 0
## # ℹ 271 more rows
Visualising the bigram analysis on an a graph using
igraph begins with setting the minimum number of
combinations to 2 (n = 2) [or as required]:
## Visualizing a network of bigrams with ggraph
library(igraph)
##
## Attaching package: 'igraph'
## The following object is masked from 'package:tidyr':
##
## crossing
## The following objects are masked from 'package:dplyr':
##
## as_data_frame, groups, union
## The following objects are masked from 'package:stats':
##
## decompose, spectrum
## The following object is masked from 'package:base':
##
## union
bigram_graph <- bigram_counts %>%
filter(n >= 2) %>%
graph_from_data_frame()
bigram_graph
## IGRAPH dc5979d DN-- 43 32 --
## + attr: name (v/c), n (e/n)
## + edges from dc5979d (vertex names):
## [1] price ->inflation inflation->expectations south ->africa
## [4] core ->inflation headline ->inflation commodity->export
## [7] economic ->outlook export ->prices food ->price
## [10] inflation->remains advanced ->economies average ->salaries
## [13] economic ->conditions economic ->growth forecast ->period
## [16] gdp ->growth gdp ->outcome geo ->political
## [19] global ->growth growth ->forecast inflation->forecast
## [22] inflation->rate medium ->term modest ->economic
## + ... omitted several edges
Plotting the bigram graph using ggraph
library(ggraph)
## Loading required package: ggplot2
set.seed(2017)
ggraph(bigram_graph, layout = "fr") +
geom_edge_link() +
geom_node_point() + theme_bw() +
geom_node_text(aes(label = name), vjust = 1, hjust = 0.45)
## Warning: Using the `size` aesthetic in this geom was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` in the `default_aes` field and elsewhere instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
In line with the bigram_counts from above, the word
relationships from the ggraph shows inflation being the
main centre node. additionally, economic growth was a commoth theme of
the staement.
While the network showed the links and centre nodes of the MPC statement, additional information about the direction of relationship and the number of times the bigrams were counted aids the analysis.
set.seed(2020)
a <- grid::arrow(type = "closed", length = unit(.15, "inches"))
ggraph(bigram_graph, layout = "fr") +
geom_edge_link(aes(edge_alpha = n), show.legend = TRUE,
arrow = a, end_cap = circle(.07, 'inches')) +
geom_node_point(color = "lightgreen", size = 4) +
geom_node_text(aes(label = name), vjust = 1, hjust = 0.45) +
theme_void()
As already established, inflation was the central theme its outlooked remained sensitive: “price inflation” was mentioned 6 times and “inflation expectations” five times. This suggested that forecasted inflation remained a concern for the MPC and this was compounded by global economic uncertainty. The bigram shows that the food inflation and wage growth were concern and inform the MPC to maintain a restrictive policy stance by keeping interest rates elevated.
A quick wordcloud plot of the MPC statement complements
the bigram.
#install libraries
library("tm") #load library
## Loading required package: NLP
##
## Attaching package: 'NLP'
## The following object is masked from 'package:ggplot2':
##
## annotate
library("SnowballC") #load library
library("wordcloud") #load library
## Loading required package: RColorBrewer
library("RColorBrewer") #load library
library(readr)
text_data <- read_lines("mpc_25012024")
docs <- Corpus(VectorSource(text_data))
docs <- tm_map(docs, removeWords, stopwords("english"))
## Warning in tm_map.SimpleCorpus(docs, removeWords, stopwords("english")):
## transformation drops documents
docs <- tm_map(docs, removePunctuation)
## Warning in tm_map.SimpleCorpus(docs, removePunctuation): transformation drops
## documents
#create the document matrix (table containing the word frequency)
dtm <- TermDocumentMatrix(docs)
m <- as.matrix(dtm)
v <- sort(rowSums(m),decreasing = TRUE)
d <- data.frame(word = names(v),freq = v)
set.seed(1234)
wordcloud(words = d$word, freq = d$freq, min.freq = 3,
max.words = 100, random.order = FALSE, rot.per = -0.5,
colors = brewer.pal(8, "Dark2"),
scale = c(3, 0.5))
Julia Silge & David Robinson (2024). “Text Mining with R: A Tidy Approach”. Available from: https://www.tidytextmining.com/ngrams