Marta Traczyk, 433742
Zuzanna Herniczek, 433380

What makes countries happy?

A visual exploration using the world happiness report.

library(tidyverse)
library(ggridges)
library(dplyr)
library(ggcorrplot)
library(sf)
library(rnaturalearth)
library(rnaturalearthdata)
library(ggrepel)
library(ggiraph)

Dataset

The data used for this project is taken form The World Happiness Report and can be found on Kaggle. The Kaggle dataset contains five csv files with information from years 2015-2019. For the purpose of this project we add ‘year’ column to each file, to be able to merge them into one dataframe.

y2015 <- read_csv('data/2015.csv') %>% mutate(year = 2015)
y2016 <- read_csv('data/2016.csv') %>% mutate(year = 2016)
y2017 <- read_csv('data/2017.csv') %>% mutate(year = 2017)
y2018 <- read_csv('data/2018.csv') %>% mutate(year = 2018)
y2019 <- read_csv('data/2019.csv') %>% mutate(year = 2019)

Throughout the years the names of the columns in the report change. To be able to merge and compare the data, we change column names to uniform names.

clean_2015 <- y2015 %>%
  transmute(
    country = Country,
    happiness_rank = `Happiness Rank`,
    happiness_score = `Happiness Score`,
    gdp_per_capita = `Economy (GDP per Capita)`,
    social_support = Family,
    life_expectancy = `Health (Life Expectancy)`,
    freedom = Freedom,
    generosity = Generosity,
    corruption = as.numeric(`Trust (Government Corruption)`),
    year = year
  )

clean_2016 <- y2016 %>%
  transmute(
    country = Country,
    happiness_rank = `Happiness Rank`,
    happiness_score = `Happiness Score`,
    gdp_per_capita = `Economy (GDP per Capita)`,
    social_support = Family,
    life_expectancy = `Health (Life Expectancy)`,
    freedom = Freedom,
    generosity = Generosity,
    corruption = as.numeric(`Trust (Government Corruption)`),
    year = year
  )

clean_2017 <- y2017 %>%
  transmute(
    country = Country,
    happiness_rank = Happiness.Rank,
    happiness_score = Happiness.Score,
    gdp_per_capita = Economy..GDP.per.Capita.,
    social_support = Family,
    life_expectancy = Health..Life.Expectancy.,
    freedom = Freedom,
    generosity = Generosity,
    corruption = as.numeric(Trust..Government.Corruption.),
    year = year
  )

clean_2018 <- y2018 %>%
  transmute(
    country = `Country or region`,
    happiness_rank = `Overall rank`,
    happiness_score = Score,
    gdp_per_capita = `GDP per capita`,
    social_support = `Social support`,
    life_expectancy = `Healthy life expectancy`,
    freedom = `Freedom to make life choices`,
    generosity = Generosity,
    corruption = as.numeric(`Perceptions of corruption`),  # <-- FIXED
    year = year
  )

clean_2019 <- y2019 %>%
  transmute(
    country = `Country or region`,
    happiness_rank = `Overall rank`,
    happiness_score = Score,
    gdp_per_capita = `GDP per capita`,
    social_support = `Social support`,
    life_expectancy = `Healthy life expectancy`,
    freedom = `Freedom to make life choices`,
    generosity = Generosity,
    corruption = as.numeric(`Perceptions of corruption`),  # <-- FIXED
    year = year
  )

Next step is to merge the separate yearly files into one.

happiness_all <- bind_rows(
  clean_2015,
  clean_2016,
  clean_2017,
  clean_2018,
  clean_2019
)

happiness_all
## # A tibble: 782 × 10
##    country     happiness_rank happiness_score gdp_per_capita social_support
##    <chr>                <dbl>           <dbl>          <dbl>          <dbl>
##  1 Switzerland              1            7.59           1.40           1.35
##  2 Iceland                  2            7.56           1.30           1.40
##  3 Denmark                  3            7.53           1.33           1.36
##  4 Norway                   4            7.52           1.46           1.33
##  5 Canada                   5            7.43           1.33           1.32
##  6 Finland                  6            7.41           1.29           1.32
##  7 Netherlands              7            7.38           1.33           1.28
##  8 Sweden                   8            7.36           1.33           1.29
##  9 New Zealand              9            7.29           1.25           1.32
## 10 Australia               10            7.28           1.33           1.31
## # ℹ 772 more rows
## # ℹ 5 more variables: life_expectancy <dbl>, freedom <dbl>, generosity <dbl>,
## #   corruption <dbl>, year <dbl>
summary(happiness_all)
##    country          happiness_rank  happiness_score gdp_per_capita  
##  Length:782         Min.   :  1.0   Min.   :2.693   Min.   :0.0000  
##  Class :character   1st Qu.: 40.0   1st Qu.:4.510   1st Qu.:0.6065  
##  Mode  :character   Median : 79.0   Median :5.322   Median :0.9822  
##                     Mean   : 78.7   Mean   :5.379   Mean   :0.9160  
##                     3rd Qu.:118.0   3rd Qu.:6.189   3rd Qu.:1.2362  
##                     Max.   :158.0   Max.   :7.769   Max.   :2.0960  
##                                                                     
##  social_support   life_expectancy     freedom         generosity    
##  Min.   :0.0000   Min.   :0.0000   Min.   :0.0000   Min.   :0.0000  
##  1st Qu.:0.8694   1st Qu.:0.4402   1st Qu.:0.3098   1st Qu.:0.1300  
##  Median :1.1247   Median :0.6473   Median :0.4310   Median :0.2020  
##  Mean   :1.0784   Mean   :0.6124   Mean   :0.4111   Mean   :0.2186  
##  3rd Qu.:1.3273   3rd Qu.:0.8080   3rd Qu.:0.5310   3rd Qu.:0.2788  
##  Max.   :1.6440   Max.   :1.1410   Max.   :0.7240   Max.   :0.8381  
##                                                                     
##    corruption          year     
##  Min.   :0.0000   Min.   :2015  
##  1st Qu.:0.0540   1st Qu.:2016  
##  Median :0.0910   Median :2017  
##  Mean   :0.1254   Mean   :2017  
##  3rd Qu.:0.1560   3rd Qu.:2018  
##  Max.   :0.5519   Max.   :2019  
##  NA's   :1

The dataset contains 782 observations (country-year combinations) and 10 columns, covering the period 2015–2019. It is derived from the World Happiness Report and contains measures of happiness and its determinants.

Columns: - country - name of the country - happiness_rank - rank based on happiness score - happiness_score - gdp_per_capita - GDP per capita (PPP-adjusted), standardized to a comparable scale across years - social_support - survey-based measure of support from friends and family (“If you were in trouble, do you have relatives or friends you can count on to help you whenever you need them?”) - life_expectancy - healthy life expectancy at birth, based on WHO Global Health Observatory data - freedom - measure of perceived freedom to make life choices (“Are you satisfied or dissatisfied with your freedom to choose what you do with your life?”) - generosity - survey-based measure of charitable giving (“Have you donated money to a charity in the past month?”) - corruption - perceived corruption in government and business; averaged across survey questions - year

Data Exploration

happiness_all %>%
  ggplot(aes(x = happiness_score, y = factor(year), fill = factor(year))) +
  geom_density_ridges(alpha = 0.7) +
  labs(
    title = "Happiness Score Distribution by Year",
    x = "Happiness Score",
    y = "Year"
  ) +
  theme_minimal() +
  theme(legend.position = "none")
## Picking joint bandwidth of 0.37

Chart above shows the distribution of the happiness score across the years. It provides a visual comparison of how happiness scores were spread out in years 2015-2019.

The overall shape and position of the distribution is stable over time. No dramatic shift can be observed in overall happiness. Most years show a peak around a score of 5-6. In 2015 the peak was slightly below 5, but moved to the right in later years, showing that people were getting happier.

num_cols <- happiness_all %>% 
  select(where(is.numeric)) %>% 
  select(-matches("year")) %>% 
  select(-matches("happiness_rank")) %>% 
  names()

for (yr in sort(unique(happiness_all$year))) {
  
  data_year <- happiness_all %>%
    filter(year == yr) %>%
    select(all_of(num_cols))
  
  zero_var_cols <- sapply(data_year, function(x) sd(x, na.rm = TRUE) == 0)

  data_year <- data_year[, !zero_var_cols]
  
  corr_year <- cor(data_year, use = "pairwise.complete.obs")
  corr_year[is.na(corr_year)] <- 0

  p <- ggcorrplot(
    corr_year,
    hc.order = TRUE,
    type = "lower",
    lab = TRUE,
    lab_size = 3.5,
    method = "square",
    colors = c("steelblue3", "white", "firebrick2"),
    outline.col = "white",
    legend.title = "Correlation"
  ) +
    theme_minimal() +
    labs(
      title = paste("Happiness Factors Correlation:", yr),
      subtitle = "Strength of relationship between socio-economic metrics",
      caption = "Source: World Happiness Report"
    ) +
    theme(
      plot.title = element_text(face = "bold", size = 15, margin = margin(b = 10)),
      axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1),
      panel.grid.major = element_blank()
    )

  print(p)
}
## Warning: `aes_string()` was deprecated in ggplot2 3.0.0.
## ℹ Please use tidy evaluation idioms with `aes()`.
## ℹ See also `vignette("ggplot2-in-packages")` for more information.
## ℹ The deprecated feature was likely used in the ggcorrplot package.
##   Please report the issue at <https://github.com/kassambara/ggcorrplot/issues>.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

Overall the trends in correlation remained the same through the years. There’s strong positive correlation between gdp_per_capita and variables like life_expectancy, happiness_score and social_support. Happiness_score is also strongly correlated with social_support and life_expectancy.

The strong link between gdp_per_capita and life_expectancy reflects the fact that higher-income nations typically invest more in healthcare infrastructure, sanitation, and nutrition. The high correlation between social_support and happiness_score suggests that having a reliable community or government “safety net” significantly reduces individual stress and increases overall life satisfaction. Because wealth enables better health, and healthy citizens are more productive, gdp_per_capita, social_support, and life_expectancy often move together. As seen in the matrix, corruption (or perceptions of it) often shows a weak relationship with happiness, as it erodes the social trust. Interestingly, generosity often shows the weakest correlation with the other “hard” metrics, suggesting that the tendency to give is a cultural or psychological trait that doesn’t strictly depend on a country’s wealth

top_5_names <- happiness_all %>%
  filter(year == max(year)) %>%
  slice_max(happiness_score, n = 5) %>%
  pull(country)

plot_data <- happiness_all %>%
  filter(country %in% top_5_names)

ggplot(plot_data, aes(x = year, y = happiness_score, color = country, group = country)) +
  geom_line(linewidth = 1.2, alpha = 0.8) +
  geom_point(size = 3) +
  geom_text_repel(
    data = plot_data %>% filter(year == max(year)),
    aes(label = country),
    nudge_x = 0.5,
    direction = "y",
    hjust = 0,
    segment.color = 'grey80'
  ) +
  scale_x_continuous(breaks = unique(happiness_all$year), expand = expansion(mult = c(0.05, 0.2))) +
  theme_minimal(base_size = 14) +
  labs(
    title = "Happiness Score Trends: Top 5 Countries (2015-2019)",
    subtitle = "Tracking the world's happiest nations based on the 2019 leaders",
    x = "Year",
    y = "Happiness Score"
  ) +
  theme(
    legend.position = "none",
    panel.grid.minor = element_blank(),
    plot.title = element_text(face = "bold")
  )

The happiest nations, led by Finland, show remarkably high and stable scores above 7.4. Finland specifically demonstrates a strong upward trend, pulling away from peers like Denmark and Norway by 2019. Most countries in this group maintain a tight cluster, suggesting that once a high level of social and economic well-being is achieved, it is highly resilient.

bottom_5_names <- happiness_all %>%
  filter(year == max(year)) %>%
  slice_min(happiness_score, n = 5) %>%
  pull(country)

plot_data <- happiness_all %>%
  filter(country %in% bottom_5_names)

ggplot(plot_data, aes(x = year, y = happiness_score, color = country, group = country)) +
  geom_line(linewidth = 1.2, alpha = 0.8) +
  geom_point(size = 3) +
  geom_text_repel(
    data = plot_data %>% filter(year == max(year)),
    aes(label = country),
    nudge_x = 0.5,
    direction = "y",
    hjust = 0,
    segment.color = 'grey80'
  ) +
  scale_x_continuous(breaks = unique(happiness_all$year), expand = expansion(mult = c(0.05, 0.2))) +
  theme_minimal(base_size = 14) +
  labs(
    title = "Happiness Score Trends: Bottom 5 Countries (2015-2019)",
    subtitle = "Tracking the nations with the lowest scores as of 2019",
    x = "Year",
    y = "Happiness Score"
  ) +
  theme(
    legend.position = "none",
    panel.grid.minor = element_blank(),
    plot.title = element_text(face = "bold")
  )

The least happy nations experience much higher volatility and scores predominantly below 3.6. Notable declines are visible in South Sudan and Afghanistan, where scores dropped sharply toward 2019. Central African Republic shows extreme fluctuation, dropping to a low near 2.7 in 2017 before a partial recovery.

World Map

Country names in the dataset and in ‘rnaturalearth’ differ. To be able to show the happiness scores on a map, we need to set matching names.

world <- ne_countries(scale = "medium", returnclass = "sf")
setdiff(happiness_all$country, world$name)
##  [1] "United States"            "Czech Republic"          
##  [3] "North Cyprus"             "Somaliland region"       
##  [5] "Macedonia"                "Bosnia and Herzegovina"  
##  [7] "Dominican Republic"       "Swaziland"               
##  [9] "Palestinian Territories"  "Congo (Kinshasa)"        
## [11] "Congo (Brazzaville)"      "Central African Republic"
## [13] "Ivory Coast"              "Somaliland Region"       
## [15] "South Sudan"              "Taiwan Province of China"
## [17] "Hong Kong S.A.R., China"  "Trinidad & Tobago"       
## [19] "Northern Cyprus"
country_map <- c(
  "United States" = "United States of America",
  "Czech Republic" = "Czechia",
  "North Cyprus" = "Northern Cyprus",
  "Macedonia" = "North Macedonia",
  "Bosnia and Herzegovina" = "Bosnia and Herz.",
  "Dominican Republic" = "Dominican Rep.",
  "Swaziland" = "Eswatini",
  "Palestinian Territories" = "Palestine",
  "Congo (Kinshasa)" = "Dem. Rep. Congo",
  "Congo (Brazzaville)" = "Congo",
  "Central African Republic" = "Central African Rep.",
  "Ivory Coast" = "Côte d'Ivoire",
  "Somaliland region" = "Somaliland",
  "South Sudan" = "S. Sudan",
  "Taiwan Province of China" = "Taiwan",
  "Hong Kong S.A.R., China" = "Hong Kong",
  "Trinidad & Tobago" = "Trinidad and Tobago",
  "Republic of Congo" = "Congo",
  "Democratic Republic of the Congo" = "Dem. Rep. Congo"
)

happiness_all <- happiness_all %>%
  mutate(country = recode(country, !!!country_map))

Now it is possible to plot the map.

start_year <- 2015
data_2015 <- happiness_all %>% filter(year == start_year)

map_data_2015 <- world %>%
  left_join(data_2015, by = c("name" = "country")) %>%
  st_transform(crs = "+proj=robin") 

ggplot(map_data_2015) +
  geom_sf(aes(fill = happiness_score), color = "white", size = 0.05) +
  scale_fill_viridis_c(
    option = "magma", 
    na.value = "#f0f0f0",
    name = "Happiness Score",
    limits = c(min(happiness_all$happiness_score), max(happiness_all$happiness_score)),
    guide = guide_colorbar(
      direction = "horizontal",
      barwidth = 15,
      barheight = 0.5,
      title.position = "top"
    )
  ) +
  labs(
    title = paste("Global Happiness Scores –", start_year),
    subtitle = "Higher scores (yellow) indicate greater self-reported well-being"
  ) +
  theme_void() +
  theme(
    legend.position = "bottom",
    plot.title = element_text(face = "bold", size = 18, hjust = 0.5),
    plot.subtitle = element_text(size = 11, color = "grey30", hjust = 0.5, margin = margin(b = 15)),
    plot.margin = margin(10, 10, 10, 10)
  )

latest_year <- max(happiness_all$year)
data_latest <- happiness_all %>% filter(year == latest_year)

map_data <- world %>%
  left_join(data_latest, by = c("name" = "country")) %>%
  st_transform(crs = "+proj=robin") 

ggplot(map_data) +
  geom_sf(aes(fill = happiness_score), color = "white", size = 0.05) +
  scale_fill_viridis_c(
    option = "magma", 
    na.value = "#f0f0f0",
    name = "Happiness Score",
    guide = guide_colorbar(
      direction = "horizontal",
      barwidth = 15,
      barheight = 0.5,
      title.position = "top"
    )
  ) +
  labs(
    title = paste("Global Happiness Scores –", latest_year),
    subtitle = "Higher scores (yellow) indicate greater self-reported well-being"
  ) +
  theme_void() +
  theme(
    legend.position = "bottom",
    plot.title = element_text(face = "bold", size = 18, hjust = 0.5),
    plot.subtitle = element_text(size = 11, color = "grey30", hjust = 0.5, margin = margin(b = 15)),
    plot.margin = margin(10, 10, 10, 10)
  )

The two maps show the global distribution of happiness scores in 2015 and 2019, where lighter colors indicate higher self-reported well-being and darker colors indicate lower happiness.

Overall, the global pattern of happiness remains relatively stable across the two years. Countries in Northern and Western Europe, as well as Australia, New Zealand, and Canada, consistently report high happiness levels. In contrast, many countries in Sub-Saharan Africa and parts of the Middle East remain among the lowest-scoring regions in both years.

However, there are noticeable regional shifts between 2015 and 2019. In Eastern Europe and parts of Asia, happiness scores appear slightly higher in 2019, suggesting gradual improvement in well-being.

latest_year <- max(happiness_all$year)
data_latest <- happiness_all %>% filter(year == latest_year)

map_data_interactive <- world %>%
  left_join(data_latest, by = c("name" = "country")) %>%
  st_transform(crs = "+proj=robin") %>%
  mutate(name_clean = stringr::str_replace_all(name, "'", " ")) %>% 
  mutate(tooltip = paste0("Country: ", name_clean, "\nScore: ", round(happiness_score, 2)))

gg_map <- ggplot(map_data_interactive) +
  geom_sf_interactive(aes(
    fill = happiness_score, 
    tooltip = tooltip,    
    data_id = name_clean  # Use the cleaned version here as well
  ), color = "white", size = 0.1) +
  scale_fill_viridis_c(
    option = "magma", 
    na.value = "#f0f0f0",
    name = "Score"
  ) +
  labs(
    title = paste("Interactive Global Happiness –", latest_year),
    subtitle = "Hover over a country to see its specific score"
  ) +
  theme_void() +
  theme(
    legend.position = "bottom",
    plot.title = element_text(face = "bold", size = 18, hjust = 0.5)
  )

girafe(ggobj = gg_map, 
       options = list(
         opts_hover(css = "fill:cyan;cursor:pointer;"),
         opts_tooltip(css = "background-color:black;color:white;padding:5px;border-radius:5px;")
       ))
improvement <- happiness_all %>%
  filter(year %in% c(2015, 2019)) %>%
  select(country, year, happiness_score) %>%
  pivot_wider(names_from = year, names_prefix = "y", values_from = happiness_score) %>%
  mutate(change = y2019 - y2015) %>%
  slice_max(change, n = 5) %>%
  rename("2015 Score" = y2015, "2019 Score" = y2019, "Improvement" = change)
knitr::kable(improvement, caption = "Top 5 Most Improved Countries (2015-2019)")
Top 5 Most Improved Countries (2015-2019)
country 2015 Score 2019 Score Improvement
Benin 3.340 4.883 1.543
Côte d’Ivoire 3.655 4.944 1.289
Togo 2.839 4.085 1.246
Honduras 4.788 5.860 1.072
Burkina Faso 3.587 4.587 1.000

Does money buy happines?

data_2019 <- happiness_all %>% filter(year == 2019)

outliers <- data_2019 %>% 
  filter(country %in% c("Costa Rica", "Botswana", "United Arab Emirates", "Finland", "Afghanistan"))

ggplot(data_2019, aes(x = gdp_per_capita, y = happiness_score)) +
  geom_point(aes(color = life_expectancy), size = 3, alpha = 0.7) +
  geom_smooth(method = "lm", color = "grey30", linetype = "dashed", se = FALSE) +
  geom_text_repel(data = outliers, aes(label = country), 
                  box.padding = 0.5, point.padding = 0.5, size = 4) +
  scale_color_viridis_c(option = "plasma") +
  theme_minimal(base_size = 14) +
  labs(
    title = "The Economic Engine of Happiness",
    subtitle = "Relationship between GDP per Capita and Happiness Score (2019)",
    x = "GDP per Capita (Relative Scale)",
    y = "Happiness Score",
    color = "Life Expectancy"
  ) +
  theme(plot.title = element_text(face = "bold"))
## `geom_smooth()` using formula = 'y ~ x'

The scatterplot illustrates a clear positive relationship between GDP per capita and happiness score in 2019, indicating that wealthier countries tend to report higher levels of life satisfaction. The fitted linear trend confirms this overall association, although substantial dispersion around the line suggests that economic prosperity alone does not fully explain national happiness levels.

Coloring points by life expectancy reveals an additional pattern: countries with higher happiness scores often also exhibit better health outcomes, reinforcing the strong interconnection between economic conditions, public health, and subjective well-being. Several notable outliers highlight this complexity. Costa Rica achieves high happiness despite relatively moderate GDP, suggesting the importance of social cohesion and well-being-oriented policies. In contrast, the United Arab Emirates combines high GDP with only moderate happiness, indicating that economic wealth does not automatically translate into life satisfaction. Finland stands out as a high-performing country across all dimensions, while Afghanistan and Botswana illustrate cases where low economic and health indicators coincide with low happiness scores.

Overall, this visualization emphasizes that while GDP per capita is an important driver of happiness, non-economic factors such as health, social support, and governance play a crucial role in shaping subjective well-being.

How happy is a country compared to what its economic wealth would predict?

While the correlation between wealth and happiness is clear, the outliers tell the most compelling story. By calculating the residual (the difference between the actual score and the score predicted by GDP), we can identify nations that defy the economic trend.

model <- lm(happiness_score ~ gdp_per_capita, data = data_2019)
data_2019$predicted <- predict(model)
data_2019$residual <- data_2019$happiness_score - data_2019$predicted

heroes <- data_2019 %>% slice_max(residual, n = 5)
strugglers <- data_2019 %>% slice_min(residual, n = 5)
labeled_countries <- bind_rows(heroes, strugglers)

ggplot(data_2019, aes(x = gdp_per_capita, y = happiness_score)) +
  geom_smooth(method = "lm", color = "grey80", se = FALSE, size = 0.5) +
  geom_point(aes(color = residual), size = 3) +
  scale_color_gradient2(low = "#d7191c", mid = "#ffffbf", high = "#1a9641", 
                        midpoint = 0, name = "Happiness Gap") +
  geom_text_repel(data = labeled_countries, 
                  aes(label = country),
                  fontface = "bold", 
                  box.padding = 0.8,
                  max.overlaps = Inf) +
  theme_minimal() +
  labs(
    title = "Happiness Heroes vs. Underperformers",
    subtitle = "Highlighting countries that are happier (green) or sadder (red) than their wealth predicts",
    x = "GDP per Capita",
    y = "Happiness Score"
  ) +
  theme(legend.position = "right")
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## `geom_smooth()` using formula = 'y ~ x'

The “Happiness Heroes” (green) Countries like Costa Rica and Uzbekistan consistently score much higher than their GDP would suggest. In Costa Rica’s case, researchers often point to strong social ties, a focus on environmental well-being, and the absence of a military (allowing for more social spending) as reasons for this “happiness surplus.”

The “Underperformers” (red) On the other hand, some nations score significantly lower than their economic standing predicts. For instance, countries in the Middle East or parts of Sub-Saharan Africa often show negative residuals. This “happiness deficit” is frequently linked to high levels of perceived corruption, lack of personal freedom, or recent histories of conflict that erode the benefits of economic growth.

The graph above shows that GDP is the floor, not the ceiling. It provides the resources for a good life, but that’s not the only thing that matters.

If not money, then what?

top_10_ingredients <- happiness_all %>%
  filter(year == 2019) %>%
  slice_max(happiness_score, n = 10) %>%
  select(country, gdp_per_capita, social_support, life_expectancy, freedom, generosity, corruption) %>%
  pivot_longer(cols = -country, names_to = "factor", values_to = "value")

ggplot(top_10_ingredients, aes(x = reorder(country, value), y = value, fill = factor)) +
  geom_bar(stat = "identity") +
  coord_flip() +
  scale_fill_brewer(palette = "Set3") +
  theme_minimal() +
  labs(
    title = "The Composition of Happiness: Top 10 Countries",
    subtitle = "Relative contribution of different socio-economic factors (2019)",
    x = "",
    y = "Accumulated Factor Score",
    fill = "Factor"
  )

Across all top-performing countries, social support and life expectancy consistently make up the largest portions of the total score, highlighting their central role in sustaining high levels of happiness. Factors such as freedom and low perceived corruption further differentiate countries within this high-happiness group, while generosity contributes a smaller but still visible share.

Coclusion: The world’s happiest countries achieve their high scores through a balanced combination of economic prosperity, strong social networks, good health outcomes, and institutional quality, rather than relying on a single dominant factor.