Public Trust in Government Hits Historic Lows

2023

Using Pew Research data to visualize the evolution of public trust towards different U.S. governments between 1972 and 2023.

Ana Pérez-Barrera
2024-01-19

Introduction

According to the OECD (2013), Public Trust in Government represents the confidence of citizens and businesses in the actions of government to do what is right and perceived as fair. While levels of trust in institutions vary significantly across countries, opinion surveys suggest that there has been a decline in trust in public institutions in recent decades (United Nations, 2021). For instance, in the United States, overall trust in the national government has declined from 73% in 1958 to 24% in 2021. Western Europe has seen a similar steady decline in public trust since the 1970s.

Why should we care about public trust? Firstly, it is a fundamental indicator upon which the legitimacy and sustainability of political systems are built. Secondly, greater public trust has been found to improve compliance in regulations and tax collections, as well as to be a crucial determinant for successful government responses. Finally, increasing concerns suggest that the crisis of public confidence may contribute to support for extreme political views, rising public discontent, protests and, in some cases, violent conflict.

Looking closely at the U.S.

The Pew Research Center has published an article showing the evolution of trust along 4 ideological lines between 1972 and 2023 in the U.S.: Public Trust in Government: 1958-2023 (2023). The report includes data on different trends in trust among party members, races or ideological leanings. This post will be based on the following graph by party and ideology:

Public Trust in Government: 1958-2023 (Pew Research Center, 2023).

In line with the Pew Research Center findings, the U.S. is currently experiencing historic lows in levels of public trust. Consequently, fewer than two-in-ten Americans say they trust the government in Washington to do what is right “just about always” (1%) or “most of the time” (15%). This is among the lowest trust measures in nearly seven decades of polling.

The following ideas are extracted from the article:

The aim of this post is to replicate the original graph, while proposing an alternative form of visualization that illustrates the main takeaways.

Cleaning the data

Data on surveys was obtained through different sources, such as the National Election Studies, Gallup, ABC/Washington Post, CBS/New York Times, CNN Polls, and Pew Research Center (from 2020 onward, data is collected through Pew Research Center’s online American Trends Panel; whereas data before 2020 was obtained through traditional telephone surveys). This data is available in csv format and consists on 134 observations representing public trust levels over time among 4 ideological lines.

data <-
  read_csv(file = "chart-export-6dcaa713-b94d-4cb5-b91e-e8d433400438.csv",
           skip = 3, quote = "\\.") |>
  mutate(Date = mdy(str_remove_all(Date, "\t|\"")),
         `Conservative Rep/Lean Rep` =
           as.numeric(str_remove_all(`Conservative Rep/Lean Rep`, '"'))) |>
  rename(pollster = .) 

data<-na.omit(data)

colnames(data)[3] <- "Liberal_Dem"
colnames(data)[4] <- "Cons_Dem"
colnames(data)[5] <- "Liberal_Rep"
colnames(data)[6] <- "Cons_Rep"

summary(data)
      Date              pollster          Liberal_Dem   
 Min.   :1972-10-15   Length:134         Min.   : 8.00  
 1st Qu.:1991-03-02   Class :character   1st Qu.:24.25  
 Median :1999-02-08   Mode  :character   Median :30.00  
 Mean   :1999-05-25                      Mean   :29.61  
 3rd Qu.:2009-04-29                      3rd Qu.:36.00  
 Max.   :2023-06-11                      Max.   :58.00  
    Cons_Dem      Liberal_Rep       Cons_Rep    
 Min.   :12.00   Min.   : 9.00   Min.   : 4.00  
 1st Qu.:26.00   1st Qu.:22.00   1st Qu.:15.25  
 Median :32.00   Median :31.00   Median :27.00  
 Mean   :31.69   Mean   :34.81   Mean   :30.49  
 3rd Qu.:38.00   3rd Qu.:50.00   3rd Qu.:46.75  
 Max.   :56.00   Max.   :66.00   Max.   :69.00  

Building the chart

The background

Since the background of the graph is divided by presidential terms, the first step implies creating a vector of date strings for the start and end dates. I firstly established the intervals for the terms of Presidents Ford, Reagan, Clinton, Obama, and Biden. These intervals are then organized into a data frame named colored. Additionally, a vector assigns distinct colors to each president name, which will be later used for the annotations.

Similarly, I created a second set of intervals for Presidents Nixon, Carter, H.W. Bush, W. Bush, and Trump, saving them into a data frame named white. These are later used for coloring specific background areas of the plot.

colored_xmin <- c("1974-12-01", "1980-11-07", "1993-01-17",  
                  "2008-12-21","2021-04-11")
colored_xmax <- c("1977-04-25", "1989-01-15","2001-01-17", 
                  "2017-04-11","2023-06-11")
colored <- data.frame(xmin = as.Date(colored_xmin),
                        xmax = as.Date(colored_xmax),
                        interval = c("Ford","Reagan",
                                    "Clinton", "Obama","Biden" ))
colored_interval <-  c("Ford" = "#C81518", "Reagan" = "#C81518",
                         "Clinton" = "#2E5A87", "Obama" = "#2E5A87", 
                         "Biden" = "#2E5A87")
  
white_xmin <- c("1972-10-15", "1977-04-25", "1989-01-15",
                "2001-01-17","2017-04-11")
white_xmax <- c("1974-12-01","1980-11-07","1993-01-17",
                  "2008-12-21","2021-04-11")
white <- data.frame(xmin = as.Date(white_xmin),
                      xmax = as.Date(white_xmax),
                      interval = c("Nixon", "Carter","H.W.Bush", 
                                   "W.Bush", "Trump"))
white_interval <- c("Nixon" = "#C81518","Carter" = "#2E5A87",
                      "H.W.Bush" = "#C81518","W.Bush" = "#C81518",
                      "Trump" = "#C81518")

Subsequently, the geom_rect function applies a rectangular shape using the intervals defined in the colored data frame. This is necessary to ensure that the rectangles do not overlap on the lines.

Plot <- ggplot(data, aes(x = Date)) +
  geom_rect(aes(xmin = xmin, xmax = xmax, ymin = 0, ymax = 100),
            data = colored,
            fill = "#E8e8da", 
            alpha = 0.3,
            inherit.aes = FALSE, na.rm = TRUE) +
  guides(fill = FALSE)  +
  theme_minimal()

Plot

Let’s fill it

Each geom_line function is used to plot a line for a specific variable (Liberal Democrat, Cons-Moderate Democrat, Moderate-Lib Republican, and Conservative Republican) to represent the evolution of trust across ideological leanings.

Plot <- Plot +
   
  geom_line(aes(y = Liberal_Dem, color = "Liberal Dem/Lean Dem"), 
             linetype="solid", size=1.75, 
             alpha = 1, linejoin = "mitre", lineend= "round") +
  geom_line(aes(y = Cons_Dem, color = "Cons-Moderate Dem/Lean Dem"), 
            linetype="solid", size=1.75, 
            alpha = 1, linejoin = "mitre", lineend= "round") +
  geom_line(aes(y = Liberal_Rep, color = "Moderate-Lib Rep/Lean Rep"), 
            linetype="solid", size=1.75, 
            alpha = 1, linejoin = "mitre", lineend= "round") +
  geom_line(aes(y = Cons_Rep, color = "Conservative Rep/Lean Rep"), 
            linetype="solid", size=1.75, 
            alpha = 1, linejoin = "mitre", lineend= "round") +
  scale_color_manual(values = c("Conservative Rep/Lean Rep" = "#BF3B27",
                                "Liberal Dem/Lean Dem" = "#456A83",
                                "Cons-Moderate Dem/Lean Dem" = "#82A6BF",
                                "Moderate-Lib Rep/Lean Rep" = "#E37F73"),
                     breaks = c("Conservative Rep/Lean Rep", 
                                "Moderate-Lib Rep/Lean Rep", 
                                "Cons-Moderate Dem/Lean Dem", 
                                "Liberal Dem/Lean Dem"),
                     name= "") +
  scale_y_continuous(limits = c(0, 100), 
                     breaks = seq(0, 100,by = 20),
                     expand = expansion(0)) + 
  scale_x_date(date_labels = "%Y",
               limits = c(as.Date("1972-10-15"),
                          max(data$Date)),
                breaks = seq(as.Date("1975-01-01"),  
                            max(data$Date), by = "5 years"),
               expand = expansion(0)) + 
  geom_vline(xintercept = as.Date("1972-10-15"), 
            color = "darkgrey", 
            size =0.5) 
 
Plot

Adding text

Plot <- Plot +
  labs(title = "Trust in government by party and ideology",
       subtitle = "% who say they trust the government to do what is right just about always/most of the time",
       caption = "PEW RESEARCH CENTER",
        x = "",
        y = "") 

Plot

Annotations

In order to include the names of the Presidents at the top of the chart, I used the annotate function based on the colored and white data frames initially created. Here, I indicated the intervals for the presidential terms and the assigned colors depending on the political party. The text is then rotated in -90 degrees to place the names vertically.

Plot <- Plot + annotate("text", x = as.Date(colored_xmin) + 
                            (as.Date(colored_xmax) -
                               as.Date(colored_xmin))/7, 
                        y = 94, 
                        label = colored$interval, 
                        size = 4, 
                        angle = -90,  
                        color = colored_interval[colored$interval])
  
Plot <- Plot + annotate("text", x = as.Date(white_xmin) +
                            (as.Date(white_xmax) - 
                               as.Date(white_xmin))/7, 
                         y = 94, 
                        label = white$interval, 
                        angle = -90, 
                        color = white_interval[white$interval])
Plot

Defining the theme

Google fonts

I downloaded the specific types of font from Google Fonts and added them to my library. This will be later used within the theme to achieve an accurate replication of the overall graph.

library(showtext)
library(sysfonts)
library(showtextdb)

sysfonts::font_add_google("Libre Franklin", family="franklin")
showtext::showtext_auto()
sysfonts::font_add_google("Playfair Display", family="playfair")
showtext::showtext_auto()
sysfonts::font_add_google("Lato", family="lato")
showtext::showtext_auto()
sysfonts::font_add_google("Almarai", family="almarai")

Arranging the final plot

Ultimately, I still needed to make some changes on the final theme. First of all, to place the legend where is originally located in the bottom, I used the argument legend.position. Subsequently, I indicated the specific attributes of the letter with legend.text. Then, I modified the axis attributes (line, text and ticks). The specifications are mostly based on the color and the size. I also had to set the margins for the axis.text.x.

On the other hand, I set the panel.grid as element_blank () to remove the grid lines from the plot. Finally, I specified the font, the color, the margins, and the size of the element_text for the title, subtitle and caption elements.

Plot <- Plot +
   
   theme (legend.position = "bottom",
          legend.key.height = unit(0.01, "cm"), 
          legend.key.width = unit(1, "lines"), 
          legend.text = element_text(family = "almarai", size = 16, 
                                      face = "plain", color ="#4b4647"),
          axis.line.x =  element_line(color = "#696969",size = 0.7),
          axis.line.y =  element_line(color = "#a1a1a1",  size = 0.5),
          axis.text.y = element_text(margin = margin(r=0),size=16, 
                                   colour = "#696969"),
          axis.text.x = element_text(margin = margin(t=7), size=16, 
                                   colour = "#696969"),
          axis.ticks.x = element_line(size = 0.7, color = "#696969"), 
          axis.ticks.y = element_line(size =0.7,color= "#696969"),
          axis.ticks.length.x = unit(0.15, "cm"),
          axis.ticks.length.y = unit(0.15, "cm"), 
          panel.grid = element_blank(),
          panel.grid.major.y = element_line(color = "#E5E5E5"),
          plot.margin = margin(t=5,b=5,r=5,l=5), 
          plot.title = element_text( face = "bold", family = "franklin", 
                                    size = 22, hjust = -0.04), 
          plot.subtitle = element_text(face = "italic",colour = "#8d8f8e",
                                      family = "playfair", margin = margin
                                     (t = 9, b= 30, l=),
                                     size = 17, hjust = -.05),
          plot.caption = element_text(face = "bold",hjust = 0,
                                     margin = margin ( 2,  0, 0, 1),
                                     family = "lato", size = 14)) 
 
 Plot <- Plot + theme(aspect.ratio = 1/3.3)
 Plot

Alternative visualization

My purpose here is to present an alternative form of visualization to the previous graph. However, I want to ensure that key elements of the original plot remain recognizable in order to facilitate a straightforward comparison. The ultimate goal is to enhance the interpretation and ensure that the information is better presented through data visualisation.

My first thought when analyzing the original graph was that there was a lot of noise concentrated in the centre of the graph, which resulted in visual confusion and hindered a clear understanding of the message. Has public trust been declining? Is there a difference among ideologies? Where is this difference? Let’s improve it!

Managing the data

I believe that creating smoother evolution lines would help to avoid noise within the graph. For that purpose, I had to reshape the data (tidy_data) using the pivot_longer function. Then, I calculated the average trust for each combination of year and ideology using the group_by and summarise functions. The resulting data-frame has columns for “year,” “ideology,” and “mean_cases,” which shows the rounded average of public trust.

tidy_data <- data |> 
  pivot_longer(cols="Liberal_Dem":"Cons_Rep", 
               names_to = "ideology",values_to = "cases") |> 
  
  group_by(year = lubridate::year(Date), ideology) |> 
  summarise(mean_cases = round(mean(cases, na.rm = TRUE)))
  tidy_data$year <- as.numeric(tidy_data$year)
  
glimpse(tidy_data)
Rows: 184
Columns: 3
Groups: year [46]
$ year       <dbl> 1972, 1972, 1972, 1972, 1974, 1974, 1974, 1974, 1…
$ ideology   <chr> "Cons_Dem", "Cons_Rep", "Liberal_Dem", "Liberal_R…
$ mean_cases <dbl> 53, 66, 44, 62, 40, 40, 36, 39, 37, 40, 32, 44, 4…

Separate plots!

I believe that the original graph has room for improvement, whether through simplification, by emphasizing key data points, or reducing visual clutter.Therefore, I used the facet_wrap function to visualize each ideological category individually. Subsequently, the geom_line function plots a line to represent the evolution of each ideology, with different colors assigned.

The geom_hline draws a horizontal dashed line at y = 60. The aim of highlighting only this line is to emphasize when public trust reached levels around 60%. Finally, gghighlight is used to display the other ideologies in the background, but nicely faded out. This is included to enhance the visual aesthetics of the plot. The plots are previously arranged to facilitate the comparison among ideologies in the final visualization chart.

  tidy_data <- tidy_data |>
  mutate(ideology = factor(ideology, 
                           levels = c("Liberal_Rep", 
                                      "Cons_Rep", 
                                      "Cons_Dem",
                                      "Liberal_Dem"))) |> 
  arrange(ideology, year)
  
  Plot2 <- ggplot(tidy_data, aes(x = year)) +
  geom_hline(yintercept =  60,
             linetype= "dashed", 
             size=.3, 
             color="#696969") +
  geom_line(aes(x=year, y=mean_cases, color=ideology),
            linetype="solid", 
            size=1.3, alpha = 1, 
            linejoin = "round", lineend= "round") +
  gghighlight(use_direct_label = FALSE, 
              unhighlighted_params = list(colour = alpha("white", .8))) +
  facet_wrap(~ideology, labeller = labeller
              (ideology = c ("Liberal_Rep" = "Moderate Republican",
                             "Liberal_Dem" = "Liberal Democrat",
                             "Cons_Dem" = "Conservative Democrat",    
                             "Cons_Rep" ="Conservative Republican")))  +
  scale_color_manual(values = c("Cons_Rep" = "#BF3B27",
                                "Liberal_Dem" = "#456A83",
                                "Cons_Dem" = "#82A6BF",
                                "Liberal_Rep" = "#E37F73")) +
  scale_x_continuous(breaks = seq(min(tidy_data$year), 
                                 max(tidy_data$year), by = 10)) + 
  scale_y_continuous(breaks = c(0,20,40,60,80,100),
                     labels = c("", "20","40","60","","")) +
  theme_minimal( ) +
  labs(title = "Trust in government by party and ideology",
       
       subtitle = "% who say they trust the government to do what is right just about always/most of the time")
  
  Plot2 

Key data points

The story I want to tell is that public trust has declined over time, reaching historic lows in the recent history of the U.S. For that purpose, i used both functions geom_point and geom_text to highlight the data points where public trust reached both its highest and lowest levels.

This way, the message is clear: public trust has indeed declined in recent years. In three of the four ideological leans, the maximum level of trust was only reached in 1972, i.e. 50 years ago. In the case of the liberal democrats, the maximum was reached in the begining of the 2000s. All four ideological leans tend to share a decline in public trust that seems to worsen from 2020 onward. A crisis in public trust!

  Plot2 <- Plot2 +
  geom_point(data = tidy_data  |> 
               group_by(ideology) |> 
               slice(which.min(mean_cases)),
             aes(x = year, y = mean_cases,
                 color = ideology),
             shape = 16,size = 3) +
  geom_text(data = tidy_data |> 
               group_by(ideology)  |> 
               slice(which.min(mean_cases)),
             aes(x = year, y = mean_cases, 
                 color = ideology, 
                 label = mean_cases),
             hjust = -1, vjust = 1, size= 5,  fontface="bold")  +
  geom_point(data = tidy_data |> 
                 group_by(ideology) |> 
                 slice(which.max(mean_cases)),
               aes(x = year, y = mean_cases,
                   color = ideology),
               shape = 16,size = 3) +
  geom_text(data = tidy_data |>
               group_by(ideology) |> 
                slice(which.max(mean_cases)),
              aes(x = year, y = mean_cases, 
                  color = ideology,
                  label = mean_cases),
              hjust = -.7, vjust = 0, size= 5,  fontface="bold")

  Plot2

Final theme

The main inspiration for the final theme was based on the Consumer Confidence Around the World chart from the R Gallery. Within the theme, the strip.text.x function adjusts the appearance of the text in the facet labels along the x-axis, the legend is removed, the axis.text adjusts the appearance of the y and x axis text, including margin, size, and color; plot.background sets the color of the background, and plot.(sub)title adjusts the appearance of both the title and subtitle, including font face, color, margin, and size.

As a result, a cleaner theme is achieved, where unnecessary noise is removed and only the key elements are maintained to tell the story that public trust is reaching historic lows. Moreover, the evolution of the target ideology is more obvious and it is possible to put in perspective with other ideologies.

  Plot2 <- Plot2 + 
  theme(axis.title =element_blank(), 
        strip.text.x = element_text(face="bold",size=15, ),
        legend.position = "none",
        axis.text.y = element_text(margin = margin(r=0),size=12, 
                                   colour = "#696969"),
        axis.text.x = element_text(margin = margin(t=7),size=12, 
                                   colour = "#696969"),
        plot.background = element_rect(color="#F4F5F1",
                                       fill="#F4F5F1"),
        plot.margin = margin(10,10,10,10),
        plot.title = element_text(face = "bold", family = "franklin", 
                                  size = 22),
        plot.subtitle = element_text(face = "bold",colour = "#8d8f8e",  
                                     margin = margin(t = 8, b= 30), 
                                     size =17)) 

  Plot2

Conclusions

The final visualization allows to extract some information that was perhaps not so obvious in the original graph. The main conclusions I draw are as follows:

Limitations

The main limitation of the improved chart is that some information from the original chart has been removed to maintain a clean theme. For example, the presidential terms and the names of the presidents, which allowed to track trust according to who was in power. On the other hand, including additional information on crucial events in history would have helped to contextualize such evolution, like the Vietnam War, the 9/11 terrorist attacks or the COVID-19 pandemic. In this case, I have opted for a clean theme following the less is more norm.

Bibliography

Bell, P. (2023). Public Trust in Government: 1958-2023 | Pew Research Center. Pew Research Center - U.S. Politics & Policy. https://www.pewresearch.org/politics/2023/09/19/public-trust-in-government-1958-2023/

OECD (2013), “Trust in government”, in Government at a Glance 2013, OECD Publishing, Paris. DOI: https://doi.org/10.1787/gov_glance-2013-7-en

Perry, J. (2021). Trust in public institutions: Trends and implications for economic security | Division for Inclusive Social Development - U.N. https://www.un.org/development/desa/dspd/2021/07/trust-public-institutions/

Reuse

Text and figures are licensed under Creative Commons Attribution CC BY 4.0. The figures that have been reused from other sources don't fall under this license and can be recognized by a note in their caption: "Figure from ...".

Citation

For attribution, please cite this work as

Pérez-Barrera (2024, Jan. 19). Data visualization | MSc CSS: Public Trust in Government Hits Historic Lows. Retrieved from https://csslab.uc3m.es/dataviz/projects/2023/100507931/

BibTeX citation

@misc{pérez-barrera2024public,
  author = {Pérez-Barrera, Ana},
  title = {Data visualization | MSc CSS: Public Trust in Government Hits Historic Lows},
  url = {https://csslab.uc3m.es/dataviz/projects/2023/100507931/},
  year = {2024}
}