Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

In this assignment we study how health and wealth relate across the world, using indicators published by the World Bank. The data comes in several separate files, so we will

Loading...
Loading...

the data

there are four files in the data/ folder

fileone row permain column
life-expectancy.csvcountry and yearlife_exp (years)
gdp-per-capita.csvcountry and yeargdp_per_capita (current US$)
population.csvcountry and yearpopulation
countries.csvcountryregion, income_level

loading

load each of the four files into a dataframe, named respectively life, gdp, pop and countries
and have a look at each of them: shape, first rows, types

content

to better understand what we have at hand, answer these questions

  1. how many rows do we have in each table

  2. the three indicator files have the same columns country_code, country, year
    what are the rows that identify a measure uniquely ? so, what are the two columns we need to merge on ?

merging

how many rows for the merge ?

we’re trying to foresee how many rows a merge would yield

the three files do not have the same number of rows: what does that tell you ?
it is not enough to conclude by itself, we need to know how the keys of the 3 dataframes compare:

  1. how do the keys of life and pop compare ?

  2. same question for life and gdp ?

  3. and so, how many rows do you expect from an inner merge

  4. and from an outer merge ? how many undefined data will the outer merge contain ?

merging the indicators

merge life, gdp and pop into a single dataframe indicators, with these 6 columns

country_code, country, year, life_exp, gdp_per_capita, population

do this twice:

  1. once with the default merge,

  2. and once with how="outer"

  3. compare the number of rows with the expectations

  4. finally check for missing data in both; does that add up with the previous question ?

which kind of merge do you prefer here, and why ?
(for the rest of the assignment, we keep the outer version, to see the missing values)

adding the regions

now we want to add the region and income_level columns to indicators, taken from countries
this time we merge on one column only: country_code

  1. how many rows do you expect in the result ?
    (for a given country, is there one row or several in the countries table ?)

  2. merge, and call the result df; check that you get the number of rows you expected

  3. you now have both a country and a name column, that seem to contain the same thing; (optional step) double-check they genuinely are identical

  4. drop the name column

cleaning

  1. look at the values in the region column: value_counts() should show you something that is not a region

  2. look at some entries in this category, to check it’s safe to remove

  3. remove those entries

computing decade

as we have not yet seen how to deal with time-related data, we will add a column decade (like 1990, 2000, 2010, 2020) computed from year as an integer

groupby

  1. compute the average life expectancy for each region

  2. then do the same for each pair (region, decade)

  3. what is the type of the index of this last result ?

  4. look at the levels attribute of this index; what do you find in there ?

pivot_table

build a table with one row per region, one column per decade, and the average life expectancy in each cell

stack and unstack

you have just seen two ways to compute almost the same thing: the 2-level groupby result, and the pivot table
use unstack() on the former to obtain the latter, and check that they are equal
(see also DataFrame.equals, or pd.testing.assert_frame_equal)

and conversely, what does stack() do when applied on the pivot table ?

so, in a nutshell: pivot_table(index=A, columns=B, values=C) is the same as groupby([A, B])[C].mean().unstack()
(and the pivot table can do more, e.g. several aggregation functions or margins=True)

plotting with pandas

  1. use pivoted to draw one curve per region, showing how the life expectancy evolves over decades
    the result should look like this

  1. now a bar chart (kind="barh" or df.plot.barh()) of the average life expectancy per region for the most recent decade only (the column 2020)

  1. finally, a scatter plot of life_exp against gdp_per_capita for year 2019, using df.plot.scatter()
    use a log scale on the x axis (logx=True) to make it readable

pairplot

seaborn’s pairplot() draws, for a set of numerical columns:

see this page for a short intro to seaborn

  1. on the 2019 data, draw a pairplot of the columns life_exp, gdp_per_capita and population, with a color per region

  2. the result is not very readable; why ? try to fix this by using the logarithm of the two last columns (numpy.log10)

you should produce something like this:

optional: going further

how many countries are there in each (region, income level) pair ?
build the table with one row per region, one column per income level, and add the totals with margins=True

appendix: weighting by population

in the groupby section, we have computed the mean over countries
to get the life expectancy of the people of a region, each country should be weighted by its population

there is no weighted mean in groupby, but a weighted mean is sum(life_exp * population) / sum(population)
so compute these two sums by (region, decade), and divide them

appendix: where does the data come from

all the data was downloaded once from the World Bank open data API, restricted to the years 1990 to 2020

for example, with httpie, the life expectancy for all countries, in 2019 only, as json (format==json and the other == are query parameters)

http "https://api.worldbank.org/v2/country/all/indicator/SP.DYN.LE00.IN" format==json date==2019 per_page==300

for the years used here, replace the date with date==1990:2020, and use per_page==20000 to get everything in one page
the country metadata is at https://api.worldbank.org/v2/country
the script fetch-data.py, next to this notebook, does the whole job with Python’s requests library instead

rows with no value were dropped from the three indicator files, which is why they do not all cover the same country / year pairs