Project 2 — Dataset 3: Livestock Production (FAOSTAT)
Author
NOELLE
Published
October 1, 2026
Introduction
My classmate Bowen Cho posted about using FAOSTAT’s livestock production data on the Week 5 Discussion 5A board — he’d worked with it years ago using base R loops and wanted to redo it with tidyverse. He didn’t attach a file, so I pulled real data from FAOSTAT’s own bulk download myself, since it’s the same public source he named.
Approach
FAOSTAT’s full livestock file covers every country and product since 1961, which is way more than I need, so I will first cut it down to a manageable slice — 8 countries, 4 livestock products, years 2000 through 2023 — and save that as the raw wide CSV, keeping the year-by-column layout FAOSTAT itself uses. Then I will bring it into R and reshape the year columns into a proper year variable with pivot_longer(), keeping missing production values as NA. Since Bowen’s whole point was comparing an old, slow way of doing this to a modern one, I will also time a base R loop against the tidyverse version, the way he described. Once it’s tidy, I will look at how production of each product changed from 2000 to 2023 and which country produces the most of any one product.
Data source
FAO FAOSTAT, “Crops and Livestock Products” domain, bulk download: https://www.fao.org/faostat/en/#data/QCL. I downloaded the full bulk file (Production_Crops_Livestock_E_All_Data.zip, updated December 2025) and filtered it down to the Production element (in tonnes) for four livestock products — cattle meat, chicken meat, cow milk, and hen eggs — across eight countries (Australia, Brazil, China, France, Germany, India, Kenya, and the United States), for the years 2000–2023.
Data structure before tidying
The raw file has 32 rows (8 countries × 4 products) and 26 columns: Country, Item, and then one column per year from 2000 to 2023, holding the production quantity in tonnes. It’s wide because each year is a separate column instead of year being its own variable.
# A tibble: 32 × 5
Country Item `2000` `2010` `2023`
<chr> <chr> <dbl> <dbl> <dbl>
1 Australia Cattle meat 1987902 2128598 2234836
2 Australia Chicken meat 610000 934352 1413189
3 Australia Cow milk 10847000 9023000 8467000
4 Australia Hen eggs 143000 174000 240010
5 Brazil Cattle meat 6578800 6977484 8962423
6 Brazil Chicken meat 5980600 10692556 13293837
7 Brazil Cow milk 20379988 31636924 36305658
8 Brazil Hen eggs 1732489 2252054 3440030
9 China Cattle meat 4642024 5676946 6789201
10 China Chicken meat 9064191 12184797 15483431
# ℹ 22 more rows
The table contains 32 rows and 26 columns, as expected. Some production values are missing, and I will preserve those missing values when reshaping the data.
# A tibble: 768 × 4
country item year tonnes
<chr> <chr> <int> <dbl>
1 Australia Cattle meat 2000 1987902
2 Australia Cattle meat 2001 2078900
3 Australia Cattle meat 2002 2089742
4 Australia Cattle meat 2003 1997563
5 Australia Cattle meat 2004 2112892
6 Australia Cattle meat 2005 2090272
7 Australia Cattle meat 2006 2188318
8 Australia Cattle meat 2007 2168946
9 Australia Cattle meat 2008 2138328
10 Australia Cattle meat 2009 2106385
# ℹ 758 more rows
rename_with(tolower) lowercases Country and Item so every column name in the tidy table is lowercase, matching the rest of the project.
pivot_longer() is the reshape: the 24 year columns collapse into two columns, year (which year it is) and tonnes (the production number for that country, product, and year).
as.integer(year) turns the year, which comes out of the column names as text, back into a real number so it sorts and plots correctly.
There are 30 missing values out of 768 observations (3.9%). These are all 24 years of India’s cattle meat series and six years of France’s hen egg series. The CSV does not explain why these values are missing. I will keep them as NA because missing data does not mean zero production. The totals below exclude missing values, so they represent the available observations.
A detour: comparing the old way to the new way
Bowen’s post mentioned he used to reshape FAOSTAT data with base R loops, so here’s that comparison — a loop-based reshape against pivot_longer(), both doing the exact same wide-to-long job, timed with system.time().
Code
base_r_reshape <-function(df) { year_cols <-setdiff(names(df), c("Country", "Item")) out <-data.frame()for (i inseq_len(nrow(df))) {for (yr in year_cols) { out <-rbind(out, data.frame(country = df$Country[i],item = df$Item[i],year =as.integer(yr),tonnes = df[[yr]][i] )) } } out}time_base_r <-system.time(base_r_reshape(raw))time_tidyverse <-system.time( raw |>rename_with(tolower) |>pivot_longer(cols =-c(country, item), names_to ="year", values_to ="tonnes") |>mutate(year =as.integer(year)))tibble(Approach =c("Base R loop (rbind row by row)", "tidyverse pivot_longer()"),`Time (sec)`=c(time_base_r[["elapsed"]], time_tidyverse[["elapsed"]])) |> knitr::kable()
Approach
Time (sec)
Base R loop (rbind row by row)
0.131
tidyverse pivot_longer()
0.003
The loop adds rows one at a time using rbind(), while pivot_longer() reshapes the year columns in one operation. The table shows the elapsed time for one run of each method on this dataset. To draw conclusions about performance on larger datasets, I would repeat the timing with larger samples.
Analytical methods
I will answer two questions using the tidy long table:
2000 vs. 2023 — the percentage change in total production for each livestock product, using the available data from the eight-country sample.
Largest production quantities — the five largest country/product combinations in 2023.
ggplot(change, aes(x =reorder(item, pct_change), y = pct_change)) +geom_col(fill ="#1F4E79") +coord_flip() +labs(title ="Change in production, 2000 to 2023 (8-country sample)",x =NULL, y ="% change") +theme_minimal()
Across the available observations in this eight-country sample, cow milk production increased by 87.5%, chicken meat by 78.2%, hen eggs by 66.8%, and cattle meat by 15.9%. Cow milk had the largest percentage increase, while cattle meat had the smallest. These comparisons describe the sample and do not establish why production changed. France’s hen egg value is missing in 2023, so the egg totals do not include the same countries in both years.
India’s cow milk production is the largest figure in this sample, at 127,105,138 tonnes in 2023. The next largest is United States cow milk production, at 102,653,065 tonnes. India’s figure is about 24% higher.
Conclusions
Cow milk and chicken meat had the largest percentage increases in this sample, while cattle meat grew more slowly. The data show how production changed, but additional information would be needed to explain those changes.
The timing comparison shows how long each reshaping method took in one run on a small dataset. I would repeat the comparison with larger datasets before making a broader claim about speed.
I kept missing values as NA because an unavailable value is not the same as zero production. Missing observations also limit the comparisons: France’s hen egg production is included in the 2000 total but unavailable for 2023.
I would extend the analysis by including more countries and comparing production per person. For the growth comparison, I would also restrict each product to countries with data in both 2000 and 2023.
AI Use
Anthropic. (2026). Claude Sonnet 5 [Large language model]. https://claude.ai. Accessed September 30, 2026.
I used Claude to proofread my writing and help me find mistakes in my R code. I reviewed the suggestions and checked the changes before including them.