First I download the libraries I expect to use.
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.2.1 ✔ readr 2.2.0
## ✔ forcats 1.0.1 ✔ stringr 1.6.0
## ✔ ggplot2 4.0.3 ✔ tibble 3.3.1
## ✔ lubridate 1.9.5 ✔ tidyr 1.3.2
## ✔ purrr 1.2.2
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(dplyr)
library(ggplot2)
library(gt)
For this exercise I work with data about Life Expectancy from the World Bank. The data are in uploaded in GitHub. I retrieve them from there.
url <- "https://raw.githubusercontent.com/Enrique01234/607-Fall-2026/refs/heads/main/API_SP.DYN.LE00.IN_DS2_en_excel_v2_461099%20without%20metadata.csv"
Given the original data has three lines which are not needed for the exercise (e.g.the date of the latest update by the World Bank), they are skipped
lifex <- read_csv(file= url, show_col_types = FALSE, progress = FALSE, skip = 3)
lifex
## # A tibble: 265 × 70
## `Country Name` `Country Code` `Indicator Name` `Indicator Code` `1960` `1961`
## <chr> <chr> <chr> <chr> <dbl> <dbl>
## 1 Aruba ABW Life expectancy… SP.DYN.LE00.IN 64.0 64.2
## 2 Africa Easter… AFE Life expectancy… SP.DYN.LE00.IN 44.2 44.5
## 3 Afghanistan AFG Life expectancy… SP.DYN.LE00.IN 32.8 33.3
## 4 Africa Wester… AFW Life expectancy… SP.DYN.LE00.IN 37.8 38.1
## 5 Angola AGO Life expectancy… SP.DYN.LE00.IN 37.9 36.9
## 6 Albania ALB Life expectancy… SP.DYN.LE00.IN 56.4 57.5
## 7 Andorra AND Life expectancy… SP.DYN.LE00.IN 72.1 72.6
## 8 Arab World ARB Life expectancy… SP.DYN.LE00.IN 44.8 45.5
## 9 United Arab E… ARE Life expectancy… SP.DYN.LE00.IN 50.7 51.6
## 10 Argentina ARG Life expectancy… SP.DYN.LE00.IN 64.2 64.6
## # ℹ 255 more rows
## # ℹ 64 more variables: `1962` <dbl>, `1963` <dbl>, `1964` <dbl>, `1965` <dbl>,
## # `1966` <dbl>, `1967` <dbl>, `1968` <dbl>, `1969` <dbl>, `1970` <dbl>,
## # `1971` <dbl>, `1972` <dbl>, `1973` <dbl>, `1974` <dbl>, `1975` <dbl>,
## # `1976` <dbl>, `1977` <dbl>, `1978` <dbl>, `1979` <dbl>, `1980` <dbl>,
## # `1981` <dbl>, `1982` <dbl>, `1983` <dbl>, `1984` <dbl>, `1985` <dbl>,
## # `1986` <dbl>, `1987` <dbl>, `1988` <dbl>, `1989` <dbl>, `1990` <dbl>, …
There are two columns about the indicator (the name and the code) which are the same for all rows (countries). In order to save space and facilitate the manipulation of the file, I delete them and create a new data frame (lifex2). I use the column numbers because R was not recognizing the columns by their name.
lifex2<- lifex[-c(3,4)]
lifex2
## # A tibble: 265 × 68
## `Country Name` `Country Code` `1960` `1961` `1962` `1963` `1964` `1965`
## <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Aruba ABW 64.0 64.2 64.6 64.9 65.3 65.6
## 2 Africa Eastern and … AFE 44.2 44.5 44.9 45.2 45.5 45.8
## 3 Afghanistan AFG 32.8 33.3 33.8 34.2 34.7 35.1
## 4 Africa Western and … AFW 37.8 38.1 38.7 38.9 39.2 39.5
## 5 Angola AGO 37.9 36.9 37.2 37.4 37.7 38.0
## 6 Albania ALB 56.4 57.5 58.5 59.5 60.4 61.3
## 7 Andorra AND 72.1 72.6 73.0 73.3 73.6 73.9
## 8 Arab World ARB 44.8 45.5 46.0 47.0 47.6 48.1
## 9 United Arab Emirates ARE 50.7 51.6 52.5 53.5 54.5 55.5
## 10 Argentina ARG 64.2 64.6 64.6 64.9 64.8 65.1
## # ℹ 255 more rows
## # ℹ 60 more variables: `1966` <dbl>, `1967` <dbl>, `1968` <dbl>, `1969` <dbl>,
## # `1970` <dbl>, `1971` <dbl>, `1972` <dbl>, `1973` <dbl>, `1974` <dbl>,
## # `1975` <dbl>, `1976` <dbl>, `1977` <dbl>, `1978` <dbl>, `1979` <dbl>,
## # `1980` <dbl>, `1981` <dbl>, `1982` <dbl>, `1983` <dbl>, `1984` <dbl>,
## # `1985` <dbl>, `1986` <dbl>, `1987` <dbl>, `1988` <dbl>, `1989` <dbl>,
## # `1990` <dbl>, `1991` <dbl>, `1992` <dbl>, `1993` <dbl>, `1994` <dbl>, …
In the first two rows we can see that the column “Country Names” includes country groupings (e.g. Africa, Eastern and Southern). As they are not countries, I do not want to include them in the data frame, nor do I want to delete them by hand.
Fortunately, the World Bank provides the metadata. In that worksheet (I have saved them separately in GitHub) the country groupings have no information about the region. Some of them (like in the example in the previous paragraph) are sub-regions. However, in other cases (such as low income countries) they span more than one region.
I get the data from GitHub and create a new data frame (lifexMETAD).
url<- "https://raw.githubusercontent.com/Enrique01234/607-Fall-2026/refs/heads/main/Lifex%20Metadata.csv"
lifexMETAD<- read_csv(file= url, show_col_types = FALSE, progress = FALSE)
lifexMETAD
## # A tibble: 264 × 2
## `Country Code` Region
## <chr> <chr>
## 1 ABW Latin America & Caribbean
## 2 AFE <NA>
## 3 AFG Middle East & North Africa
## 4 AFW <NA>
## 5 AGO Sub-Saharan Africa
## 6 ALB Europe & Central Asia
## 7 AND Europe & Central Asia
## 8 ARB <NA>
## 9 ARE Middle East & North Africa
## 10 ARG Latin America & Caribbean
## # ℹ 254 more rows
Clearly, the rows which are not countries have “NA” in the Region column.
I (left)merge the two files because I plan to use these “NAs” to filter out the rows which are not countries. The merged data frame is called “lifex3”
lifex3<- lifex2|>
left_join(lifexMETAD)
## Joining with `by = join_by(`Country Code`)`
R automatically selected “Country Code” as the key variable to merge the files.
lifex3
## # A tibble: 265 × 69
## `Country Name` `Country Code` `1960` `1961` `1962` `1963` `1964` `1965`
## <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Aruba ABW 64.0 64.2 64.6 64.9 65.3 65.6
## 2 Africa Eastern and … AFE 44.2 44.5 44.9 45.2 45.5 45.8
## 3 Afghanistan AFG 32.8 33.3 33.8 34.2 34.7 35.1
## 4 Africa Western and … AFW 37.8 38.1 38.7 38.9 39.2 39.5
## 5 Angola AGO 37.9 36.9 37.2 37.4 37.7 38.0
## 6 Albania ALB 56.4 57.5 58.5 59.5 60.4 61.3
## 7 Andorra AND 72.1 72.6 73.0 73.3 73.6 73.9
## 8 Arab World ARB 44.8 45.5 46.0 47.0 47.6 48.1
## 9 United Arab Emirates ARE 50.7 51.6 52.5 53.5 54.5 55.5
## 10 Argentina ARG 64.2 64.6 64.6 64.9 64.8 65.1
## # ℹ 255 more rows
## # ℹ 61 more variables: `1966` <dbl>, `1967` <dbl>, `1968` <dbl>, `1969` <dbl>,
## # `1970` <dbl>, `1971` <dbl>, `1972` <dbl>, `1973` <dbl>, `1974` <dbl>,
## # `1975` <dbl>, `1976` <dbl>, `1977` <dbl>, `1978` <dbl>, `1979` <dbl>,
## # `1980` <dbl>, `1981` <dbl>, `1982` <dbl>, `1983` <dbl>, `1984` <dbl>,
## # `1985` <dbl>, `1986` <dbl>, `1987` <dbl>, `1988` <dbl>, `1989` <dbl>,
## # `1990` <dbl>, `1991` <dbl>, `1992` <dbl>, `1993` <dbl>, `1994` <dbl>, …
Comparing with the lifex 2 tibble, it can be seen a new column has been added (from 68 to 69 columns).
In order to make sure I can filter and keep the countries, I explore which rows are the ones which have “NAs” in the Region column. I use a new data frame: lifextry.
lifextry<-lifex3|>
filter(is.na(Region))
lifextry
## # A tibble: 48 × 69
## `Country Name` `Country Code` `1960` `1961` `1962` `1963` `1964` `1965`
## <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Africa Eastern and … AFE 44.2 44.5 44.9 45.2 45.5 45.8
## 2 Africa Western and … AFW 37.8 38.1 38.7 38.9 39.2 39.5
## 3 Arab World ARB 44.8 45.5 46.0 47.0 47.6 48.1
## 4 Central Europe and … CEB 67.9 68.2 67.7 68.7 69.2 69.4
## 5 Caribbean small sta… CSS 60.3 60.7 61.4 61.7 62.1 62.4
## 6 East Asia & Pacific… EAP 37.8 43.1 51.2 51.8 52.4 52.3
## 7 Early-demographic d… EAR 46.9 47.4 47.9 48.3 48.7 47.8
## 8 East Asia & Pacific EAS 41.7 46.3 53.3 53.9 54.5 54.4
## 9 Europe & Central As… ECA 60.9 61.4 61.8 62.2 62.5 62.8
## 10 Europe & Central As… ECS 67.4 67.7 67.7 68.0 68.5 68.6
## # ℹ 38 more rows
## # ℹ 61 more variables: `1966` <dbl>, `1967` <dbl>, `1968` <dbl>, `1969` <dbl>,
## # `1970` <dbl>, `1971` <dbl>, `1972` <dbl>, `1973` <dbl>, `1974` <dbl>,
## # `1975` <dbl>, `1976` <dbl>, `1977` <dbl>, `1978` <dbl>, `1979` <dbl>,
## # `1980` <dbl>, `1981` <dbl>, `1982` <dbl>, `1983` <dbl>, `1984` <dbl>,
## # `1985` <dbl>, `1986` <dbl>, `1987` <dbl>, `1988` <dbl>, `1989` <dbl>,
## # `1990` <dbl>, `1991` <dbl>, `1992` <dbl>, `1993` <dbl>, `1994` <dbl>, …
As I cannot see all of the rows, I add the instruction to see all 48 of them.
print (lifextry, n = 48)
## # A tibble: 48 × 69
## `Country Name` `Country Code` `1960` `1961` `1962` `1963` `1964` `1965`
## <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Africa Eastern and … AFE 44.2 44.5 44.9 45.2 45.5 45.8
## 2 Africa Western and … AFW 37.8 38.1 38.7 38.9 39.2 39.5
## 3 Arab World ARB 44.8 45.5 46.0 47.0 47.6 48.1
## 4 Central Europe and … CEB 67.9 68.2 67.7 68.7 69.2 69.4
## 5 Caribbean small sta… CSS 60.3 60.7 61.4 61.7 62.1 62.4
## 6 East Asia & Pacific… EAP 37.8 43.1 51.2 51.8 52.4 52.3
## 7 Early-demographic d… EAR 46.9 47.4 47.9 48.3 48.7 47.8
## 8 East Asia & Pacific EAS 41.7 46.3 53.3 53.9 54.5 54.4
## 9 Europe & Central As… ECA 60.9 61.4 61.8 62.2 62.5 62.8
## 10 Europe & Central As… ECS 67.4 67.7 67.7 68.0 68.5 68.6
## 11 Euro area EMU 69.3 69.7 69.6 69.8 70.4 70.5
## 12 European Union EUU 69.1 69.4 69.3 69.6 70.2 70.3
## 13 High income HIC 68.3 68.7 68.7 68.9 69.4 69.4
## 14 Heavily indebted po… HPC 40.8 41.1 41.7 41.9 42.3 42.6
## 15 IBRD only IBD 45.9 48.6 52.5 53.0 53.5 53.2
## 16 IDA & IBRD total IBT 45.3 47.7 51.0 51.5 52.0 51.8
## 17 IDA total IDA 42.6 43.1 43.7 44.0 44.6 44.9
## 18 IDA blend IDB 42.5 43.1 44.0 44.6 45.2 45.7
## 19 IDA only IDX 42.6 43.1 43.6 43.8 44.4 44.5
## 20 Not classified INX NA NA NA NA NA NA
## 21 Latin America & Car… LAC 54.5 55.1 55.5 55.9 56.4 56.8
## 22 Latin America & Car… LCN 55.1 55.6 56.0 56.4 56.8 57.3
## 23 Least developed cou… LDC 41.2 41.6 42.1 42.2 42.8 42.9
## 24 Low income LIC 41.3 41.6 42.0 42.2 42.7 42.9
## 25 Lower middle income LMC 44.7 45.1 45.6 46.0 46.4 45.9
## 26 Low & middle income LMY 43.5 46.0 49.6 50.1 50.6 50.4
## 27 Late-demographic di… LTE 43.9 48.3 54.9 55.4 56.0 56.4
## 28 Middle East, North … MEA 43.5 44.7 45.2 45.8 46.6 47.2
## 29 Middle income MIC 43.6 46.3 50.1 50.6 51.1 50.9
## 30 Middle East, North … MNA 43.4 44.2 44.7 45.7 46.5 47.1
## 31 North America NAC 69.9 70.4 70.2 70.0 70.3 70.4
## 32 OECD members OED 67.2 67.6 67.6 67.8 68.2 68.4
## 33 Other small states OSS 57.3 57.8 58.2 58.1 58.6 59.3
## 34 Pre-demographic div… PRE 39.9 40.2 40.7 41.1 41.4 41.7
## 35 Pacific island smal… PSS 54.9 55.5 56.1 56.6 56.8 57.9
## 36 Post-demographic di… PST 68.8 69.3 69.3 69.4 69.9 70.0
## 37 South Asia SAS 45.6 45.9 46.3 46.5 46.9 45.8
## 38 Sub-Saharan Africa … SSA 41.4 41.7 42.2 42.5 42.8 43.1
## 39 Sub-Saharan Africa SSF 41.4 41.7 42.2 42.5 42.9 43.1
## 40 Small states SST 57.9 58.4 58.9 59.0 59.4 60.1
## 41 East Asia & Pacific… TEA 37.6 42.9 51.1 51.7 52.3 52.2
## 42 Europe & Central As… TEC 64.7 65.0 65.1 65.7 66.1 66.2
## 43 Latin America & the… TLA 54.6 55.1 55.6 56.0 56.4 56.9
## 44 Middle East, North … TMN 43.4 44.2 44.7 45.7 46.5 47.1
## 45 South Asia (IDA & I… TSA 45.6 45.9 46.3 46.5 46.9 45.8
## 46 Sub-Saharan Africa … TSS 41.4 41.7 42.2 42.5 42.9 43.1
## 47 Upper middle income UMC 42.9 47.0 53.0 53.6 54.1 54.1
## 48 World WLD 50.9 52.8 55.3 55.7 56.1 55.9
## # ℹ 61 more variables: `1966` <dbl>, `1967` <dbl>, `1968` <dbl>, `1969` <dbl>,
## # `1970` <dbl>, `1971` <dbl>, `1972` <dbl>, `1973` <dbl>, `1974` <dbl>,
## # `1975` <dbl>, `1976` <dbl>, `1977` <dbl>, `1978` <dbl>, `1979` <dbl>,
## # `1980` <dbl>, `1981` <dbl>, `1982` <dbl>, `1983` <dbl>, `1984` <dbl>,
## # `1985` <dbl>, `1986` <dbl>, `1987` <dbl>, `1988` <dbl>, `1989` <dbl>,
## # `1990` <dbl>, `1991` <dbl>, `1992` <dbl>, `1993` <dbl>, `1994` <dbl>,
## # `1995` <dbl>, `1996` <dbl>, `1997` <dbl>, `1998` <dbl>, `1999` <dbl>, …
None of the rows are a country. Then, I proceed to filter them out and create a new data frame (lifex4).
lifex4<-lifex3|>
filter_out(is.na(Region))
lifex4
## # A tibble: 217 × 69
## `Country Name` `Country Code` `1960` `1961` `1962` `1963` `1964` `1965`
## <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Aruba ABW 64.0 64.2 64.6 64.9 65.3 65.6
## 2 Afghanistan AFG 32.8 33.3 33.8 34.2 34.7 35.1
## 3 Angola AGO 37.9 36.9 37.2 37.4 37.7 38.0
## 4 Albania ALB 56.4 57.5 58.5 59.5 60.4 61.3
## 5 Andorra AND 72.1 72.6 73.0 73.3 73.6 73.9
## 6 United Arab Emirates ARE 50.7 51.6 52.5 53.5 54.5 55.5
## 7 Argentina ARG 64.2 64.6 64.6 64.9 64.8 65.1
## 8 Armenia ARM 59.1 59.5 59.9 60.3 60.7 61.1
## 9 American Samoa ASM 65.1 65.6 66.0 66.2 66.1 66.0
## 10 Antigua and Barbuda ATG 62.6 63.3 64.0 64.7 65.4 66.2
## # ℹ 207 more rows
## # ℹ 61 more variables: `1966` <dbl>, `1967` <dbl>, `1968` <dbl>, `1969` <dbl>,
## # `1970` <dbl>, `1971` <dbl>, `1972` <dbl>, `1973` <dbl>, `1974` <dbl>,
## # `1975` <dbl>, `1976` <dbl>, `1977` <dbl>, `1978` <dbl>, `1979` <dbl>,
## # `1980` <dbl>, `1981` <dbl>, `1982` <dbl>, `1983` <dbl>, `1984` <dbl>,
## # `1985` <dbl>, `1986` <dbl>, `1987` <dbl>, `1988` <dbl>, `1989` <dbl>,
## # `1990` <dbl>, `1991` <dbl>, `1992` <dbl>, `1993` <dbl>, `1994` <dbl>, …
Lifex4 has 217 and lifex 3 has 265. The difference of 48 is the number of rows in lifextry. I have laso inspected visually the 217 rows but it is too long to print here. They are all countries or semi-autonomus areas (like Aruba).
Lifex4 is the data frame to tidy. A new data frame is created with long-pivot and called “pivlifex”.
pivlifex<- lifex4|>
pivot_longer (!c(1,2, Region), names_to = "Year", values_to = "Life Expectancy")
pivlifex
## # A tibble: 14,322 × 5
## `Country Name` `Country Code` Region Year `Life Expectancy`
## <chr> <chr> <chr> <chr> <dbl>
## 1 Aruba ABW Latin America & Caribb… 1960 64.0
## 2 Aruba ABW Latin America & Caribb… 1961 64.2
## 3 Aruba ABW Latin America & Caribb… 1962 64.6
## 4 Aruba ABW Latin America & Caribb… 1963 64.9
## 5 Aruba ABW Latin America & Caribb… 1964 65.3
## 6 Aruba ABW Latin America & Caribb… 1965 65.6
## 7 Aruba ABW Latin America & Caribb… 1966 66.1
## 8 Aruba ABW Latin America & Caribb… 1967 66.4
## 9 Aruba ABW Latin America & Caribb… 1968 66.7
## 10 Aruba ABW Latin America & Caribb… 1969 67.1
## # ℹ 14,312 more rows
Now pivlifex is a tidy data frame. Each column is a variable and each variable is a column. Also, each observation is a row and each row is an observation while each value is a cell and each cell is a single value.
Given the Region category is included, I explore the top performer (highest life expectancy) per region. I do this irrespective of the year, assuming the most recent year with data would show the highest value.
pivlifex|>
group_by(Region)|>
top_n(1)
## Selecting by Life Expectancy
## # A tibble: 7 × 5
## # Groups: Region [7]
## `Country Name` `Country Code` Region Year `Life Expectancy`
## <chr> <chr> <chr> <chr> <dbl>
## 1 Bermuda BMU North America 2024 82.5
## 2 Hong Kong SAR, China HKG East Asia & Pacif… 2021 85.5
## 3 Kuwait KWT Middle East & Nor… 2024 84.6
## 4 Monaco MCO Europe & Central … 2024 86.5
## 5 Maldives MDV South Asia 2024 81.3
## 6 Puerto Rico (US) PRI Latin America & C… 2024 81.9
## 7 Seychelles SYC Sub-Saharan Africa 2020 77.2
Then, I do the same for the countries with the lowest life expectancy. I expect these worst performances to be the ones in 1960.
pivlifex|>
group_by(Region)|>
top_n(-1)
## Selecting by Life Expectancy
## # A tibble: 7 × 5
## # Groups: Region [7]
## `Country Name` `Country Code` Region Year `Life Expectancy`
## <chr> <chr> <chr> <chr> <dbl>
## 1 Afghanistan AFG Middle East & North Afr… 1984 31.5
## 2 Bangladesh BGD South Asia 1971 26.5
## 3 Bermuda BMU North America 1960 66.5
## 4 Haiti HTI Latin America & Caribbe… 1963 42.7
## 5 Cambodia KHM East Asia & Pacific 1977 11.3
## 6 South Sudan SSD Sub-Saharan Africa 1988 11.0
## 7 Kosovo XKX Europe & Central Asia 1962 48.4
The highest life expectancy is actually observed in the most recent years, as expected. However, some of the lowest ones are recorded in the 1970s and 1980s. This may be the result of internal wars or long periods of economic decline.