Analyzing Brazil’s votes at the UN

tidyverse
ggplot2
a look into Brazil standing at the General Assembly
Published

June 12, 2025

The package unvotes allows us to access voting data from the UN. Today, we will analyse Brazil’s “Yes”, “No” and “Abstain” votes from 1995 to 2023 – a period that saw the country under 5 different presidents representing 5 distinct political parties and covers a significant portion of the country post-dictatorship democratic history. From FHC’s social and economical liberalism, to the rise of Lula’s and the Worker’s Party (PT) to power and the impeachment of his successor, Dilma Rousseff; the short, market-oriented presidency of Michel Temer and finally Bolsonaro’s far-right administration.

Note: the current administration under Lula (which took office in January 2023) is not featured in the package.

Packages

Code
library(pacman)
pacman :: p_load(unvotes, lubridate, tidyverse, ggtext, knitr, 
                 dplyr, scales, tidyr, showtext, kableExtra, DT)

Data

Code
# Load the UN votes data
data("un_votes")
data("un_roll_calls")
data("un_roll_call_issues")

# Define Brazilian presidential administrations
brazil_administrations <- data.frame(
  president = c("FHC I", "FHC II", "Lula I", "Lula II", 
                "Dilma I", "Dilma II", "Temer", "Bolsonaro", "Lula III"),
  start_date = as.Date(c("1995-01-01", "1999-01-01", "2003-01-01", "2007-01-01",
                         "2011-01-01", "2015-01-01", "2016-08-31", "2019-01-01", "2023-01-01")),
  end_date = as.Date(c("1998-12-31", "2002-12-31", "2006-12-31", "2010-12-31",
                       "2014-12-31", "2016-08-30", "2018-12-31", "2022-12-31", "2024-12-31")),
  party = c("PSDB", "PSDB", "PT", "PT", "PT", "PT", "PMDB", "PSL/PL", "PT")
)

# Create Brazil-specific dataset with administrations
brazil_votes <- un_votes %>%
  filter(country == "Brazil") %>%
  left_join(un_roll_calls, by = "rcid") %>%
  left_join(un_roll_call_issues, by = "rcid") %>%
  mutate(year = year(date)) %>%
  # Assign votes to administrations
  rowwise() %>%
  mutate(
    administration = case_when(
      date >= as.Date("1995-01-01") & date <= as.Date("1998-12-31") ~ "FHC I",
      date >= as.Date("1999-01-01") & date <= as.Date("2002-12-31") ~ "FHC II",
      date >= as.Date("2003-01-01") & date <= as.Date("2006-12-31") ~ "Lula I",
      date >= as.Date("2007-01-01") & date <= as.Date("2010-12-31") ~ "Lula II",
      date >= as.Date("2011-01-01") & date <= as.Date("2014-12-31") ~ "Dilma I",
      date >= as.Date("2015-01-01") & date <= as.Date("2016-08-30") ~ "Dilma II",
      date >= as.Date("2016-08-31") & date <= as.Date("2018-12-31") ~ "Temer",
      date >= as.Date("2019-01-01") & date <= as.Date("2022-12-31") ~ "Bolsonaro",
      date >= as.Date("2023-01-01") ~ "Lula III", #not featured on unvotes
      TRUE ~ "Pre-1995"
    )
  ) %>%
  ungroup() %>%
  filter(administration != "Pre-1995") %>%
  mutate(
    party = case_when(
      administration %in% c("FHC I", "FHC II") ~ "PSDB",
      administration %in% c("Lula I", "Lula II", "Dilma I", "Dilma II", "Lula III") ~ "PT",
      administration == "Temer" ~ "PMDB",
      administration == "Bolsonaro" ~ "PSL/PL"
    )
  )

Vote Distribution by Administration

Now that we have our data frame ready, we can look into more specific details. Let’s start with vote distribution by administration.

Code
# Overall vote distribution by administration
vote_summary <- brazil_votes %>%
  filter(!is.na(administration)) %>%
  count(administration, vote, party) %>%
  group_by(administration) %>%
  mutate(
    total_votes = sum(n),
    percentage = n / total_votes * 100
  ) %>%
  ungroup()

vote_summary %>%
  select(administration, party, vote, n, percentage) %>%
  arrange(administration, vote)
# A tibble: 21 × 5
   administration party  vote        n percentage
   <chr>          <chr>  <fct>   <int>      <dbl>
 1 Bolsonaro      PSL/PL yes       101     75.9  
 2 Bolsonaro      PSL/PL abstain    24     18.0  
 3 Bolsonaro      PSL/PL no          8      6.02 
 4 Dilma I        PT     yes       430     93.7  
 5 Dilma I        PT     abstain    26      5.66 
 6 Dilma I        PT     no          3      0.654
 7 Dilma II       PT     yes       103     97.2  
 8 Dilma II       PT     abstain     3      2.83 
 9 FHC I          PSDB   yes       404     93.3  
10 FHC I          PSDB   abstain    29      6.70 
# ℹ 11 more rows

Now, we can visualize it:

Code
#```{r plot-name, fig.width=12, fig.height=8, dpi=300, warning=FALSE, message=FALSE}

font_add_google("Nunito", "nunito", regular.wt = 400, bold.wt = 700)
showtext_auto()
showtext_opts(dpi = 300)

# Create ordered factor for proper chronological display
brazil_votes$administration_ordered <- factor(
  brazil_votes$administration,
  levels = c("FHC I", "FHC II", "Lula I", "Lula II", 
             "Dilma I", "Dilma II", "Temer", "Bolsonaro"))

vote_summary %>%
  mutate(
    administration_ordered = factor(administration, 
                                   levels = c("FHC I", "FHC II", "Lula I", "Lula II","Dilma I", "Dilma II", "Temer", "Bolsonaro"))
  ) %>%
  ggplot(aes(x = administration_ordered, y = percentage, fill = vote)) +
  geom_col(position = "stack", alpha = 0.8) +
  scale_fill_manual(
    values = c("abstain" = "#FFD700", "no" = "red", "yes" = "#2E8B57"),  # Alphabetical order
    labels = c("abstain" = "Abstain", "no" = "No", "yes" = "Yes")
  ) +
  labs(
    title = "Brazil's UN Voting Patterns by Presidential Administration",
    subtitle = "Percentage distribution of Yes, No, and Abstain votes (1995-2023)",
    x = "Presidential Administration",
    y = "Percentage of Votes (%)",
    fill = "Vote Type",
    caption = "Data: {unvotes}"
  ) +
  theme_minimal() +
  theme(
      plot.title = element_text(family = "nunito", size = 12),
      plot.subtitle = element_text(family = "nunito", size = 8, margin = margin(b = .25, unit = "cm")),
      plot.caption = element_text(family = "nunito", size = 8, margin = margin(t = .5, unit = "cm")),
      panel.grid = element_blank(), 
      plot.background = element_rect(fill = "#fefefe", color = NA), 
      plot.margin = margin(c(.5,.5,.5,.5), unit = "cm"),
      axis.text.x = element_text(angle = 45, hjust = 1 )
    )

Vote Distribution by Issue

Now, we will take a look into how Brazil has voted in the main issues brought up at the General Assembly over the years.

Code
table(brazil_votes$issue)

                         Colonialism         Arms control and disarmament 
                                 302                                  538 
                Economic development                         Human rights 
                                 228                                  535 
                Palestinian conflict Nuclear weapons and nuclear material 
                                 458                                  417 

Excluding the NA values, there are six issues featured in our dataset: Economic development, Palestinian conflict, Arms control and disarmament, Human rights, Colonialism and Nuclear weapons and material.

Code
# Examine voting patterns by issue type
issue_summary <- brazil_votes %>%
  filter(!is.na(issue), !is.na(vote)) %>%
  count(administration, issue, vote, party) %>%
  group_by(administration, issue) %>%
  mutate(
    total_issue_votes = sum(n),
    percentage = n / total_issue_votes * 100
  ) %>%
  ungroup()

create_voting_summary <- function(issue_summary) {
  
  # 1. Wide format table showing percentages by administration and issue
  percentage_table <- issue_summary %>%
    select(administration, issue, vote, percentage) %>%
    pivot_wider(names_from = vote, 
                values_from = percentage, 
                values_fill = 0) %>%
    arrange(match(administration, c("FHC I", "FHC II", "Lula I", "Lula II", 
                                   "Dilma I", "Dilma II", "Temer", "Bolsonaro")))
  
  return(percentage_table)
}

voting_summary <- create_voting_summary(issue_summary)

formatted_table <- voting_summary %>%
  kable(digits = 1, 
        caption = "Brazilian UN Assembly Voting Patterns (% by Administration and Issue)",
        col.names = c("Administration", "Issue", "Abstain", "No", "Yes")) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                full_width = FALSE,
                font_size = 12) %>%
  column_spec(1, bold = TRUE) %>%
  column_spec(2, width = "4cm") %>%
  collapse_rows(columns = 1, valign = "top")

formatted_table
Brazilian UN Assembly Voting Patterns (% by Administration and Issue)
Administration Issue Abstain No Yes
FHC I Colonialism 100.0 0.0 0.0
Arms control and disarmament 88.1 11.9 0.0
Economic development 88.9 11.1 0.0
Human rights 95.6 4.4 0.0
Palestinian conflict 91.8 8.2 0.0
Nuclear weapons and nuclear material 92.5 7.5 0.0
FHC II Colonialism 100.0 0.0 0.0
Arms control and disarmament 86.7 13.3 0.0
Economic development 95.0 5.0 0.0
Human rights 91.2 8.8 0.0
Palestinian conflict 95.6 4.4 0.0
Nuclear weapons and nuclear material 87.1 12.9 0.0
Lula I Colonialism 97.8 2.2 0.0
Arms control and disarmament 90.7 9.3 0.0
Economic development 100.0 0.0 0.0
Human rights 76.6 23.4 0.0
Palestinian conflict 100.0 0.0 0.0
Nuclear weapons and nuclear material 92.1 7.9 0.0
Lula II Colonialism 98.1 1.9 0.0
Arms control and disarmament 98.9 1.1 0.0
Economic development 97.7 2.3 0.0
Human rights 82.2 17.8 0.0
Palestinian conflict 100.0 0.0 0.0
Nuclear weapons and nuclear material 98.6 1.4 0.0
Dilma I Colonialism 100.0 0.0 0.0
Arms control and disarmament 94.3 5.7 0.0
Economic development 94.9 5.1 0.0
Human rights 92.0 8.0 0.0
Palestinian conflict 100.0 0.0 0.0
Nuclear weapons and nuclear material 93.9 6.1 0.0
Dilma II Colonialism 100.0 0.0 0.0
Arms control and disarmament 100.0 0.0 0.0
Economic development 100.0 0.0 0.0
Human rights 95.0 5.0 0.0
Palestinian conflict 100.0 0.0 0.0
Nuclear weapons and nuclear material 100.0 0.0 0.0
Temer Colonialism 90.0 10.0 0.0
Arms control and disarmament 93.8 6.2 0.0
Economic development 94.2 3.8 1.9
Human rights 83.1 15.5 1.4
Palestinian conflict 91.8 6.1 2.0
Nuclear weapons and nuclear material 90.9 9.1 0.0
Bolsonaro Colonialism 87.5 12.5 0.0
Arms control and disarmament 87.5 12.5 0.0
Economic development 87.5 12.5 0.0
Human rights 50.0 28.6 21.4
Palestinian conflict 35.7 35.7 28.6
Nuclear weapons and nuclear material 88.2 11.8 0.0