Investigate What Makes Countries Happy
Analyze real survey data to discover what truly drives national happiness.
Introduction
30 Second Summary
Have you ever wondered why people in some countries consistently report being happier than others? It turns out the answer is more surprising than you might think.
In this project, you will investigate real survey data from the World Happiness Report to discover what actually drives national happiness. You will form a hypothesis, test it with visualizations, and uncover the true factors behind why some countries thrive.
What You'll Build
You will run a polished Jupyter Notebook that generates scatter plots, heatmaps, and multi-panel charts from 150+ countries of data, telling a clear visual story about what makes nations happy.
By the end of this project, you'll have:
- A scatter plot that tests whether GDP predicts happiness and reveals the relationship is not the whole story.
- A correlation heatmap showing which of the six happiness factors are the true drivers of national well-being.
- A multi-panel figure comparing all six factors side by side so you can spot the strongest predictors at a glance.
- Secret Mission: Build a Happiness Report Card comparing the top 5 happiest countries against the bottom 5 across all factors.
Are there any prerequisites?
This project assumes you have Python 3.11+ installed, PyCharm set up, and a Kaggle account with your API credentials configured. Step 1 will walk you through creating a virtual environment and downloading the dataset.
Before We Start
Before We Start...
Before diving into data, let's lock in what you're about to build and the question your analysis will answer.
Set Up Your Environment
To investigate what makes countries happy, you need the right data analysis tools and the actual happiness dataset on your machine.
In this step, you'll create a Python project with dedicated libraries for data analysis, then download the World Happiness Report covering 150+ countries.
In this step, get ready to:
- Create a project folder with a virtual environment and install data analysis libraries.
- Download the World Happiness Report dataset from Kaggle.
- Launch Jupyter Notebook and create your investigation notebook.
Create a project folder and virtual environment
A virtual environment keeps this project's libraries separate from any other Python work on your machine.
- Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type PyCharm and press Enter to open it.
- Open a terminal inside PyCharm by clicking Terminal in the bottom toolbar (or press Alt+F12).
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir happiness-investigation
cd happiness-investigation
- Create and activate a virtual environment by running these commands:
macOS
python3 -m venv venv
source venv/bin/activate
Windows
python -m venv venv
.\venv\Scripts\Activate.ps1
Seeing a script execution error?
If PowerShell blocks the activation script, run this command first to allow it:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Then run the activation command again.
You should see (venv) appear at the start of your terminal prompt. This confirms the virtual environment is active.
What is a virtual environment?
A virtual environment is an isolated Python installation for one project. Libraries you install here stay contained and won't conflict with other projects on your machine.
- Install the data analysis libraries by running:
pip install pandas seaborn matplotlib notebook
What are these libraries?
- pandas loads and manipulates tabular data (like spreadsheets).
- seaborn and matplotlib create publication-quality charts and visualizations.
- notebook provides Jupyter Notebook, an interactive coding environment for data exploration.
Before you run the next command, what version number do you expect to see for Jupyter Notebook?
- Verify the install worked by running:
jupyter notebook --version
You should see 7.6.0 (or a similar 7.x version) printed in your terminal.
Seeing command not found?
Make sure your virtual environment is active. You should see (venv) in your prompt. If not, re-run the activation command from the platform tab above.
If the environment is active but the command still fails, re-run pip install notebook and check for errors in the output.
Help me troubleshoot why jupyter notebook --version is not working
Download the World Happiness Report dataset
The Kaggle API lets you download datasets directly from the command line. You already have your Kaggle credentials configured, so one command is all you need.
- Download the World Happiness Report dataset by running:
kaggle datasets download -d unsdsn/world-happiness
- Unzip the downloaded file by running:
macOS
unzip world-happiness.zip
Windows
Expand-Archive -Path .\world-happiness.zip -DestinationPath .
- Confirm the dataset files are present by running:
ls
You should see several CSV files including 2019.csv. This is the file you'll use for your investigation.
Not seeing any CSV files?
Check that the kaggle command completed without errors. If you see an authentication error, confirm your Kaggle API credentials are in the correct location (~/.kaggle/kaggle.json on macOS or C:\Users\<you>\.kaggle\kaggle.json on Windows).
Help me fix my Kaggle API download
Launch Jupyter Notebook and create your notebook
Jupyter Notebook is an interactive coding environment where you can run Python code in small cells and see results inline. It is the industry standard for data exploration.
- Launch Jupyter Notebook by running:
jupyter notebook
What happens when you run this?
This command starts a Jupyter server and opens your browser to the notebook dashboard. The terminal is now occupied running the server. Do not close this terminal window while you are working.
To stop the server later, press Ctrl+C in the terminal.
Your browser opens to the Jupyter dashboard showing the files in your happiness-investigation folder.
- In the top-right corner of the Jupyter dashboard, click New.
- Select Python 3 from the dropdown menu.
- A new notebook opens in a separate browser tab. Click Untitled at the top of the page (next to the Jupyter logo).
- Type happiness_investigation and click Rename.
You now have a blank notebook called happiness_investigation.ipynb open and ready for code.
Browser did not open automatically?
Look at your terminal output for a URL that starts with http://localhost:8888. Copy and paste that URL into your browser manually.
Help me access my Jupyter Notebook in the browser
Your environment is ready with everything you need. Next up, you'll load the happiness data into your notebook and meet the dataset for the first time.
Load and Explore Your Data
Your environment is set up and your dataset is waiting. Now it is time to actually look at the data you will be investigating.
You cannot analyze what you have not seen. Before forming any hypothesis about what makes countries happy, you need to understand what the dataset contains, how many countries it covers, and what factors it measures.
In this step, get ready to:
- Import your data analysis libraries.
- Load the World Happiness Report dataset and inspect its structure.
- Run summary statistics to see the happiest and least happy countries.
Import your data analysis libraries
Every data investigation starts by loading the tools you will use. You need three libraries: one for data manipulation, one for basic plotting, and one for statistical visualizations.
- In your happiness_investigation.ipynb notebook, click into the first empty cell and paste the following code:
# Cell 1: Imports
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
- Run the cell by pressing Shift+Enter.
What does this code do?
- pandas (imported as pd) is your data manipulation library. It loads CSVs into tables you can filter, sort, and summarize.
- matplotlib (imported as plt) handles basic chart rendering.
- seaborn (imported as sns) builds on matplotlib with statistical visualization functions like heatmaps and styled scatter plots.
If the cell runs without any error, your imports loaded successfully. A new empty cell appears below, ready for your next block of code.
Seeing a ModuleNotFoundError?
This means the library is not installed in your virtual environment. Make sure your venv is activated (you should see (venv) in your terminal prompt). Then re-run:
pip install pandas seaborn matplotlib notebook
After installing, restart your Jupyter kernel by clicking Kernel then Restart in the menu bar, and run the import cell again.
Help me fix my ModuleNotFoundError in Jupyter
Load the 2019 happiness data
With your libraries ready, you can load the actual dataset. The 2019.csv file from the World Happiness Report is already in your project folder from the previous step.
- In the new cell below your imports, paste the following code and run it with Shift+Enter:
# Cell 2: Load and explore data
df = pd.read_csv('2019.csv')
print("Shape:", df.shape)
print("\nColumns:", df.columns.tolist())
print("\nFirst 5 rows:")
print(df.head())
What does this code do?
- pd.read_csv('2019.csv') reads the CSV file into a DataFrame (a table structure in pandas) and stores it in df.
- df.shape prints the number of rows and columns so you know the dataset's size.
- df.columns.tolist() lists every column name so you know what factors are available.
- df.head() shows the first 5 rows so you can see real data values.
You should see output showing 156 rows and 9 columns. The columns include Score, GDP per capita, Social support, Healthy life expectancy, Freedom to make life choices, Generosity, and Perceptions of corruption.
Seeing FileNotFoundError?
This means Python cannot find 2019.csv in the current directory. Make sure you launched jupyter notebook from inside your happiness-investigation folder where the CSV file lives.
Help me fix my FileNotFoundError when loading 2019.csv
Run basic statistics and view the happiest countries
You know the dataset's shape and columns. Now you need to understand the ranges of its values and see which countries sit at the top and bottom of the happiness ranking.
Before you run this next cell, take a guess. Which countries do you think will appear in the top 10 happiest?
- In a new cell, paste the following code and run it with Shift+Enter:
# Cell 3: Basic statistics
print(df.describe())
print("\nTop 10 Happiest Countries:")
print(df.nlargest(10, 'Score')[['Country or region', 'Score']].to_string(index=False))
print("\nBottom 10 Countries:")
print(df.nsmallest(10, 'Score')[['Country or region', 'Score']].to_string(index=False))
What does this code do?
- df.describe() calculates count, mean, standard deviation, min, max, and quartiles for every numeric column. This tells you the range and spread of each factor.
- df.nlargest(10, 'Score') returns the 10 rows with the highest happiness Score.
- df.nsmallest(10, 'Score') returns the 10 rows with the lowest happiness Score.
You should see the top 10 dominated by Nordic countries (Finland, Denmark, Norway, Iceland). The bottom 10 are primarily in Sub-Saharan Africa. This pattern already hints that happiness is tied to structural factors, not just individual wealth.
Seeing a KeyError for 'Score'?
The column names in your CSV might differ slightly. Run print(df.columns.tolist()) again and check the exact spelling of the happiness score column. It should be Score with a capital S.
Help me fix a KeyError when accessing columns in my DataFrame
✔️ Awesome, I've got everything!
Great. Make sure your notebook is saved by pressing Cmd+S (macOS) or Ctrl+S (Windows).
ⓧ I'd like to double check the full code
Here is the complete code across all three cells. Compare each cell in your notebook against this reference:
# Cell 1: Imports
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# Cell 2: Load and explore data
df = pd.read_csv('2019.csv')
print("Shape:", df.shape)
print("\nColumns:", df.columns.tolist())
print("\nFirst 5 rows:")
print(df.head())
# Cell 3: Basic statistics
print(df.describe())
print("\nTop 10 Happiest Countries:")
print(df.nlargest(10, 'Score')[['Country or region', 'Score']].to_string(index=False))
print("\nBottom 10 Countries:")
print(df.nsmallest(10, 'Score')[['Country or region', 'Score']].to_string(index=False))
You have met your data. You know it covers 156 countries, measures six factors, and that Nordic countries dominate the top while Sub-Saharan Africa clusters at the bottom. Next up, you will test the most obvious hypothesis: does money buy happiness?
Test the 'Money Buys Happiness' Hypothesis
In Step 2, you loaded the World Happiness Report data and met the 156 countries inside it. Now it is time to ask the big question: does money actually buy happiness?
Most people assume wealthier countries are happier. In this step, you will put that intuition to the test with a scatter plot and a correlation coefficient. If GDP predicts happiness, the data will show a clear upward trend.
In this step, get ready to:
- Create a scatter plot of GDP per capita vs Happiness Score.
- Calculate the correlation coefficient to quantify the relationship.
Create a scatter plot of GDP per capita vs Happiness Score
A scatter plot places each country as a dot on a grid. If GDP truly predicts happiness, the dots will trend upward from left to right.
- In your notebook, create a new cell below Cell 3 and paste the following code:
# Cell 4: GDP vs Happiness scatter plot
plt.figure(figsize=(10, 6))
sns.scatterplot(data=df, x='GDP per capita', y='Score', color='steelblue', s=60)
plt.title('Does Money Buy Happiness?', fontsize=14, fontweight='bold')
plt.xlabel('GDP per Capita')
plt.ylabel('Happiness Score')
plt.tight_layout()
plt.savefig('gdp_vs_happiness.png', dpi=150)
plt.show()
What does this code do?
This cell creates a publication-quality scatter plot using seaborn.
- plt.figure(figsize=(10, 6)) sets the chart size to 10 inches wide by 6 inches tall.
- sns.scatterplot() plots each country as a dot with GDP on the x-axis and Happiness Score on the y-axis.
- plt.savefig('gdp_vs_happiness.png', dpi=150) saves the chart as a high-resolution image file in your project folder.
Before you run this cell, what do you expect to see? Will the dots form a clear upward trend, or will they be scattered randomly?
- Run Cell 4 by pressing Shift+Enter.
You should see a scatter plot where the dots clearly trend upward from left to right. Countries with higher GDP per capita tend to have higher happiness scores.
Don't see a chart?
- Make sure you ran Cells 1-3 first. Cell 4 depends on the df variable created in Cell 2.
- Check that your column name is exactly 'GDP per capita' with a lowercase "p" in "per". Run df.columns.tolist() to verify the exact names.
- If you see a blank plot, confirm that plt.show() is the last line in the cell.
Help me debug my scatter plot
Calculate the correlation coefficient
The scatter plot shows an upward trend visually. A correlation coefficient puts a number on that relationship, ranging from -1 (perfect negative) to +1 (perfect positive).
- Create a new cell below Cell 4 and paste this code:
# Cell 5: GDP correlation
gdp_corr = df['Score'].corr(df['GDP per capita'])
print(f"Correlation between GDP and Happiness: {gdp_corr:.3f}")
What does this code do?
The .corr() method calculates the Pearson correlation between two columns. A result around 0.79 means GDP and happiness have a strong positive relationship.
At this point, the data seems to confirm the hypothesis: money does buy happiness.
- Run Cell 5 by pressing Shift+Enter.
You should see output like Correlation between GDP and Happiness: 0.794 (your exact number may differ slightly).
Seeing an error or unexpected value?
- A KeyError means the column name does not match. Double-check with df.columns.tolist() that you are using the exact column names from your dataset.
- If the correlation is NaN, one of the columns may contain non-numeric values. Run df.dtypes to confirm both columns are numeric.
Help me debug my correlation calculation
✔️ Awesome, I've got everything!
Great. Confirm you have saved your notebook with Cells 4 and 5 complete.
ⓧ I'd like to double check the full code
Your notebook should now have Cells 1 through 5. Scroll up through your notebook and confirm these cells are present in order:
- Cell 1: Imports (pandas, matplotlib.pyplot, seaborn).
- Cell 2: Load data with pd.read_csv('2019.csv') and print shape, columns, and head.
- Cell 3: Basic statistics with describe(), top 10 and bottom 10 countries.
- Cell 4: GDP vs Happiness scatter plot saved as gdp_vs_happiness.png.
- Cell 5: Correlation coefficient stored in gdp_corr and printed.
Your scatter plot tells a compelling story: wealthier countries tend to be happier. With a correlation of about 0.79, the evidence looks strong. Case closed, right?
Not so fast. In the next step, you will zoom in on individual countries and discover that this tidy relationship has some serious cracks.
Find the Cracks in the Theory
In the last step, you found a correlation of ~0.79 between GDP and happiness. That is a strong signal. It looks like the case is closed: money buys happiness.
But a single number hides individual stories. Some wealthy countries are not proportionally happier, and some moderate-income countries punch well above their weight. In this step, you will dig into the data and find the countries that break the pattern.
In this step, get ready to:
- Identify countries that defy the "money = happiness" rule.
- Visualize the outliers on an annotated scatter plot.
Identify countries that break the GDP pattern
A correlation of 0.79 means most countries follow the trend. But "most" is not "all." The interesting story lives in the exceptions.
Before you run this next cell, think about it: do you expect every wealthy country to be equally happy? Or might some rich nations score lower than you would predict?
- Create a new cell in your notebook below Cell 5.
- Filter for high-GDP countries with below-median happiness, and moderate-GDP countries with top-quartile happiness, by pasting this code:
# Investigate outliers
high_gdp = df[df['GDP per capita'] > df['GDP per capita'].quantile(0.75)]
low_happiness_rich = high_gdp[high_gdp['Score'] < high_gdp['Score'].median()]
print("High GDP but below-median happiness (among wealthy):")
print(low_happiness_rich[['Country or region', 'Score', 'GDP per capita']].to_string(index=False))
moderate_gdp = df[
(df['GDP per capita'] > df['GDP per capita'].quantile(0.25)) &
(df['GDP per capita'] < df['GDP per capita'].quantile(0.75))
]
high_happiness_moderate = moderate_gdp[moderate_gdp['Score'] > df['Score'].quantile(0.75)]
print("\nModerate GDP but high happiness:")
print(high_happiness_moderate[['Country or region', 'Score', 'GDP per capita']].to_string(index=False))
What does this code do?
This cell runs two investigations on your data:
- high_gdp selects countries in the top 25% by GDP. Then low_happiness_rich narrows that down to the ones below the median happiness within that wealthy group.
- moderate_gdp selects countries in the middle 50% by GDP. Then high_happiness_moderate finds those above the 75th percentile for happiness overall.
The result: two groups of countries that break the "rich = happy" assumption from opposite directions.
- Run the cell by pressing Shift+Enter.
You should see two printed tables. The first shows wealthy countries with below-median happiness among the rich group. The second shows moderate-income countries with surprisingly high happiness scores.
Your earlier 0.79 correlation suggested a simple story. These tables prove it is incomplete. Money helps, but it is clearly not the whole picture.
Seeing a KeyError?
- Make sure you ran all previous cells (Cells 1-5) before this one. The df variable must already exist.
- Check that the column name matches exactly: GDP per capita (capital G, lowercase p). You can verify with df.columns.tolist() in a new cell.
Why is my cell throwing a KeyError?
Visualize the outliers on the scatter plot
Tables prove the outliers exist, but a chart makes them impossible to ignore. You will rebuild the GDP scatter plot and color-code the countries that break the pattern.
- Create a new cell below Cell 6.
- Build an annotated scatter plot highlighting both outlier groups by pasting this code:
# Annotated scatter plot with outliers
plt.figure(figsize=(10, 6))
sns.scatterplot(data=df, x='GDP per capita', y='Score', color='lightgray', s=50)
sns.scatterplot(data=low_happiness_rich, x='GDP per capita', y='Score', color='red', s=80, label='Rich but less happy')
sns.scatterplot(data=high_happiness_moderate, x='GDP per capita', y='Score', color='green', s=80, label='Moderate GDP but very happy')
for _, row in low_happiness_rich.head(3).iterrows():
plt.annotate(row['Country or region'], (row['GDP per capita'], row['Score']),
textcoords="offset points", xytext=(5, 5), fontsize=8, color='red')
for _, row in high_happiness_moderate.head(3).iterrows():
plt.annotate(row['Country or region'], (row['GDP per capita'], row['Score']),
textcoords="offset points", xytext=(5, 5), fontsize=8, color='green')
plt.title('The Cracks in "Money = Happiness"', fontsize=14, fontweight='bold')
plt.xlabel('GDP per Capita')
plt.ylabel('Happiness Score')
plt.legend()
plt.tight_layout()
plt.savefig('gdp_outliers.png', dpi=150)
plt.show()
What does this code do?
- All 156 countries are plotted in light gray as the background context.
- The low_happiness_rich countries are highlighted in red. These are wealthy nations that are less happy than expected.
- The high_happiness_moderate countries are highlighted in green. These are moderate-income nations that are happier than their GDP would predict.
- The plt.annotate() loops label the top 3 countries from each group directly on the chart so you can see exactly who they are.
- Run the cell by pressing Shift+Enter.
You should see a scatter plot with most dots in light gray, a cluster of red dots (rich but less happy), and green dots (moderate GDP but very happy). Country names are annotated next to the top 3 from each group.
- Confirm that gdp_outliers.png was saved in your project folder by running this in a new cell:
import os
print(os.listdir('.'))
You should see gdp_outliers.png alongside gdp_vs_happiness.png in the file listing.
Chart not displaying?
- Make sure Cell 6 ran successfully first. Cell 7 depends on the low_happiness_rich and high_happiness_moderate variables created there.
- If you see a blank plot, check that plt.show() is the last line in the cell. Any code after it can interfere with rendering.
Help me debug my scatter plot with annotations in Jupyter Notebook.
✔️ Awesome, I've got everything!
Great. Double check you have saved your notebook with all cells through Cell 7.
ⓧ I'd like to double check the full code
Scroll through your notebook and confirm the following cells are present, in order:
- Cell 1: Imports (pandas, matplotlib.pyplot, seaborn).
- Cell 2: Load 2019.csv and print shape, columns, first 5 rows.
- Cell 3: Basic statistics, top 10, and bottom 10 countries.
- Cell 4: GDP vs Happiness scatter plot, saved as gdp_vs_happiness.png.
- Cell 5: GDP correlation coefficient printed.
- Cell 6: Outlier investigation: high_gdp, low_happiness_rich, moderate_gdp, high_happiness_moderate filters and prints.
- Cell 7: Annotated scatter plot with outliers highlighted, saved as gdp_outliers.png.
Look at your annotated scatter plot. The red and green dots tell a clear story: GDP alone does not explain happiness for every country. Something else is at play. In the next step, you will zoom out and examine all six happiness factors to discover what actually drives national well-being.
Uncover What Actually Drives Happiness
You've proven that GDP alone does not tell the full story. Some wealthy countries are surprisingly unhappy, and some moderate-income countries thrive. So what else matters?
A correlation heatmap will compare all six happiness factors at once, revealing which ones truly predict national happiness. You will also build a multi-panel figure that shows every factor's relationship with happiness side by side.
In this step, get ready to:
- Build a correlation heatmap of all factors.
- Create a multi-panel scatter plot comparing all six factors.
- Print your key findings summary.
Build a correlation heatmap of all factors
The .corr() method calculates how strongly every pair of numeric columns moves together. A heatmap turns that matrix into a color-coded grid you can read at a glance.
- In your notebook, create a new cell below Cell 7 and add the following code to build the heatmap:
# Select the numeric factors to compare
factors = ['Score', 'GDP per capita', 'Social support', 'Healthy life expectancy',
'Freedom to make life choices', 'Generosity', 'Perceptions of corruption']
correlation_matrix = df[factors].corr()
plt.figure(figsize=(10, 8))
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', center=0,
fmt='.2f', square=True, linewidths=0.5)
plt.title('What Really Drives Happiness?\nCorrelation Between All Factors', fontsize=13, fontweight='bold')
plt.tight_layout()
plt.savefig('correlation_heatmap.png', dpi=150)
plt.show()
What does this code do?
The factors list selects the seven numeric columns (the happiness score plus all six contributing factors). Calling .corr() on those columns produces a 7x7 matrix of correlation coefficients.
The sns.heatmap() function draws that matrix as a color grid. Values near 1.0 (dark red) mean two factors move strongly together. Values near 0 (white) mean little relationship.
Before you run this cell, predict: which factor do you think will have the strongest correlation with happiness, besides GDP?
- Run the cell.
You should see a color-coded matrix where Social support and GDP per capita show the strongest correlations with Score (both above 0.7). The heatmap also saves as correlation_heatmap.png in your project folder.
Don't see the heatmap?
- Make sure you ran Cell 2 (data loading) before this cell. The df variable must exist.
- Check that the column names in your factors list match the exact column names from df.columns (they are case-sensitive).
- If you see a NameError, re-run your import cell (Cell 1) first.
help me debug my correlation heatmap
Create a multi-panel scatter plot comparing all six factors
The heatmap gives you numbers, but scatter plots show the shape of each relationship. A 2x3 grid puts all six factors on screen at once so you can visually compare them.
- Create a new cell below your heatmap cell and add this code to build the multi-panel figure:
# Create a 2x3 grid of scatter plots
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
factors_to_plot = ['GDP per capita', 'Social support', 'Healthy life expectancy',
'Freedom to make life choices', 'Generosity', 'Perceptions of corruption']
for ax, factor in zip(axes.flatten(), factors_to_plot):
sns.scatterplot(data=df, x=factor, y='Score', ax=ax, color='steelblue', s=40, alpha=0.7)
corr = df['Score'].corr(df[factor])
ax.set_title(f'{factor}\n(r = {corr:.2f})', fontsize=10)
ax.set_ylabel('Happiness Score')
plt.suptitle('The Six Factors Behind World Happiness', fontsize=14, fontweight='bold', y=1.02)
plt.tight_layout()
plt.savefig('six_factors.png', dpi=150, bbox_inches='tight')
plt.show()
What does this code do?
The plt.subplots(2, 3) call creates a figure with six subplots arranged in 2 rows and 3 columns. The loop plots each factor against happiness Score in its own panel.
Each subplot title shows the factor name and its correlation coefficient (r value). This makes it easy to compare how tightly each factor relates to happiness just by scanning the scatter patterns and numbers.
- Run the cell.
You should see six scatter plots in a grid. GDP per capita and Social support show tight upward clusters, while Generosity shows a looser cloud. The figure saves as six_factors.png in your project folder.
Seeing overlapping labels?
- Make sure you have plt.tight_layout() in your code. This adjusts spacing between subplots.
- If the title gets cut off, confirm that bbox_inches='tight' is included in your savefig call.
help me fix my multi-panel scatter plot layout
Print your key findings summary
Visualizations tell the story, but a ranked list makes the conclusion concrete. This final cell sorts every factor by correlation strength and prints the top predictors of happiness.
- Create a new cell below your multi-panel plot and add this code to print your findings:
# Rank all factors by correlation strength
print("KEY FINDINGS")
print("=" * 50)
factors_to_plot = ['GDP per capita', 'Social support', 'Healthy life expectancy',
'Freedom to make life choices', 'Generosity', 'Perceptions of corruption']
correlations = {factor: df['Score'].corr(df[factor]) for factor in factors_to_plot}
sorted_corr = sorted(correlations.items(), key=lambda x: abs(x[1]), reverse=True)
for factor, corr in sorted_corr:
print(f" {factor}: r = {corr:.3f}")
print("\nTop 3 predictors of happiness:")
for i, (factor, corr) in enumerate(sorted_corr[:3], 1):
print(f" {i}. {factor} (r = {corr:.3f})")
print("\nConclusion: Money matters, but Social support and")
print("Healthy life expectancy are equally important predictors.")
print("Generosity and Corruption perceptions have weaker links to happiness.")
What does this code do?
The dictionary comprehension calculates the correlation between each factor and the happiness Score. Sorting by abs(x[1]) ranks factors by how strongly they relate to happiness regardless of direction.
The printed conclusion summarizes what your investigation revealed: Social support rivals GDP as the strongest predictor, and Freedom matters more than Generosity.
Before you run this cell, predict: based on the heatmap and scatter plots you already saw, which factor do you think ranks first?
- Run the cell.
You should see all six factors ranked by correlation strength, with the top 3 predictors listed, and a printed conclusion. Social support rivals GDP at the top, and Freedom to make life choices matters more than Generosity.
Seeing unexpected rankings?
- Make sure you loaded 2019.csv in Cell 2. Other year files have different column names and values.
- Check that factors_to_plot contains all six factor names spelled exactly as they appear in your DataFrame columns.
help me understand my correlation rankings
✔️ Awesome, I've got everything!
Great. Confirm you have saved your notebook with all three new cells (Cells 8, 9, and 10).
ⓧ I'd like to double check the full code
Your notebook should now have Cells 1 through 10. Scroll through and confirm these three cells from this step are present in order:
- Cell 8: The correlation heatmap (defines factors and correlation_matrix, creates heatmap, saves correlation_heatmap.png).
- Cell 9: The multi-panel scatter plot (defines factors_to_plot, creates 2x3 subplots, saves six_factors.png).
- Cell 10: The key findings summary (builds correlations dict, sorts by absolute value, prints top 3 predictors and conclusion).
Secret mission
Build a Happiness Report Card
You know which factors predict happiness globally. But what does happiness actually look like country by country? In this extension, you will build a normalized report card that compares the top 5 happiest countries against the bottom 5 across all six factors.
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely locally, so there are no ongoing costs. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.
Resources you used:
- The happiness-investigation project folder on your Desktop (contains your notebook, dataset, virtual environment, and saved PNG figures).
- A running Jupyter Notebook server (if still active in your terminal).
Keep everything running
No action needed. Choose this if you are still actively building or want to keep exploring the data.
- Your Jupyter Notebook server stays running in the terminal where you launched it.
- Your virtual environment stays activated in that terminal session.
- All your saved PNG figures remain in the happiness-investigation folder on your Desktop.
Pause - I'll come back to this later
Shut down the running Jupyter server to free up your terminal. Your files stay on disk so you can resume later.
- Switch to the terminal where Jupyter Notebook is running.
- Press Ctrl+C to stop the Jupyter server. Confirm with y if prompted.
- Deactivate your virtual environment by running:
deactivate
To resume later, open a terminal in the happiness-investigation folder, run source venv/bin/activate, then run jupyter notebook.
Delete - I don't want to use this again
Remove all project files, the virtual environment, and all saved figures. This is a complete cleanup.
- If Jupyter is still running, press Ctrl+C in that terminal to stop it.
- Deactivate your virtual environment by running:
deactivate
- Delete the entire project folder. On macOS/Linux, run:
rm -rf ~/Desktop/happiness-investigation
- On Windows, run:
rmdir /s /q %USERPROFILE%\Desktop\happiness-investigation
This removes the notebook, dataset, virtual environment, and all saved PNG figures in one step.
Nice Work!
Nice Work!
You just completed a full data analytics investigation from raw CSV to polished visualizations and written conclusions. You took real World Happiness Report data, formed a hypothesis, tested it, found it incomplete, and uncovered the true drivers of national happiness.
You've learned how to:
- Test a hypothesis with data by building scatter plots, calculating correlation coefficients, and investigating outliers that break the pattern.
- Reveal hidden relationships using a correlation heatmap that showed Social support, GDP, and Healthy life expectancy as the strongest predictors of happiness.
- Communicate findings visually by producing publication-quality multi-panel figures that compare all six World Happiness Report factors side by side.
- Secret Mission: Built a normalized Happiness Report Card comparing the top 5 happiest countries against the bottom 5 across all six factors.
Ready to quiz yourself?