Skip to content

Instantly share code, notes, and snippets.

@acbass49
Created April 15, 2026 23:40
Show Gist options
  • Select an option

  • Save acbass49/6d5ec139e45b281970b990553b1be098 to your computer and use it in GitHub Desktop.

Select an option

Save acbass49/6d5ec139e45b281970b990553b1be098 to your computer and use it in GitHub Desktop.
2025 General Conference Stats Update
library(tidyverse)
library(stringr)
library(scales)
library(ggtext)
library(patchwork)
library(showtext)
font_add("Cairo_bold", "~/Library/Fonts/Cairo-Bold.ttf")
font_add("Cairo", "~/Library/Fonts/Cairo-Regular.ttf")
showtext::showtext_auto(enable = TRUE)
df <- read.csv("./data/membership.csv")
# dataset: https://docs.google.com/spreadsheets/d/1ghU8_9CcaW337iUCMoiFDvPV1SMAQCecu2ZfWrx1y9w/edit?gid=0#gid=0
data <- df[20:(nrow(df)-1), c("Year","Missionaries", "Converts", "Members", "New.Children.of.Record")] |>
mutate(across(everything(), \(x) str_remove_all(x, pattern = fixed(",")))) |>
mutate(across(everything(), as.numeric))
# 2024 actuals (released April 2025 General Conference)
yr_2024 <- data.frame(
Year = 2024,
Missionaries = 74127,
Converts = 308682,
Members = 17509781,
New.Children.of.Record = 91617
)
# 2025 actuals (released April 2026 General Conference)
yr_2025 <- data.frame(
Year = 2025,
Missionaries = 78596, # full-time teaching missionaries
Converts = 385490,
Members = 17887212,
New.Children.of.Record = 91835
)
data <- bind_rows(data, yr_2024, yr_2025) |>
arrange(desc(Year))
# ── Chart 1: Missionaries (p1) + Convert Baptisms (p2) ──────────────────────
(p1 <- data |>
filter(Year >= 1977, !is.na(Missionaries)) |>
ggplot(aes(x = Year, y = Missionaries)) +
geom_line(linewidth = 1.5, color = "grey") +
labs(
title = "<span style='color:grey'>Missionaries</span> Increase",
subtitle = "The missionary count has almost tripled since 1980",
x = "Year",
y = "Missionary Count"
) +
theme(
axis.ticks = element_blank(),
axis.title = element_text(size = 40),
axis.text = element_text(size = 30),
panel.background = element_blank(),
text = element_text(face = "bold", family = "Cairo"),
plot.title = element_markdown(size = 50, family = "Cairo_bold", hjust = 0.5),
plot.background = element_rect(fill = "grey95", color = NA),
plot.subtitle = element_text(hjust = 0.5, face = "italic", size = 40, family = "Cairo"),
panel.grid = element_line(color = "grey95"),
panel.grid.major.y = element_line(color = "lightgrey", linetype = "solid"),
plot.caption = element_text(hjust = 1, size = 33, face = "italic")
) +
scale_x_continuous(breaks = seq(1975, 2025, 5)) +
scale_y_continuous(
breaks = seq(0, 500000, 25000),
labels = scales::comma_format(),
limits = c(0, 100000)
) +
geom_vline(xintercept = 2012) +
geom_text(aes(x = 2008, y = 20000, label = "Age Change ->"), size = 8, family = "Cairo") +
geom_vline(xintercept = 2020) +
geom_text(aes(x = 2016.5, y = 20000, label = "COVID-19 ->"), size = 8, family = "Cairo") +
geom_vline(xintercept = 2025, linetype = "dashed") +
geom_text(aes(x = 2018, y = 95000, label = "Women's Age Change (18) ->"), size = 8, family = "Cairo")
)
(p2 <- data |>
filter(Year >= 1977) |>
ggplot(aes(x = Year, y = Converts)) +
geom_line(linewidth = 1.5, color = "#097969") +
labs(
title = "<span style='color:#097969'>Convert Baptisms</span> Surge in 2025",
subtitle = "385,490 baptisms — a new high",
x = "Year",
y = "Convert Baptisms",
caption = "@mormon_metrics | Source: General Conference"
) +
theme(
axis.ticks = element_blank(),
axis.title = element_text(size = 40),
axis.text = element_text(size = 30),
panel.background = element_blank(),
text = element_text(face = "bold", family = "Cairo"),
plot.title = element_markdown(size = 50, family = "Cairo_bold", hjust = 0.5),
plot.background = element_rect(fill = "grey95", color = NA),
plot.subtitle = element_text(hjust = 0.5, face = "italic", size = 40, family = "Cairo"),
panel.grid = element_line(color = "grey95"),
panel.grid.major.y = element_line(color = "lightgrey", linetype = "solid"),
plot.caption = element_text(hjust = 1, size = 33)
) +
scale_x_continuous(breaks = seq(1975, 2025, 5)) +
scale_y_continuous(
breaks = seq(0, 500000, 100000),
labels = scales::comma_format(),
limits = c(0, 400000)
) +
geom_vline(xintercept = 2012) +
geom_text(aes(x = 2008, y = 50000, label = "Age Change ->"), size = 8, family = "Cairo") +
geom_vline(xintercept = 2020) +
geom_text(aes(x = 2016.5, y = 50000, label = "COVID-19 ->"), size = 8, family = "Cairo") +
geom_vline(xintercept = 2025, linetype = "dashed") +
geom_text(aes(x = 2018, y = 95000, label = "Women's Age Change (18) ->"), size = 8, family = "Cairo")
)
p3 <- p1 / p2
ggsave("./images/32_conference_stats_2025_1.png", p3, width = 1700, height = 2000, units = "px", dpi = 300)
# ── Chart 2: Converts per missionary ────────────────────────────────────────
p4 <- data |>
filter(Year >= 1977, !is.na(Missionaries)) |>
mutate(converts_per_missionary = Converts / Missionaries) |>
ggplot(aes(x = Year, y = converts_per_missionary)) +
geom_point(size = 3, alpha = 0.4, color = "goldenrod3") +
geom_smooth(se = FALSE, color = "goldenrod3", method = "lm") +
labs(
title = "Missionaries Today Have Half The Baptisms As The 80s",
subtitle = "From ~8 converts per missionary in the 1980s; 2025 sees an uptick to ~4.9",
x = "Year",
y = "Converts Per Missionary",
caption = "@mormon_metrics | Source: General Conference"
) +
theme(
axis.ticks = element_blank(),
axis.title = element_text(size = 40),
axis.text = element_text(size = 30),
panel.background = element_blank(),
text = element_text(face = "bold", family = "Cairo"),
plot.title = element_text(hjust = 0.5, size = 50, face = "bold", family = "Cairo_bold"),
plot.background = element_rect(fill = "grey95", color = NA),
plot.subtitle = element_text(hjust = 0.5, face = "italic", size = 40, family = "Cairo"),
panel.grid = element_line(color = "grey95"),
panel.grid.major.y = element_line(color = "lightgrey", linetype = "solid"),
plot.caption = element_text(hjust = 1, size = 33)
) +
scale_x_continuous(breaks = seq(1975, 2025, 5)) +
scale_y_continuous(breaks = seq(0, 10, 2), limits = c(0, 10)) +
geom_vline(xintercept = 2012) +
geom_text(aes(x = 2008, y = 1, label = "Age Change ->"), size = 8, family = "Cairo") +
geom_vline(xintercept = 2020) +
geom_text(aes(x = 2016.5, y = 1, label = "COVID-19 ->"), size = 8, family = "Cairo") +
geom_vline(xintercept = 2025, linetype = "dashed") +
geom_text(aes(x = 2019, y = 8, label = "Women's Age Change (18) ->"), size = 8, family = "Cairo")
ggsave("./images/32_conference_stats_2025_2.png", p4, width = 2000, height = 2000, units = "px", dpi = 300)
# ── Chart 3: Total membership + YoY growth + Children of Record ─────────────
p5 <- data |>
filter(Year >= 1973) |>
ggplot(aes(x = Year, y = Members)) +
geom_line(linewidth = 1.5, color = "#FF7F0E") +
geom_text(
data = data |> filter(Year == 2025) |>
mutate(percent_lbl = format(round(Members), big.mark = ",")),
aes(x = Year, y = Members, label = percent_lbl),
family = "Cairo", fontface = "bold", size = 15,
nudge_x = 4.5, show.legend = FALSE, color = "#FF7F0E"
) +
labs(
title = "Total Church Membership Continues To Grow",
y = "Total Church Membership (Official)",
x = "Year"
) +
theme(
axis.ticks = element_blank(),
axis.title = element_text(size = 40),
axis.text = element_text(size = 30),
panel.background = element_blank(),
text = element_text(face = "bold", family = "Cairo"),
plot.title = element_text(size = 60, face = "bold", family = "Cairo_bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5, face = "italic", size = 45, family = "Cairo"),
legend.title = element_blank(),
legend.background = element_blank(),
plot.background = element_rect(fill = "grey95", color = NA),
legend.box.background = element_blank(),
legend.key = element_blank(),
legend.position = "top",
panel.grid = element_line(color = "grey95"),
panel.grid.major.y = element_line(color = "lightgrey", linetype = "solid"),
plot.caption = element_text(size = 33, face = "italic")
) +
scale_x_continuous(breaks = seq(1970, 2030, 5), limits = c(1970, 2032)) +
scale_y_continuous(
breaks = seq(0, 20000000, 5000000),
limits = c(0, 20000000),
labels = scales::label_number(scale_cut = cut_short_scale())
)
p6_1 <- data |>
filter(Year >= 1973) |>
mutate(
members_lead = lead(Members, 1),
perc_change = round((Members - members_lead) / Members * 100, 2)
) |>
ggplot(aes(x = Year, y = perc_change)) +
geom_point(size = 3, alpha = 0.4, color = "black") +
geom_smooth(se = FALSE, color = "black", method = "lm") +
labs(
title = "YoY Percent Change In Membership Has Dropped Over Time",
subtitle = "2025 shows uptick driven by higher convert baptisms",
y = "YoY Membership Growth % (Official)",
x = "Year"
) +
theme(
axis.ticks = element_blank(),
axis.title = element_text(size = 40),
axis.text = element_text(size = 30),
panel.background = element_blank(),
text = element_text(face = "bold", family = "Cairo"),
plot.title = element_text(size = 60, face = "bold", family = "Cairo_bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5, face = "italic", size = 45, family = "Cairo"),
legend.title = element_blank(),
legend.background = element_blank(),
panel.grid = element_line(color = "grey95"),
plot.background = element_rect(fill = "grey95", color = NA),
legend.box.background = element_blank(),
legend.key = element_blank(),
legend.position = "top",
panel.grid.major.y = element_line(color = "lightgrey", linetype = "solid"),
plot.caption = element_text(size = 33)
) +
scale_x_continuous(breaks = seq(1970, 2030, 5), limits = c(1970, 2030)) +
scale_y_continuous(breaks = seq(1, 10, 1))
p6 <- data |>
filter(Year >= 1973) |>
ggplot(aes(x = Year, y = New.Children.of.Record)) +
geom_line(linewidth = 1.5, color = "#1D71BA") +
geom_text(
data = data |> filter(Year == 2025) |>
mutate(percent_lbl = format(round(New.Children.of.Record), big.mark = ",")),
aes(x = Year, y = New.Children.of.Record, label = percent_lbl),
family = "Cairo", fontface = "bold", size = 15,
nudge_x = 3.5, show.legend = FALSE, color = "#1D71BA"
) +
labs(
title = "Children of Record Births Remain Flat in 2025",
caption = "@mormon_metrics | Source: General Conference",
subtitle = "Children of Record Births Have Flatlined Despite 3X Increase In Membership",
y = "Children of Record Births (Official)",
x = "Year"
) +
theme(
axis.ticks = element_blank(),
axis.title = element_text(size = 40),
axis.text = element_text(size = 30),
panel.background = element_blank(),
text = element_text(face = "bold", family = "Cairo"),
plot.title = element_text(size = 60, face = "bold", family = "Cairo_bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5, face = "italic", size = 45, family = "Cairo"),
legend.title = element_blank(),
legend.background = element_blank(),
panel.grid = element_line(color = "grey95"),
plot.background = element_rect(fill = "grey95", color = NA),
legend.box.background = element_blank(),
legend.key = element_blank(),
legend.position = "top",
panel.grid.major.y = element_line(color = "lightgrey", linetype = "solid"),
plot.caption = element_text(size = 33)
) +
scale_x_continuous(breaks = seq(1970, 2030, 5), limits = c(1970, 2030)) +
scale_y_continuous(
breaks = seq(0, 200000, 25000),
limits = c(0, 200000),
labels = scales::label_number(scale_cut = cut_short_scale())
)
p7 <- (p5 / p6_1 / p6)
ggsave("./images/32_conference_stats_2025_3.png", p7, width = 2500, height = 3000, units = "px", dpi = 300)
# ── Chart 4: Births per 1000 members ────────────────────────────────────────
p8_births <- data |>
filter(Year >= 1973) |>
mutate(birth_rate = New.Children.of.Record / (Members / 1000)) |>
ggplot(aes(x = Year, y = birth_rate)) +
geom_line(linewidth = 1.5, color = "#1D71BA") +
geom_text(
data = data |> filter(Year == 2025) |>
mutate(
birth_rate = New.Children.of.Record / (Members / 1000),
percent_lbl = as.character(round(birth_rate, 2))
),
aes(x = Year, y = birth_rate, label = percent_lbl),
family = "Cairo", fontface = "bold", size = 15,
nudge_x = 1.5, show.legend = FALSE, color = "#1D71BA"
) +
labs(
title = "Births Per 1000 Members Has Dropped Over Time",
caption = "@mormon_metrics | Source: General Conference",
subtitle = "Births per 1000 members is only ~1/5 of what it was in 1980",
y = "Births Per 1000 Members (Official)",
x = "Year"
) +
theme(
axis.ticks = element_blank(),
axis.text = element_text(size = 30),
panel.background = element_blank(),
text = element_text(face = "bold", family = "Cairo"),
plot.title = element_text(size = 60, face = "bold", family = "Cairo_bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5, face = "italic", size = 45, family = "Cairo"),
plot.background = element_rect(fill = "grey95", color = NA),
axis.title = element_text(size = 40, face = "bold"),
legend.title = element_blank(),
legend.background = element_blank(),
legend.box.background = element_blank(),
legend.key = element_blank(),
legend.position = "top",
panel.grid = element_line(color = "grey95"),
panel.grid.major.y = element_line(color = "lightgrey", linetype = "solid"),
plot.caption = element_text(size = 30)
) +
scale_x_continuous(breaks = seq(1970, 2025, 5)) +
scale_y_continuous(breaks = seq(0, 30, 5), limits = c(0, 30))
ggsave("./images/32_conference_stats_2025_4.png", p8_births, width = 2500, height = 2000, units = "px", dpi = 300)
# ── Chart 5: Exits per 1000 members ─────────────────────────────────────────
p8_exits <- data |>
filter(Year >= 2000) |>
mutate(
lead_members = lead(Members, 1),
lost = (Converts + New.Children.of.Record) - (Members - lead_members),
lost_rate = lost / (Members / 1000)
) |>
ggplot(aes(x = Year, y = lost_rate)) +
geom_point(size = 3, alpha = 0.4, color = "#D62728") +
geom_smooth(se = FALSE, color = "#D62728", method = "lm") +
labs(
title = "Exits Per 1000 Members Has Increased Since 2000",
caption = "@mormon_metrics | Source: General Conference",
subtitle = "Exits per 1000 members Has Doubled Since 2000",
y = "Exits Per 1000 Members (Official)",
x = "Year"
) +
theme(
axis.ticks = element_blank(),
axis.text = element_text(size = 30),
panel.background = element_blank(),
text = element_text(face = "bold", family = "Cairo"),
plot.title = element_text(size = 60, face = "bold", family = "Cairo_bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5, face = "italic", size = 45, family = "Cairo"),
plot.background = element_rect(fill = "grey95", color = NA),
axis.title = element_text(size = 40, face = "bold"),
legend.title = element_blank(),
legend.background = element_blank(),
legend.box.background = element_blank(),
legend.key = element_blank(),
legend.position = "top",
panel.grid = element_line(color = "grey95"),
panel.grid.major.y = element_line(color = "lightgrey", linetype = "solid"),
plot.caption = element_text(size = 30)
) +
scale_x_continuous(breaks = seq(2000, 2025, 5)) +
scale_y_continuous(breaks = seq(0, 14, 2), limits = c(0, 15))
ggsave("./images/32_conference_stats_2025_5.png", p8_exits, width = 2000, height = 2000, units = "px", dpi = 300)
# ── Chart 6: Proportion of growth from births vs converts ───────────────────
prop_data <- data |>
filter(Year >= 1977) |>
mutate(
total_joiners = Converts + New.Children.of.Record,
prop_converts = Converts / total_joiners,
prop_births = New.Children.of.Record / total_joiners
)
p_prop <- prop_data |>
select(Year, prop_converts, prop_births) |>
pivot_longer(cols = c(prop_converts, prop_births), names_to = "type", values_to = "prop") |>
mutate(type = factor(type,
levels = c("prop_births", "prop_converts"),
labels = c("Children of Record", "Convert Baptisms")
)) |>
ggplot(aes(x = Year, y = prop, fill = type)) +
geom_col(width = 0.8) +
geom_segment(
data = prop_data,
aes(x = Year - 0.4, xend = Year + 0.4, y = 1-prop_births, yend = 1-prop_births),
inherit.aes = FALSE,
linewidth = 2.2, color = "black"
) +
scale_fill_manual(values = c("Children of Record" = "#1D71BA", "Convert Baptisms" = "#097969")) +
scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
scale_x_continuous(breaks = seq(1975, 2025, 5)) +
labs(
title = "Church Growth Is Increasingly Convert-Driven",
subtitle = "<span style='color:#097969; font-weight:bold'>Convert Baptisms</span> vs <span style='color:#1D71BA; font-weight:bold'>Children of Record</span> as share of new joiners",
caption = "@mormon_metrics | Source: General Conference",
x = "Year",
y = "Share of New Joiners"
) +
theme(
axis.ticks = element_blank(),
axis.title = element_text(size = 40),
axis.text = element_text(size = 30),
panel.background = element_blank(),
text = element_text(face = "bold", family = "Cairo"),
plot.title = element_text(hjust = 0.5, size = 60, face = "bold", family = "Cairo_bold"),
plot.background = element_rect(fill = "grey95", color = NA),
plot.subtitle = element_markdown(hjust = 0.5, face = "italic", size = 45, family = "Cairo_bold"),
panel.grid = element_blank(),
panel.grid.major.y = element_line(color = "lightgrey", linetype = "solid"),
plot.caption = element_text(hjust = 1, size = 30),
legend.position = "none"
)
ggsave("./images/32_conference_stats_2025_6.png", p_prop, width = 2000, height = 2000, units = "px", dpi = 300)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment