library(jsonlite)
library(tidyverse)More JSON Practice: Nobel Prize Data
Overview
For this assignment we will use the public Nobel Prize API (version 2.1) to pull laureate data in JSON format, transform it into a tidy data frame in R, and answer four questions:
- What is the gender breakdown of Nobel laureates?
- Which countries have produced the most laureates?
- How often is a prize shared?
- How old were laureates when they won, and how does that relate to the size of the team and the size of the prize?
Obtaining the JSON data
The API returns laureates in pages, so we loop through the pages using offset until a page comes back empty. The JSON is kept as nested lists (simplifyVector = FALSE) so we can control exactly how it gets flattened.
base_url <- "https://api.nobelprize.org/2.1/laureates"
# function to request one page of 100 laureates and return the list of laureates
get_page <- function(offset, limit = 100) {
url <- sprintf("%s?offset=%d&limit=%d", base_url, offset, limit)
fromJSON(url, simplifyVector = FALSE)$laureates
}
# keep requesting pages until there is nothing left
all_laureates <- list()
offset <- 0
repeat {
page <- get_page(offset)
if (length(page) == 0) break
all_laureates <- c(all_laureates, page)
offset <- offset + 100
}
length(all_laureates)[1] 1028
Tidying the JSON
A laureate can have more than one prize (Marie Curie won twice), so we build one row per laureate and prize. Organizations such as the Red Cross have no gender or birth date, so those fields will be NA for them.
get_value <- function(x, ...) {
v <- purrr::pluck(x, ..., .default = NA)
if (is.null(v) || length(v) == 0) NA else v
}
# convert the portion of a prize ("1", "1/2", "1/3", "1/4") into a number
portion_to_number <- function(x) {
map_dbl(x, function(s) {
if (is.na(s)) return(NA_real_)
p <- as.numeric(strsplit(s, "/")[[1]])
if (length(p) == 1) p else p[1] / p[2]
})
}
# one row for each prize each laureate received
nobel <- map_dfr(all_laureates, function(l) {
map_dfr(l$nobelPrizes, function(p) {
tibble(
id = get_value(l, "id"),
name = coalesce(get_value(l, "knownName", "en"),
get_value(l, "orgName", "en")),
gender = get_value(l, "gender"),
birth_date = get_value(l, "birth", "date"),
birth_country = get_value(l, "birth", "place", "countryNow", "en"),
award_year = as.integer(get_value(p, "awardYear")),
category = get_value(p, "category", "en"),
portion = get_value(p, "portion"),
prize_adj = get_value(p, "prizeAmountAdjusted")
)
})
}) %>%
mutate(
# birth dates are sometimes partial so only the year is used
birth_year = suppressWarnings(as.integer(substr(birth_date, 1, 4))),
# approximate age at the time of the award
age = award_year - birth_year,
# the laureate's own share of the (inflation adjusted) prize money
share = portion_to_number(portion),
laureate_prize = prize_adj * share
) %>%
# team size is the number of laureates who received the same prize
group_by(award_year, category) %>%
mutate(team_size = n()) %>%
ungroup()
glimpse(nobel)Rows: 1,036
Columns: 14
$ id <chr> "745", "102", "779", "259", "1004", "114", "982", "981"…
$ name <chr> "A. Michael Spence", "Aage N. Bohr", "Aaron Ciechanover…
$ gender <chr> "male", "male", "male", "male", "male", "male", "male",…
$ birth_date <chr> "1943-11-07", "1922-06-19", "1947-10-01", "1926-08-11",…
$ birth_country <chr> "USA", "Denmark", "Israel", "Lithuania", NA, "Pakistan"…
$ award_year <int> 2001, 1975, 2004, 1982, 2021, 1979, 2019, 2019, 2009, 2…
$ category <chr> "Economic Sciences", "Physics", "Chemistry", "Chemistry…
$ portion <chr> "1/3", "1/3", "1/3", "1", "1", "1/3", "1/3", "1", "1/3"…
$ prize_adj <int> 15547541, 4304697, 14874529, 3923237, 12096939, 3778486…
$ birth_year <int> 1943, 1922, 1947, 1926, 1948, 1926, 1961, 1976, 1939, 1…
$ age <int> 58, 53, 57, 56, 73, 53, 58, 43, 70, 42, 36, 70, 52, 49,…
$ share <dbl> 0.3333333, 0.3333333, 0.3333333, 1.0000000, 1.0000000, …
$ laureate_prize <dbl> 5182514, 1434899, 4958176, 3923237, 12096939, 1259495, …
$ team_size <int> 3, 3, 3, 1, 1, 3, 3, 1, 3, 3, 2, 1, 1, 1, 1, 3, 3, 2, 3…
Question 1: Gender of laureates
Organizations do not have a gender, so they are left out. Each person is counted once even if they won more than once.
# overall count of unique people by gender
nobel %>%
filter(!is.na(gender)) %>%
distinct(id, gender) %>%
count(gender) %>%
mutate(percent = round(n / sum(n) * 100, 1))# A tibble: 2 × 3
gender n percent
<chr> <int> <dbl>
1 female 69 6.9
2 male 931 93.1
# count of prizes by category and gender
gender_category <- nobel %>%
filter(!is.na(gender)) %>%
count(category, gender) %>%
group_by(category) %>%
mutate(percent = round(n / sum(n) * 100, 1)) %>%
ungroup()
gender_category# A tibble: 12 × 4
category gender n percent
<chr> <chr> <int> <dbl>
1 Chemistry female 8 4
2 Chemistry male 194 96
3 Economic Sciences female 3 3
4 Economic Sciences male 98 97
5 Literature female 19 15.4
6 Literature male 104 84.6
7 Peace female 21 18.6
8 Peace male 92 81.4
9 Physics female 5 2.2
10 Physics male 226 97.8
11 Physiology or Medicine female 14 6
12 Physiology or Medicine male 221 94
ggplot(gender_category, aes(category, n, fill = gender)) +
geom_col(position = "dodge") +
labs(x = NULL, y = "Prizes awarded")Question 2: Countries of laureates
Here we use the country where each laureate was born, using the modern name of the country (countryNow) so that places like the former USSR are not counted separately.
# top 15 birth countries
top_countries <- nobel %>%
filter(!is.na(birth_country)) %>%
count(birth_country, sort = TRUE) %>%
slice_head(n = 15)
top_countries# A tibble: 15 × 2
birth_country n
<chr> <int>
1 USA 302
2 United Kingdom 95
3 Germany 86
4 France 64
5 Japan 31
6 Sweden 30
7 Poland 29
8 Russia 29
9 Canada 23
10 Italy 20
11 the Netherlands 20
12 Austria 19
13 Switzerland 19
14 Norway 13
15 China 12
ggplot(top_countries, aes(n, reorder(birth_country, n))) +
geom_col(fill = "steelblue") +
labs(x = "Prizes", y = NULL)Question 4: Age, team size, and prize size
Ages of 0 or less come from missing or partial birth dates and are removed. The prize size is each laureate’s own share of the inflation adjusted prize money, since a bigger team means a smaller piece for each person.
laureate_age <- nobel %>%
filter(!is.na(age), age > 0)
# overall summary of age at award
summary(laureate_age$age) Min. 1st Qu. Median Mean 3rd Qu. Max.
17.00 52.00 61.00 60.61 70.00 97.00
# age by team size
laureate_age %>%
group_by(team_size) %>%
summarise(laureates = n(),
mean_age = round(mean(age), 1),
median_age = median(age))# A tibble: 3 × 4
team_size laureates mean_age median_age
<int> <int> <dbl> <dbl>
1 1 346 59.6 60
2 2 292 59.6 60
3 3 367 62.4 62
ggplot(laureate_age, aes(factor(team_size), age)) +
geom_boxplot(fill = "lightblue") +
labs(x = "Laureates sharing the prize", y = "Age at award")# age against the size of the laureate's own prize
laureate_age %>%
filter(!is.na(laureate_prize)) %>%
ggplot(aes(age, laureate_prize)) +
geom_point(alpha = 0.3) +
geom_smooth(method = "lm") +
labs(x = "Age at award", y = "Laureate's share of prize (inflation adjusted)")`geom_smooth()` using formula = 'y ~ x'
# correlation of age with team size and with prize size
laureate_age %>%
summarise(age_vs_team_size = cor(age, team_size),
age_vs_prize = cor(age, laureate_prize, use = "complete.obs"))# A tibble: 1 × 2
age_vs_team_size age_vs_prize
<dbl> <dbl>
1 0.0953 0.151
Final Analysis
The gender breakdown should show that men make up the large majority of laureates, with women having the most representation in the Peace and Literature categories. The country counts should be led by the United States, followed by European countries such as the United Kingdom, Germany, and France. Counting by birth country also hides the fact that many laureates did their prize winning work somewhere else.
Sharing a prize is the norm rather than the exception in recent decades, and the percent of shared prizes has generally risen over time, especially in the science categories where discoveries often come from several researchers. This matters for the last question, because the money for a prize is fixed and each additional laureate means a smaller portion for everyone.
Finally, laureates tend to be in their late 50s or 60s when they win, which makes sense because many prizes recognize work that took decades to be accepted. Age should only be weakly related to team size, and the size of an individual prize depends much more on the year and on the number of people sharing it than on how old the winner is. One shortcoming is that age here is only approximate since it is the award year minus the birth year, and the inflation adjusted prize amount mostly reflects when the prize was won rather than who won it.