This report replicates the Excel EDA pipeline for FIFA Men’s World Cup match attendance data spanning all 23 tournaments (1930–2026). The analysis mirrors three steps performed in Excel:
The raw dataset covers all World Cup matches from 1930 through 2022. Eight columns relevant to the attendance analysis are selected, replicating the EDADATA_ATTEND sheet.
df_raw <- read_csv("matches_1930_2022.csv", show_col_types = FALSE)
df_eda <- df_raw |>
select(Host, Year, Attendance, home_team, away_team, Score, Round, Notes)The raw data contains 964 matches across 44 columns. After selecting the 8 key columns, the working dataset has 964 rows.
## Rows: 964
## Columns: 8
## $ Host <chr> "Qatar", "Qatar", "Qatar", "Qatar", "Qatar", "Qatar", "Qata…
## $ Year <dbl> 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022,…
## $ Attendance <dbl> 88966, 44137, 68294, 88966, 44198, 68895, 43893, 88235, 446…
## $ home_team <chr> "Argentina", "Croatia", "France", "Argentina", "Morocco", "…
## $ away_team <chr> "France", "Morocco", "Morocco", "Croatia", "Portugal", "Fra…
## $ Score <chr> "(4) 3–3 (2)", "2–1", "2–0", "3–0", "1–0", "1–2", "(4) 1–1 …
## $ Round <chr> "Final", "Third-place match", "Semi-finals", "Semi-finals",…
## $ Notes <chr> "Argentina won on penalty kicks following extra time", NA, …
Matches are sorted in ascending order by attendance, replicating the left-side sort in the ATTENDENCE_WORK sheet. This surfaces the five lowest and five highest attended individual matches in the dataset.
## # A tibble: 5 × 6
## Year Host Round home_team away_team Attendance
## <dbl> <chr> <chr> <chr> <chr> <dbl>
## 1 1930 Uruguay Group stage Chile France 2000
## 2 1930 Uruguay Group stage Romania Peru 2549
## 3 1958 Sweden Group stage play-off Wales Hungary 2823
## 4 1934 Italy Quarter-finals Germany Sweden 3000
## 5 1950 Brazil Group stage Switzerland Mexico 3580
## # A tibble: 5 × 6
## Year Host Round home_team away_team Attendance
## <dbl> <chr> <chr> <chr> <chr> <dbl>
## 1 1986 Mexico Group stage Mexico Paraguay 114600
## 2 1950 Brazil Final stage Brazil Sweden 138886
## 3 1950 Brazil Group stage Brazil Yugoslavia 142429
## 4 1950 Brazil Final stage Brazil Spain 152772
## 5 1950 Brazil Final stage Uruguay Brazil 173850
The lowest-attended match on record was from the inaugural 1930 tournament in Uruguay, while the top five are all from the 1950 Brazil tournament — where the Maracanã stadium regularly drew crowds well over 100,000.
For each of the 23 tournaments, the lowest, average, and highest
single-match attendance values are computed. The 2026 North America
tournament row is added manually using data from the updated source file
(matches_1930_2022_JD).
attendance_summary <- df_eda |>
group_by(Year, Host) |>
summarise(
High = max(Attendance, na.rm = TRUE),
Average = mean(Attendance, na.rm = TRUE),
Low = min(Attendance, na.rm = TRUE),
.groups = "drop"
) |>
arrange(Year)
# Add 2026 (North America) row manually from matches_1930_2022_JD source
attendance_summary <- attendance_summary |>
add_row(Year = 2026, Host = "North America",
High = 80824, Average = 65483, Low = 43000)
attendance_summary |>
mutate(Average = round(Average, 0)) |>
knitr::kable(
col.names = c("Year", "Host", "Highest", "Average", "Lowest"),
format.args = list(big.mark = ","),
align = "llrrr"
)| Year | Host | Highest | Average | Lowest |
|---|---|---|---|---|
| 1,930 | Uruguay | 79,867 | 32,808 | 2,000 |
| 1,934 | Italy | 55,000 | 21,353 | 3,000 |
| 1,938 | France | 58,455 | 20,872 | 7,000 |
| 1,950 | Brazil | 173,850 | 47,511 | 3,580 |
| 1,954 | Switzerland | 62,500 | 29,562 | 4,000 |
| 1,958 | Sweden | 50,928 | 23,423 | 2,823 |
| 1,962 | Chile | 76,594 | 27,912 | 5,700 |
| 1,966 | England | 98,270 | 48,848 | 13,792 |
| 1,970 | Mexico | 108,192 | 50,124 | 9,624 |
| 1,974 | Germany | 81,100 | 49,099 | 13,400 |
| 1,978 | Argentina | 71,712 | 40,679 | 7,938 |
| 1,982 | Spain | 95,000 | 40,572 | 11,000 |
| 1,986 | Mexico | 114,600 | 46,039 | 13,800 |
| 1,990 | Italy | 74,765 | 48,389 | 27,833 |
| 1,994 | United States | 94,194 | 68,991 | 44,132 |
| 1,998 | France | 80,000 | 45,367 | 27,650 |
| 2,002 | Korea Republic, Japan | 69,029 | 42,271 | 24,000 |
| 2,006 | Germany | 72,000 | 52,384 | 37,216 |
| 2,010 | South Africa | 84,490 | 49,670 | 23,871 |
| 2,014 | Brazil | 74,738 | 53,592 | 37,603 |
| 2,018 | Russia | 78,011 | 47,371 | 27,015 |
| 2,022 | Qatar | 88,966 | 53,191 | 39,089 |
| 2,026 | North America | 80,824 | 65,483 | 43,000 |
The summary table is reshaped to long format and plotted as a grouped bar chart, with each tournament on the x-axis and bars for the lowest (red), average (green), and highest (blue) per-match attendance.
attendance_long <- attendance_summary |>
mutate(label = paste0(Year, "\n(", Host, ")")) |>
pivot_longer(
cols = c(Low, Average, High),
names_to = "Metric",
values_to = "Attendance"
) |>
mutate(Metric = factor(Metric, levels = c("Low", "Average", "High")))
levels(attendance_long$Metric) <- c("Lowest Attendance",
"Average Attendance",
"Highest Attendance")
x_order <- unique(attendance_long$label)
ggplot(attendance_long,
aes(x = factor(label, levels = x_order),
y = Attendance,
fill = Metric)) +
geom_col(position = position_dodge(width = 0.8),
width = 0.7,
alpha = 0.85) +
scale_fill_manual(values = c(
"Lowest Attendance" = "#C00000",
"Average Attendance" = "#70AD47",
"Highest Attendance" = "#4472C4"
)) +
scale_y_continuous(
labels = comma,
limits = c(0, 185000),
breaks = seq(0, 175000, by = 25000)
) +
labs(
title = "Men's World Cup Match Attendance by Tournament (1930\u20132026)",
subtitle = "Lowest, Average, and Highest per Tournament",
x = "World Cup Year (Host)",
y = "Attendance",
fill = NULL
) +
theme_minimal(base_size = 11) +
theme(
plot.title = element_text(hjust = 0.5, face = "bold"),
plot.subtitle = element_text(hjust = 0.5),
axis.text.x = element_text(angle = 45, hjust = 1, size = 7),
legend.position = c(0.98, 0.98),
legend.justification = c(1, 1),
legend.background = element_rect(fill = "white", color = "grey80", linewidth = 0.4),
panel.grid.major.x = element_blank()
)