---
author: "2Lt., Mark G. Sheppard, M.A., M.P.P., Ph.D. (2025)""
---

```{r, echo=FALSE, message=FALSE, warning=FALSE, include=FALSE, results='hide'}
# Packages
library(tidyverse)      # For data manipulation and ggplot2 (includes dplyr, ggplot2, and more)
library(readxl)         # For reading Excel files
library(httr)           # For downloading files from the web
library(lubridate)      # For date manipulation
library(ggplot2)        # For data visualization
library(strucchange)    # For structural break analysis
library(fredr)          # For accessing FRED economic data
library(cowplot)        # For combining plots and adding images (ggdraw, draw_image)
library(magick)         # For image reading and manipulation (image_read)

# Set FRED API key
fredr_set_key("6352ad3b393d3ab83709630e61d2b14e")
```

```{r, echo=FALSE, message=FALSE, warning=FALSE, include=FALSE, results='hide'}
# Load data
df <- {tempfile_path <- tempfile(fileext = ".xlsx"); GET("https://www.policyuncertainty.com/media/US_Policy_Uncertainty_Data.xlsx", write_disk(tempfile_path, overwrite = TRUE)); read_xlsx(tempfile_path) %>% mutate(Date = make_date(Year, Month, 1)) %>% select(Date, Three_Component_Index, Policy_Uncertainty_Index = News_Based_Policy_Uncert_Index) %>% na.omit()}
# Clean
setwd("~/Desktop") # For Mac
rm(list = setdiff(ls(), "df"))
cat("\014")     # Clear console

# Check Structural Breaks
break_dates <- df$Date[breakpoints(ts(df$Policy_Uncertainty_Index, start = c(year(min(df$Date)), month(min(df$Date))), frequency = 12) ~ 1)$breakpoints]

# Fetch both series, merge them, and calculate the average
consumer_sentiment <- fredr(series_id = "UMCSENT", observation_start = as.Date("1985-01-01"), observation_end = as.Date("2024-10-01")) %>%
  select(date, UMCSENT = value) %>%
  left_join(
    fredr(series_id = "CSCICP03USM665S", observation_start = as.Date("1985-01-01"), observation_end = as.Date("2024-10-01")) %>%
      select(date, CONCCONF = value),
    by = "date"
  ) %>%
  mutate(Average_Consumer_Sentiment = (UMCSENT * 0.5 + CONCCONF * 0.5))

# Merge consumer sentiment data into df by date
df <- df %>%
  left_join(consumer_sentiment, by = c("Date" = "date"))

# Fit the linear model to get slope and intercept for the trend line
lm_model <- lm(Policy_Uncertainty_Index ~ as.numeric(Date), data = df)
slope <- coef(lm_model)[2]
intercept <- coef(lm_model)[1]
```

Method: method = "loess"

LOESS (Locally Estimated Scatterplot Smoothing) is a non-parametric regression technique. It fits multiple regressions over localized subsets of data, which allows the line to follow the data's shape closely without assuming a strict linear relationship.
LOESS is particularly useful for datasets with non-linear relationships, as it captures fluctuations and trends more flexibly than simple linear regression.

Smoothing Span: span = 0.4

The span parameter controls how much of the data is used to fit each localized regression in the LOESS smoothing.
A smaller span (like 0.4) means the model will focus on smaller portions of data at each step, resulting in a line that is more responsive to small fluctuations in the data.
Increasing the span would create a smoother, less responsive line that averages over larger portions of data.


```{r, warning=FALSE, message=FALSE}
equation_text <- paste0("y = ", round(intercept, 0), " + ", round(slope, 4), "x")
# Define the plot
graph <- ggplot(df, aes(x = Date, y = Policy_Uncertainty_Index)) +
  geom_hline(yintercept = 125, linetype = "solid", color = "grey10", size = 0.3, alpha = 0.2) +
  annotate("text", x = min(df$Date) + 200, y = 130, label = "Average Uncertainty", color = "grey30", size = 2, hjust = 0) +
  
  # Scatter plot for actual data with rescaled bubble size relative to consumer sentiment
  geom_point(aes(size = Average_Consumer_Sentiment^10), color = "#5d90ba", alpha = 0.1, shape = 16) +
  scale_size_continuous(range = c(1, 5)) +  # Rescale bubble size to a range between 1 and 5
  
  # Add a "ball" at the end of the line
  geom_point(data = df %>% filter(Date == max(Date)), 
             aes(x = Date, y = 120), 
             color = "#085fa8", size = 2) +
  
  # Smooth line with higher resolution for finer smoothness
  geom_smooth(method = "loess", se = TRUE, span = 0.15, level = 0.95, color = "#085fa8", fill = "#5d90ba", alpha = 0.3, n = 10000) +
  
  # Regression trendline
  geom_smooth(method = "lm", color = "#085fa8", linetype = "dotted", size = 0.5, se = FALSE) +
  
  # Add regression line equation
  annotate("text", x = as.Date("1994-06-01") + 400, y = 106, label = equation_text, color = "#085fa8", size = 1.85, angle = 8, hjust = 0) +
  
  # Annotations for key dates with vertical lines and labels
  geom_vline(xintercept = as.Date("2016-11-03"), linetype = "dotted", color = "darkgrey", size = 0.3) +
  annotate("text", x = as.Date("2016-11-03"), y = 205,  # Lowered y position
           label = "Election of Donald J. Trump", vjust = -0.5, hjust = 1, color = "grey30", size = 2, angle = 90) +
  
  # Titles and labels
  labs(
    title = "The Uncertain Economy",
    subtitle = "Trends in Economic Policy Uncertainty, represented as LOESS with 95% CI \nScaled by Consumer Sentiment, Elections Highlighted for Reference\nData from 1984 to Present",
    x = NULL,  # Remove x-axis title
    y = "Policy Uncertainty Index",
    caption = "\nSource: Economic Policy Uncertainty Index, University of Michigan, The Conference Board \nNote: Line shows Locally Estimated Scatterplot Smoothing (LOESS) which is a non-parametric regression technique with smoothing span of 0.15. \nWhich fits multiple regressions over localized subsets of data, allowing the line to follow the data closely without assuming a strict linear relationship.\nConsumer sentiment was averaged using an equal weighting of the University of Michigan Consumer Sentiment Index and the Conference Board Consumer Confidence Index. \nAuthor: Mark G. Sheppard"
  ) +
  
  # Minimal theme for cleaner appearance
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold", size = 14, color = "#454545", margin = margin(b = 5)),  # Reduce bottom margin of title
    plot.subtitle = element_text(size = 7, color = "darkgrey", margin = margin(t = -5)),  # Move subtitle closer to title
    
    # Customize x-axis text
    axis.text.x = element_text(angle = 90, hjust = 1, size = 6),  # Shrink x-axis text
    
    # Customize y-axis text and title
    axis.text.y = element_text(size = 6, color = "grey30"),  # Darker y-axis text
    axis.title.y = element_text(size = 8, face = "bold", color = "black"),  # Darker y-axis title
    
    # Customize caption with additional details
    plot.caption = element_text(hjust = 0, size = 6, color = "grey50"),  # Left-align, shrink, and make caption grey
    
    # Remove legend and adjust grid lines
    legend.position = "none",
    panel.grid.major.y = element_line(linetype = "dotted", color = "grey95"),  # Make horizontal lines more transparent
    panel.grid.major.x = element_blank(),
    panel.grid.minor = element_blank()
  ) +
  
  # Custom x-axis scale and y-axis limits with data range
  scale_x_date(limits = range(df$Date), date_labels = "%Y", date_breaks = "12 months") +
  scale_y_continuous(limits = c(50, 210), breaks = seq(50, 210, by = 25))  # Set y-axis breaks every 50 and limits

# Overlay both external legend images
ggdraw(graph) + 
  draw_image(image_read("https://raw.githubusercontent.com/markgsheppard/OnlineResources/refs/heads/main/UnceraintyLegend.jpg"), 
             x = 0.7, y = 0.84, width = 0.28, height = 0.15) +
  draw_image(image_read("https://raw.githubusercontent.com/markgsheppard/OnlineResources/refs/heads/main/UnceraintyLegendBall.png"), 
             x = 0.88, y = 0.425, width = 0.15, height = 0.15)  # Position on the far right
```