This vignette will guide you through the livestock-related data available within the Evidence for Resilient Agriculture (ERA) dataset. The dataset serves as a critical resource for understanding agricultural resilience, specifically focusing on livestock systems and their adaptation to climate change.
We will cover the following:
Methodology:
1. Search Terms Used
2. Screening Criteria Applied
3. Data Extraction Process
Exploration of Livestock Data in ERA:
1. Exploring geographic locations of studies available
2. Practices in changing diets
3. Understanding Diet compositions
4. Common outcomes reported
Our initial search, conducted in 2014, queried the Web of Science and Scopus Databases for English language articles. Database queries consisted of key terms describing three components: technology, outcome, and geographic location. Given the myriad outcomes of interest, we constructed distinct search strings for each category: productivity, resilience, and mitigation outcomes, as well as a key term related to barriers to adoption.
Our methodology employed Boolean operators ‘OR’ and ‘AND’ to ensure the comprehensiveness of the search. This string for each technology category was run in both search engines for each of the outcome categories, ‘productivity,’ ‘resilience,’ ‘mitigation,’ and additionally for ‘barriers’.
Below in a snippet of the search terms used for the livestock extraction:
Source | Subject | Query |
---|---|---|
WoS | Livestock | (Livestock OR ‘mono gastric’ OR cattle OR sheep OR goats OR pigs OR poultry OR ruminant OR aquaculture OR fish) AND (‘non-conventional feed’ OR ‘Forage productivity’ OR grass OR ‘pasture additive’ OR ‘grass-legume’ OR ‘feed conversion’ OR ‘feed intake’ OR ‘protein intake’ OR ‘energy intake’ OR ‘feed availability’ OR ‘feed supplement’ OR ’energy retention’ OR ’growth rate’ OR ’feed acceptability’ OR ’feeding frequency’ OR ’stover digestibility’ OR ’paddock’ OR ’freerange’ OR ‘hay’ OR ‘silage’ OR ‘fodder shrub’ OR ’nomadic’ OR pastoral OR ’signalgrass’ OR (pasture NEAR cerrado) OR ‘crop residue’ OR ‘animal husbandry’ OR ‘pasture species’ OR ‘crop-pasture’ OR ‘pasture crop’ OR ’zero graz’ OR ‘rotational graz’ OR ’conti graz’ OR ’stocking density’ OR ’organic livestock’ OR ‘ammonia volatil’ OR ’N-retention’ OR ’cover manure’ OR ‘biogas capture’ OR ‘Manure acidification’ OR ‘Cover* manure’ OR ‘Manure collection’ OR ‘manure treatment’ OR ‘artificial insemination’ OR ‘trait selection’ OR ‘heat period’ OR ovulation OR hybrid OR ‘desirable traits’ OR ‘progeny test’ OR ‘semen analysis’ OR ‘cross breed’ OR ’Aquasilviculture’ OR ’Integrated multi-trophic aquaculture’ OR ’Organic Aquaculture’ OR ’fishing intensity’ OR ’culture based fishery’ OR ’vulnerable’ OR ’susceptible’ OR ’resistan’ OR ‘quarantine’ OR ‘antibiotic’ OR ‘vaccine’ OR ‘dewormer’ OR ‘ectoparasite’ OR ‘innoculation’ OR ’agropasto’ OR (Livestock AND (antistress OR ‘anti-stress’))) |
Scopus | Livestock | (Livestock OR ‘mono gastric’ OR cattle OR sheep OR goats OR pigs OR poultry OR ruminant OR aquaculture OR fish) AND (‘non-conventional feed’ OR ‘Forage productivity’ OR grass OR ‘pasture additive’ OR ‘grass-legume’ OR ‘feed conversion’ OR ‘feed intake’ OR ‘protein intake’ OR ‘energy intake’ OR ‘feed availability’ OR ‘feed supplement’ OR ’energy retention’ OR ’growth rate’ OR ’feed acceptability’ OR ’feeding frequency’ OR ’stover digestibility’ OR ’paddock’ OR ’freerange’ OR ‘hay’ OR ‘silage’ OR ‘fodder shrub’ OR ’nomadic’ OR pastoral OR ’signalgrass’ OR (pasture W/ cerrado) OR ‘crop residue’ OR ‘animal husbandry’ OR ‘pasture species’ OR ‘crop-pasture’ OR ‘pasture crop’ OR ’zero graz’ OR ‘rotational graz’ OR ’conti graz’ OR ’stocking density’ OR ’organic livestock’ OR ‘ammonia volatil’ OR ’N-retention’ OR ’cover manure’ OR ‘biogas capture’ OR ‘Manure acidification’ OR ‘Cover* manure’ OR ‘Manure collection’ OR ‘manure treatment’ OR ‘artificial insemination’ OR ‘trait selection’ OR ‘heat period’ OR ovulation OR hybrid OR ‘desirable traits’ OR ‘progeny test’ OR ‘semen analysis’ OR ‘cross breed’ OR ’Aquasilviculture’ OR ’Integrated multi-trophic aquaculture’ OR ’Organic Aquaculture’ OR ’fishing intensity’ OR ’culture based fishery’ OR ’vulnerable’ OR ’susceptible’ OR ’resistan’ OR ‘quarantine’ OR ‘antibiotic’ OR ‘vaccine’ OR ‘dewormer’ OR ‘ectoparasite’ OR ‘innoculation’ OR ’agropasto’ OR (Livestock AND (antistress OR ‘anti-stress’))) |
This screening criteria is designed to systematically review papers focusing on livestock management and outcomes, applying both inclusion and exclusion criteria based on specific parameters. The framework ensures that the selected studies align with the objectives of the review, particularly within the context of livestock systems in Africa.
PICOS Framework The PICOS framework helps structure systematic reviews by defining key components: Population, Intervention, Comparison, Outcomes, and Study Design.
The criteria focus on studies involving livestock, excluding fish unless explicitly directed (fish are separated into a distinct category). The geographic scope is limited to studies conducted in Africa.
Eligible studies involve practices like: Pasture and grazing management. Feed experiments (including feed processing). Improved variety and herd density management. Manure management and related practices.
Studies should ideally have a control group, except for papers reporting diet and intake where controls are not mandatory.
The review emphasizes studies reporting primary data on key outcomes: Reproductive performance, economics, yields, soil quality, biodiversity, emissions, and labour. Studies that report surveys or specific emission-related outcomes in controlled environments are included, but consultation with the team is recommended.
Studies must focus on interventions and primary data collection. Lab experiments or greenhouse studies are excluded unless they focus on emission outcomes.
Data were extracted from tables, text, and figures. Figures were digitized using available software, such as Web Plot Digitizer (https://apps.automeris.io/wpd/).
The data was extracted using a macros enabled excel template:
There are 9 sections to this workbook:
1) Pub = Publication: capture bibliographic data about an experiment
here.
2) Site & Site Soils: captures information about experimental sites,
any detailed information about soil variables should be captured in
Site.Soils
3) ExpD = Experimental design, captures information about the number of
study replicates, plot sizes, etc.
4) Practices= These include the following:
-Time periods
-Breed: Capturing information on whether the breed used in the experiment is improved or indegenous.
-Diets: This is broken into 3 tables; Ingredients fed to the animals, Nutritional content of each ingredient and the digestibility of those ingredients.
-Chemicals, Vaccines, Medicines that were given to the animals during the experiment
Make.Trt = create & name treatments by combining the experimental practices entered in the practice tabs.
EnterData = Captures values and associated errors for the experimental treatments, products, outcomes and times specified in the previous tabs. Do not start data entry until you’ve completed all the previous tabs.
Data extractors went through an intensive 3 week training that included video tutorials and practical examples of extracting data with the livestock template.
Training materials can be found here: https://cgiar-my.sharepoint.com/:f:/g/personal/p_steward_cgiar_org/EmOgDnJmyX9KkD1s8rNqggsBlSDMvTavst0gd7VIwU3EMw?e=c1gHDf
As part of the extraction process of the ERA Livestock, we created an ontology. The livestock ontology is a structured, hierarchical framework that defines and categorizes terms, concepts, and relationships related to livestock systems. It includes standardized definitions for livestock species, breeds, production systems, management practices, feed types, environmental factors, health indicators, and outcomes. By providing a shared vocabulary, livestock ontology facilitates consistent and comprehensive data representation across studies.
Link to the ontology: https://doi.org/10.7910/dvn/75e7hv
The code used to generate and compile the dataset from the excel files is in the ERAg R package using R 4.2.1 (R Development Team) and may be downloaded from GitHub https://github.com/EiA2030/ERAg.
The data can be downloading using the following code:
#download most recent version of the data
# Set up connection to S3 bucket
dl_dir<-"downloaded_data"
s3<-s3fs::S3FileSystem$new(anonymous = T)
era_s3<-"s3://digital-atlas/era"
# List the files in the s3 bucket
files<-s3$dir_ls(file.path(era_s3,"data"))
# This is the most recent version of the datas3://digital-atlas/era/data/skinny_cow_2022-YYYY-MM-DD.RData (substitute most recent date into filepath)
files<-tail(grep(".RData",grep("skinny_cow_2022",files,value=T),value=T),1)
# Set a save location for the dataset (amend to something more suitable for your needs)
save_path<-file.path(getwd(),dl_dir,basename(files))
if(!file.exists(save_path)){
s3$file_download(files,save_path,overwrite = T)
}
livestock_metadata<- miceadds::load.Rdata2(file=basename(save_path),path=dirname(save_path))
The Evidence for Resilient Agriculture (ERA) dataset provides a comprehensive analysis of the impacts of technology adoption on 87 key indicators spanning productivity, resilience, and climate change mitigation. This dataset integrates data from both agronomy and livestock studies, with a focus on creating control-versus-treatment comparisons to enable robust meta-analysis.
To extract insights specific to livestock, it is necessary to subset the dataset to isolate livestock-related studies, as follows (unhide the code):
# Subset data
livestock_data<-ERA.Compiled[!is.na(Date)]
# Subset to fewer themes to simplify plot
livestock_data<-livestock_data[Product.Type %in% c("Animal")]
Practice codes
# Filter the data
filtered_data_prac <- ERAg::PracticeCodes %>%
filter(Theme == "Animals")
# Display the filtered data in a scrollable table
DT::datatable(
filtered_data_prac,
options = list(
scrollY = "400px", # Set vertical scroll height
scrollX = TRUE, # Enable horizontal scrolling
pageLength = 10, # Display 10 rows per page
fixedHeader = FALSE # Disable fixed headers
)
)
Outcome Codes
DT::datatable(
head(ERAg::OutcomeCodes), # Display the first few rows of PracticeCodes
options = list(
scrollY = "400px", # Set vertical scroll height
scrollX = TRUE, # Enable horizontal scrolling
pageLength = 10, # Display 10 rows per page
fixedHeader = FALSE # Disable fixed headers
)
)
Products
# Filter the data
filtered_data_eu <- ERAg::EUCodes %>%
filter(Product.Type == "Animal")
# Display the filtered data in a scrollable table
DT::datatable(
filtered_data_eu,
options = list(
scrollY = "400px", # Set vertical scroll height
scrollX = TRUE, # Enable horizontal scrolling
pageLength = 10, # Display 10 rows per page
fixedHeader = FALSE # Disable fixed headers
)
)
This dataset represents spatial information about livestock-related research papers across various countries in Africa. Each point on the map corresponds to a geographic location associated with a research paper, categorized by livestock type (e.g., cattle, goat, sheep, etc.). The dataset includes:
Country: The country where the research is associated. Latitude and Longitude: Coordinates representing the location of the research. Product.Simple: The type of livestock being studied. Counts: The number of papers associated with each location (aggregated for the map). This data provides an overview of the geographic distribution and focus of livestock research, enabling users to explore patterns and trends by livestock type and region.
# Create a leaflet map with interactive filtering
# Subset the livestock data
site_data <- livestock_data %>%
dplyr::select(Product.Simple, Country, Latitude, Longitude) %>%
distinct() %>%
mutate(
Longitude = as.numeric(Longitude),
Latitude = as.numeric(Latitude)
) %>%
filter(!is.na(Longitude) & !is.na(Latitude)) # Remove rows with missing coordinates
# Count the number of records per country
livestock_counts <- site_data %>%
group_by(Country,Product.Simple) %>%
summarise(N_Livestock = n())
# Prepare the world map with only African countries
world <- ne_countries(scale = "medium", returnclass = "sf") %>%
filter(continent == "Africa") %>% # Filter for African continent only
mutate(admin = if_else(admin == "United Republic of Tanzania", "Tanzania", admin))
# Ensure the CRS is consistent (EPSG:4326)
world <- st_transform(world, crs = 4326)
# Convert site data to spatial format (only for African countries)
site_data_filtered <- site_data %>%
filter(Country %in% world$admin) %>% # Filter site data for African countries
st_as_sf(coords = c("Longitude", "Latitude"), crs = 4326, remove = FALSE)
# Join livestock counts with African country geometries for mapping
countries_studies <- world %>%
dplyr::select(admin, geometry) %>%
rename(Country = admin) %>%
left_join(livestock_counts, by = "Country")
library(ggplot2)
# Filter data if necessary (e.g., removing missing values or specific subsets)
filtered_data <- site_data_filtered
# Create a static plot
static_plot <- ggplot() +
# Base map with neutral colors
geom_sf(data = countries_studies, fill = "gray92", color = "white") +
# Points for each product type, with colors determined by the product
geom_point(data = filtered_data,
aes(x = Longitude, y = Latitude, color = Product.Simple),
size = 3, alpha = 0.8) +
# Add annotation for context
annotate("text", x = 40, y = -30, label = "One Point = One Paper",
color = "#000000", size = 5, hjust = 0) +
# Customize the plot's appearance
theme_minimal() +
theme(
axis.line = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
legend.title = element_text(size = 12, face = "bold"),
legend.text = element_text(size = 10),
legend.position = "right" # Place the legend on the right
) +
# Set coordinate limits for the map
coord_sf(xlim = c(-20, 95), ylim = c(-40, 40), expand = FALSE) +
# Add legend title
labs(color = "Livestock Type",
title = "Distribution of Livestock Research Papers",
subtitle = "Each point represents a research paper")
# Display the static plot
print(static_plot)
The treemap represents the distribution of livestock categories reported within research papers that were extracted in ERA. Each rectangle corresponds to a livestock category, and its size is proportional to the number of papers reporting on that category. Here’s an analysis of the data shown:
Dominant Categories:
Sheep: The largest rectangle (120 papers) indicates that sheep are the most frequently reported livestock category in the dataset. This could reflect their significant role in agricultural research and practices.
Goats: Goats follow closely with 91 papers, showing their importance, likely due to their adaptability to diverse environments.
Moderately Represented Categories:
Cattle (96 papers) and Fish (71 papers) are also prominent, reflecting their widespread use in agricultural systems, both for food production and other economic activities.
# Prepare data for the treemap
products <- livestock_data %>%
distinct(Product.Simple, Code) %>% # Keep only unique combinations
group_by(Product.Simple) %>%
summarise(Row_Count = n(), .groups = "drop") # Count occurrences per category
# Create the static treemap
static_treemap <- ggplot(products, aes(
area = Row_Count, # Size of rectangles based on row count
fill = Row_Count, # Fill color based on row count
label = Product.Simple # Labels for each rectangle
)) +
geom_treemap() +
geom_treemap_text(
colour = "white", # Text color
place = "center", # Place text at the center of rectangles
grow = TRUE # Adjust text size to fit rectangles
) +
scale_fill_viridis_c() + # Apply Viridis color scale
labs(
title = "Distribution of Livestock Types", # Plot title
fill = "Row Count" # Legend title
) +
theme_minimal()
# Display the static treemap
print(static_treemap)
Outcome measures, units, and products were extracted as reported. Outcome codes differentiated plant parts (grain vs stover) and products from the same species (milk versus meat). Oftentimes authors discussed many treatments but only reported a few. The ways in which the outcomes were reported in the papers, by year or aggregation across practices, dictated which data were extracted and how treatments were coded. Experimental Units (EU) described the species or product that were measured (e.g., maize grain).
# Aggregate data by pillar and indicator
DATA_OUTCOMES <- livestock_data %>%
group_by(Out.Pillar, Out.Ind) %>%
summarise(N_Livestock = n_distinct(Code), .groups = "drop") %>%
rename(Pillar = Out.Pillar, Indicator = Out.Ind)
# Sort indicators within each pillar for better organization
DATA_OUTCOMES <- DATA_OUTCOMES %>%
arrange(Pillar, Indicator) %>%
mutate(
Indicator = factor(Indicator, levels = unique(Indicator)),
Pillar = factor(Pillar, levels = c("Productivity", "Resilience", "Mitigation"))
)
# Define hover information for specific indicators
hover_info <- tibble(
Indicator = c("Product Yield", "Income", "Economic Performance", "Costs", "Soil Quality", "Efficiency"),
HoverDetails = c("Meat yield; Egg yield; Milk yield; Reproductive yield; Weight gain", "Gross return; Gross margin; Net return", "Benefit Cost Ratios", "Labour cost; Total cost; Fixed cost; Variable cost", "Soil Erosion; Soil Nitrogen; Soil Organic Carbon", "Feed Conversion Ratios; Protein Conversion Ratios; Nitrogen Apparent Efficiency")
)
# Add hover text to the data
DATA_OUTCOMES <- DATA_OUTCOMES %>%
left_join(hover_info, by = "Indicator") %>% # Join hover info
mutate(
HoverText = ifelse(
!is.na(HoverDetails),
paste0(Indicator, "\n", HoverDetails, "\nCount: ", N_Livestock),
paste0(Indicator, "\nCount: ", N_Livestock)
)
)
# Define custom colors for each pillar
pillar_colors <- c(
"Productivity" = "#FFCC66",
"Resilience" = "#990000",
"Mitigation" = "#8B4513"
)
# Create the static faceted bar plot
static_plot <- ggplot(DATA_OUTCOMES, aes(
x = Indicator, y = N_Livestock, fill = Pillar
)) +
geom_col(width = 0.8) + # Vertical bars
labs(
y = "Number of Papers",
x = "Outcome Indicator",
title = "Number of Papers by Outcome Indicator and Pillar"
) +
scale_fill_manual(values = pillar_colors, na.value = "gray70") +
scale_y_continuous(limits = c(0, max(DATA_OUTCOMES$N_Livestock) + 50), expand = c(0, 0)) +
facet_wrap(~Pillar, scales = "free_x", nrow = 1) +
theme_minimal() +
theme(
axis.text.x = element_text(size = 10, angle = 45, hjust = 1), # Rotate x-axis labels
axis.text.y = element_text(size = 10), # Adjust y-axis labels
axis.title.x = element_text(size = 12, face = "bold", margin = margin(t = 10)), # Add margin
axis.title.y = element_text(size = 12, face = "bold"), # Y-axis title styling
legend.position = "none", # Remove legend
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
strip.text = element_text(size = 12, face = "bold"), # Facet strip labels styling
plot.margin = margin(10, 10, 10, 10) # Reduced margins
)
# Display the static plot
print(static_plot)
The objective of this analysis is to systematically analyze and explore the impact of livestock feeding practices, particularly tree fodder addition and substitution, on key outcomes (e.g., weight gain, feed intake). To achieve this, we will follow these steps:
1. Loading Required Libraries and Functions as shown earlier in this vignette. This will require the most recent version of the data, which will come from the S3 bucket
2.Data Cleaning and Filtering
2.1 Sub-setting the data to specific livestock (e.g., cattle, sheep, goats) and practices involving tree fodder addition or substitution.
2.2 Harmonizing Units: Converts weight units (e.g., kg to g per individual per day) for consistent analysis.
2.3 Exploring Practices and Outcomes: Summarizes data availability at the sub-practice and practice levels
3. Linking Observations to Metadata: The script links experimental data with metadata to understand: The specific diet fed to animals, nutritional composition and digestibility of the diet, Key fields in the metadata that describe treatments and practices.
4. Plotting simple figures
4.1 Plotting weight gain distribution with outliers
4.2 Plotting weight gain distribution without outliers
PLEASE UNHIDE THE CODES TO VIEW. You can use these to tailor to your own needs
This section retrieves the most recent version of the livestock dataset from an S3 bucket. It lists the available files, downloads the required .RData file, and loads it into R using miceadds::load.Rdata2. The objective is to prepare the dataset (livestock_metadata) for analysis.
# First we need to get the detailed dataset containing the management information that relates to the experiment, this is in the Atlas S3 Bucket
dl_dir<-"downloaded_data"
s3<-s3fs::S3FileSystem$new(anonymous = T)
era_s3<-"s3://digital-atlas/era"
# List the files in the s3 bucket
files<-s3$dir_ls(file.path(era_s3,"data"))
# This is the most recent version of the datas3://digital-atlas/era/data/skinny_cow_2022-YYYY-MM-DD.RData (substitute most recent date into filepath)
files<-tail(grep(".RData",grep("skinny_cow_2022",files,value=T),value=T),1)
# Set a save location for the dataset (amend to something more suitable for your needs)
save_path<-file.path(getwd(),dl_dir,basename(files))
if(!file.exists(save_path)){
s3$file_download(files,save_path,overwrite = T)
}
livestock_metadata<- miceadds::load.Rdata2(file=basename(save_path),path=dirname(save_path))
section focuses on filtering the dataset to a specific scope for analysis. It begins by identifying subpractices related to “Feed Addition” and “Feed Substitution” within the “Animals” theme, specifically those involving agroforestry. The data is then narrowed to livestock types of interest, such as cattle, goats, and sheep, and further refined to focus on key productivity outcomes, including “Meat Yield,” “Weight Gain,” and “Milk Yield.” The latest version of the dataset, stored as a .parquet file in the S3 bucket, is downloaded and processed using the same subsetting logic. Confounding practices, such as “Concentrates” and “Feed Crop,” are excluded to improve data quality.
focal_pracs<-PracticeCodes[Practice %in% c("Feed Addition","Feed Substitution")
& grepl("Agrofor",Subpractice),Code]
data<-ERA.Compiled[grepl(paste(focal_pracs,collapse = "|"),plist)]
# 1.2) Subset to cattle and small ruminants #####
focal_prods<-c("Cattle","Goat","Sheep")
data<-data[Product.Simple %in% focal_prods]
# 1.3) Subset to outcomes of interest #####
focal_out<-c("Meat Yield","Weight Gain","Milk Yield")
data<-data[Out.SubInd %in% focal_out]
# List the files in the s3 bucket
files<-s3$dir_ls(file.path(era_s3,"data"))
# This should be the most recent version of the data era_compiled-v1.0_2018-v1.1_2020-skinny_cow_2022_YYYY_MM_DD.parquet
files<-tail(grep("parquet",grep("era_compiled",files,value=T),value=T),1)
# Set a save location for the dataset (amend to something more suitable for your needs)
save_path<-file.path(getwd(),dl_dir,basename(files))
if(!file.exists(save_path)){
s3$file_download(files,save_path,overwrite = T)
}
ERA.Compiled_new<-arrow::read_parquet(save_path)
data_new<-ERA.Compiled_new[Version=="skinny_cow_2022" & grepl(paste(focal_pracs,collapse = "|"),plist)]
# Remove confounding subpractices
data_new<-data_new[!grepl("Concentrates|Feed Crop|Breed|Feed NonCrop",SubPrName)]
# The newer dataset on the s3 should be superior to old dataset so let's use it
data<-data_new
# Remove mechanical processing (this is just going to be chopping tree forages, probably not of interest)
data[,SubPrName:=gsub("-Feed Mech Process|-Grazing Cut & Carry","",SubPrName)]
# Looking at any practices than involve tree fodder addition or substitution
data<-data[,tree_fodder_add:=F][grepl("Feed AgFor (Add)",SubPrName,fixed=T),tree_fodder_add:=T]
data<-data[,tree_fodder_sub:=F][grepl("Feed AgFor (Sub)",SubPrName,fixed=T),tree_fodder_sub:=T]
Unit harmonization is essential when studying livestock papers to ensure consistency and comparability across diverse datasets. In livestock research, data on outcomes like feed intake or weight gain are often reported in various units, such as kilograms (kg), grams per individual per day (g/individual/day), or other measurement standards. Without harmonization, direct comparisons or meta-analyses become challenging, as the variability in units can introduce bias or inaccuracies in the interpretation of results. By standardizing units, such as converting weight data from kg to g/day or normalizing by the duration of the study, researchers can create a uniform dataset. This allows for meaningful cross-study comparisons, improved statistical analysis, and clearer insights into the impacts of specific practices on livestock productivity, such as weight gain or feed efficiency.
# 3.2) Harmonize units #####
data[Units=="kg" & !is.na(Duration),c("MeanT","MeanC","Units"):=.(round(1000*MeanT/(365*Duration),2),round(1000*MeanC/(365*Duration),2),"g/individual/day")
][Units=="kg/individual/day",c("MeanT","MeanC","Units"):=.(round(1000*MeanT,2),round(1000*MeanC,2),"g/individual/day")]
After subsetting the data, you can explore the number of remaining papers that are within your research focus. The following table The table provides insights into the data coverage for different livestock practices, products, and outcomes. For example, “Feed Addition” applied to “Goat” for “Weight Gain” is reported in 15 studies with 48 observations from 6 countries. This information can help identify well-studied areas and data gaps in livestock research
We can choose to explore the data in future detail. The table below shows the studies that looked at the impact of adding tree fodders to diets of cattle goat and sheep.
The table below shows the studies that looked at the impact of substitution of other ingredient types with tree fodders to diets of cattle goat and sheep.
As mentioned in the methods section, each table in the extraction excel template creates a seperate table in the data structure. Therefore, when using the metadata (which are the original seperate tables) we need to merge the tables that we are interested in.
Below are the individual tables for diets, nutrition and digestibility. We will then explore how to merge information from the different tables. To understand what the field are in each table download the follow excel sheet and navigate to the era_fields tab https://github.com/peetmate/era_codes/raw/main/era_master_sheet.xlsx”
# 4.2) Explore metadata #####
# This table is a high level description of the diet
diet_summary<-livestock_metadata$Animals.Out
diet_ingredients<-livestock_metadata$Animals.Diet
head(diet_ingredients)
#> D.Item.AOM D.ItemxProcess_low A.Level.Name D.Type
#> <char> <char> <char> <char>
#> 1: AOM_001911 hay Base Herbaceous Fodders
#> 2: AOM_000604 groundnut haulms 150g/day Crop Byproduct
#> 3: AOM_000604 groundnut haulms 300g/day Crop Byproduct
#> 4: AOM_000604 groundnut haulms 450g/day Crop Byproduct
#> 5: AOM_001901 atriplex nummularia Base Agroforestry Fodders
#> 6: AOM_000647 barley grain T2 Crop Product
#> D.Item D.Item.Group D.Source D.Process DC.Is.Dry D.Amount
#> <char> <char> <char> <list> <char> <num>
#> 1: Unspecified Grass Hay <NA> <NA> NA Yes NA
#> 2: Groundnut Haulm <NA> <NA> NA Yes 150
#> 3: Groundnut Haulm <NA> <NA> NA Yes 300
#> 4: Groundnut Haulm <NA> <NA> NA Yes 450
#> 5: Atriplex nummularia <NA> <NA> NA <NA> NA
#> 6: Barley Grain <NA> <NA> NA <NA> 400
#> D.Ad.lib D.Unit.Amount D.Unit.Time D.Unit.Animals D.Day.Start D.Day.End
#> <char> <char> <char> <char> <lgcl> <char>
#> 1: Yes g/kg day individual NA <NA>
#> 2: No g day individual NA <NA>
#> 3: No g day individual NA <NA>
#> 4: No g day individual NA <NA>
#> 5: Yes <NA> day <NA> NA <NA>
#> 6: <NA> g day <NA> NA <NA>
#> B.Code D.Item.Raw D.ItemxProcess D.Is.Group
#> <char> <char> <char> <lgcl>
#> 1: NN0022 Hay Hay FALSE
#> 2: NN0022 Groundnut Haulms Groundnut Haulms FALSE
#> 3: NN0022 Groundnut Haulms Groundnut Haulms FALSE
#> 4: NN0022 Groundnut Haulms Groundnut Haulms FALSE
#> 5: NN0080 Atriplex nummularia Atriplex nummularia FALSE
#> 6: NN0080 Barley Grain Barley Grain FALSE
#> D.Item.Root.Comp D.Item.Root.Comp.Proc_Major
#> <char> <char>
#> 1: Unspecified Grass Unspecified Grass Dried
#> 2: Groundnut Haulm Groundnut Haulm
#> 3: Groundnut Haulm Groundnut Haulm
#> 4: Groundnut Haulm Groundnut Haulm
#> 5: Atriplex nummularia Atriplex nummularia
#> 6: Barley Grain Barley Grain
#> D.Item.Root.Other.Comp.Proc_All D.Item.Other D.Item.Proc_All
#> <char> <char> <char>
#> 1: Unspecified Grass Hay <NA> Hay
#> 2: Groundnut Haulm <NA> <NA>
#> 3: Groundnut Haulm <NA> <NA>
#> 4: Groundnut Haulm <NA> <NA>
#> 5: Atriplex nummularia <NA> <NA>
#> 6: Barley Grain <NA> <NA>
#> D.Item.Proc_Major D.Item.Proc_Minor D.Item.Comp
#> <char> <char> <char>
#> 1: Dried <NA> <NA>
#> 2: <NA> <NA> Haulm
#> 3: <NA> <NA> Haulm
#> 4: <NA> <NA> Haulm
#> 5: <NA> <NA> <NA>
#> 6: <NA> <NA> Grain
#> AOM.Terms
#> <char>
#> 1: Forage Plants/Cereal and Grass Forages/Unspecified Grass/Unspecified Grass Dried
#> 2: Crop Byproduct/Legume ByProducts/Groundnut/Groundnut Haulm
#> 3: Crop Byproduct/Legume ByProducts/Groundnut/Groundnut Haulm
#> 4: Crop Byproduct/Legume ByProducts/Groundnut/Groundnut Haulm
#> 5: Forage Plants/Forage Trees/Atriplex nummularia
#> 6: Crop Product/Cereal Products/Barley/Barley Grain
#> AOM.Scientific.Name D.Item.Is.Tree D.Item.raw
#> <char> <lgcl> <char>
#> 1: <NA> FALSE Hay
#> 2: Arachis hypogaea FALSE Groundnut Haulms
#> 3: Arachis hypogaea FALSE Groundnut Haulms
#> 4: Arachis hypogaea FALSE Groundnut Haulms
#> 5: Atriplex nummularia TRUE Atriplex nummularia
#> 6: Hordeum vulgare FALSE Barley Grain
diet_nutrition<-livestock_metadata$Animals.Diet.Comp
head(diet_nutrition)
#> B.Code D.Item DC.Unit.Is.Dry is_group is_entire_diet
#> <char> <char> <char> <lgcl> <lgcl>
#> 1: NN0005 0 M Yes FALSE TRUE
#> 2: HK0066.1 0 MSK Unspecified FALSE TRUE
#> 3: HK0066.2 0 MSK Unspecified FALSE TRUE
#> 4: HK0330 0 g/kg A. tortilis Yes FALSE TRUE
#> 5: NJ0003 0 g/kg CBLE Yes FALSE TRUE
#> 6: HK0326 0 g/kg potato hash silage Yes FALSE TRUE
#> D.Item.Root.Comp.Proc_Major D.Item.Root.Other.Comp.Proc_All D.Item.AOM
#> <char> <char> <char>
#> 1: <NA> <NA> <NA>
#> 2: <NA> <NA> <NA>
#> 3: <NA> <NA> <NA>
#> 4: <NA> <NA> <NA>
#> 5: <NA> <NA> <NA>
#> 6: <NA> <NA> <NA>
#> DC.Value DC.Unit DC.Method DC.Variable D.Item.raw
#> <num> <char> <char> <char> <char>
#> 1: 876.00 g/kg Measured DM 0 M
#> 2: 921.00 g/kg Measured DM 0 MSK
#> 3: 881.00 g/kg Measured DM 0 MSK
#> 4: 867.00 g/kg Measured DM 0 g/kg A. tortilis
#> 5: 90.84 % Unspecified DM 0 g/kg CBLE
#> 6: 886.00 g/kg Measured DM 0 g/kg potato hash silage
diet_digestibility<-livestock_metadata$Animals.Diet.Digest
head(diet_digestibility)
#> B.Code D.Item DD.Unit.Is.Dry is_group is_entire_diet
#> <char> <char> <char> <lgcl> <lgcl>
#> 1: AN0057 0 PPL Yes FALSE TRUE
#> 2: LM0104 0% A. sieberiana Yes FALSE TRUE
#> 3: HK0033 0% Brewer's Grain Unspecified FALSE TRUE
#> 4: EO0046 0% CFL Yes FALSE TRUE
#> 5: HK0108 0% Leucaena Diet Unspecified FALSE TRUE
#> 6: HK0008 0% Leucaena Goat Unspecified FALSE TRUE
#> D.Item.Root.Comp.Proc_Major D.Item.Root.Other.Comp.Proc_All D.Item.AOM
#> <char> <char> <char>
#> 1: <NA> <NA> <NA>
#> 2: <NA> <NA> <NA>
#> 3: <NA> <NA> <NA>
#> 4: <NA> <NA> <NA>
#> 5: <NA> <NA> <NA>
#> 6: <NA> <NA> <NA>
#> DD.Value DD.Unit DD.Nut.or.Diet
#> <num> <char> <char>
#> 1: 0.57 Coefficient Nutrient
#> 2: 57.80 % Nutrient
#> 3: 69.86 % Nutrient
#> 4: 530.90 g/kg Nutrient
#> 5: 71.75 % Nutrient
#> 6: 50.80 % Nutrient
#> DD.Method DD.Variable D.Item.raw
#> <char> <char> <char>
#> 1: Measured total collection techinque DM 0 PPL
#> 2: Unspecified DM 0% A. sieberiana
#> 3: Measured total collection techinque (Apparent) DM 0% Brewer's Grain
#> 4: Measured total collection techinque DM 0% CFL
#> 5: Measured total collection techinque (Apparent) DM 0% Leucaena Diet
#> 6: Unspecified DM 0% Leucaena Goat
# Display the summary table of diets
DT::datatable(
diet_ingredients,
options = list(
scrollY = "400px", # Set vertical scroll height
scrollX = TRUE, # Enable horizontal scrolling
pageLength = 20, # Initial number of rows displayed
fixedHeader = FALSE # Keep column headers fixed while scrolling
)
)
DT::datatable(
diet_nutrition,
options = list(
scrollY = "400px", # Set vertical scroll height
scrollX = TRUE, # Enable horizontal scrolling
pageLength = 20, # Initial number of rows displayed
fixedHeader = FALSE # Keep column headers fixed while scrolling
)
)
# Display the summary table of diets
DT::datatable(
diet_digestibility,
options = list(
scrollY = "400px", # Set vertical scroll height
scrollX = TRUE, # Enable horizontal scrolling
pageLength = 20, # Initial number of rows displayed
fixedHeader = FALSE # Keep column headers fixed while scrolling
)
)
This script (unhide) integrates feed intake and weight gain data for livestock to analyze the relationship between feed types, including tree species, and animal performance.
It starts by filtering the feed intake data to include only entire diets with non-missing values. Tree species associated with feed intake are then merged into the dataset.
Similarly, weight gain data for non-fish species is filtered, standardized by converting weight measurements into kilograms per day, and calculating the final weight (end weight) after a given period. The script then merges the feed intake data with the weight gain data using common identifiers.
This results in a unified dataset that combines feed intake (including tree species) with corresponding weight gain metrics for livestock, enabling detailed analysis of the impacts of different feed practices on animal growth.
# 4.5) Feed Intake and Weight Gain with Tree Species #####
# Filter Feed Intake data to include only entire diets and remove NA values
feed_intake <- livestock_metadata$Data.Out[
Out.Subind == "Feed Intake" & is_entire_diet == TRUE & !is.na(ED.Intake.Item),
.(B.Code, T.Name, A.Level.Name, Out.Subind, ED.Intake.Item, ED.Mean.T, Out.Unit)
]
# Select only relevant columns for feed trees and rename
feed_trees <- data %>%
select(Code, T.Descrip, Tree.Feed) %>%
rename(`A.Level.Name` = T.Descrip, `B.Code` = Code)
# Merge feed intake with tree species data
feed_intake_trees <- merge(
feed_intake,
feed_trees,
by = c("B.Code", "A.Level.Name"),
all.x = TRUE # Left join to retain all feed intake rows
)
# Filter Weight Gain data and remove fish-related entries
WG <- livestock_metadata$Data.Out[
Out.Subind == "Weight Gain" & P.Product != "Fish",
.(B.Code, P.Product, T.Name, A.Level.Name, Out.WG.Start, Out.WG.Unit, Out.WG.Days, ED.Mean.T, Out.Unit)
]
# Define units to standardize and convert Weight Gain data
units_per_day <- c(
"g/day", "g", "g/individual/day", "g/head/day", "g/day/individual",
"g/d/individual", "g/individual/d", "g/indivividual/day", "g/individual",
"g/ndividual/day", "g/dindividual/day", "g/individual.day", "g /individual/day"
)
# Convert ED.Mean.T to kilograms/day only for specific units
WG[, ED.Mean.T_kg := ifelse(
Out.Unit %in% units_per_day, # Check if Out.Unit is in the list
round(ED.Mean.T / 1000, 3), # Convert grams/day to kilograms/day and round
round(ED.Mean.T, 3) # Retain as is if not in the list
)]
# Convert Out.WG.Start to kilograms if Out.WG.Unit indicates grams
WG[, Out.WG.Start_kg := ifelse(
grepl("^g$", Out.WG.Unit, ignore.case = TRUE), # Match exact "g"
Out.WG.Start / 1000, # Convert grams to kilograms
Out.WG.Start # Retain if already in kilograms
)]
# Calculate Out.WG.End
WG[, Out.WG.End := ifelse(
Out.Unit %in% units_per_day, # Daily gain units
round(Out.WG.Start_kg + (ED.Mean.T_kg * Out.WG.Days), 3),
ifelse( # Non-daily gain units
grepl("^(g|kg|Kg)$", Out.Unit, ignore.case = TRUE),
round(Out.WG.Start_kg + ED.Mean.T_kg, 3),
NA # Set to NA for other units
)
)]
#Merge with feed intake.
# Rename columns in WG and feed intake datasets
WG <- WG %>%
rename(
`Weight Gain` = ED.Mean.T,
`Weight Gain Unit` = Out.Unit,
`Weight Gain_kg` = ED.Mean.T_kg
)
feed_intake_trees <- feed_intake_trees %>%
rename(
`Feed Intake` = ED.Mean.T,
`Feed Intake Unit` = Out.Unit,
)
# Perform an inner join on the common columns
intake_WG <- merge(
WG, feed_intake_trees,
by = c("B.Code", "T.Name", "A.Level.Name"),
all = FALSE # Inner join: retain only matching rows
)
intake_WG<- intake_WG %>%
rename(`D.Item.raw`= ED.Intake.Item)
# Display the summary table of diets
DT::datatable(
intake_WG,
options = list(
scrollY = "400px", # Set vertical scroll height
scrollX = TRUE, # Enable horizontal scrolling
pageLength = 20, # Initial number of rows displayed
fixedHeader = FALSE # Keep column headers fixed while scrolling
)
)
This script performs a detailed analysis by merging data on livestock feed intake, weight gain, and nutritional composition to create a comprehensive dataset. First, it renames the ED.Intake.Item column to D.Item_raw in the intake_WG dataset for consistency. Then, it filters the livestock_metadata$Animals.Diet.Comp dataset to exclude rows with missing values in the DC.Value column, ensuring only complete nutritional data is used. Relevant columns (B.Code, DC.Value, DC.Unit, DC.Variable, D.Item_raw) are selected from this filtered dataset. The script merges the nutritional data with the feed intake and weight gain dataset (intake_WG) using an inner join, retaining only rows with matching values in D.Item_raw and B.Code
nutrition <- livestock_metadata$Animals.Diet.Comp %>%
filter(!is.na(DC.Value)) %>% # Omit rows with NA in DC.Value
select(B.Code, DC.Value, DC.Unit, DC.Variable, D.Item.raw)
intake_WG_nut <- merge(
nutrition, intake_WG,
by = c("D.Item.raw", "B.Code"),
all = FALSE # Inner join: retain only matching rows
)
DT::datatable(
intake_WG_nut,
options = list(
scrollY = "400px", # Set vertical scroll height
scrollX = TRUE, # Enable horizontal scrolling
pageLength = 20, # Initial number of rows displayed
fixedHeader = FALSE # Keep column headers fixed while scrolling
)
)
This script creates a Shiny app that allows users to visualize the weight gain distribution of three livestock categories: “Cattle,” “Goat,” and “Sheep,” with outliers included. The dataset WG is filtered to include only rows where the P.Product column matches the selected animal.
# Function to filter data for all animals and include outliers
filter_all_animals <- function(data) {
# Subset data for selected animals
animal_data <- data[data$P.Product %in% c("Cattle", "Goat", "Sheep"), ]
return(animal_data)
}
# Filter the data for all animals
animal_data_with_outliers <- filter_all_animals(WG)
# Create individual plots for each animal with outliers
plot_cattle <- ggplot(animal_data_with_outliers[animal_data_with_outliers$P.Product == "Cattle", ], aes(x = P.Product, y = `Weight Gain_kg`)) +
geom_boxplot(outlier.color = "red", outlier.shape = 19) +
labs(
x = "Animal Type",
y = "Weight Gain (kg/day)"
) +
theme_minimal()
plot_goat <- ggplot(animal_data_with_outliers[animal_data_with_outliers$P.Product == "Goat", ], aes(x = P.Product, y = `Weight Gain_kg`)) +
geom_boxplot(outlier.color = "red", outlier.shape = 19) +
labs(
x = "Animal Type",
y = "Weight Gain (kg/day)"
) +
theme_minimal()
plot_sheep <- ggplot(animal_data_with_outliers[animal_data_with_outliers$P.Product == "Sheep", ], aes(x = P.Product, y = `Weight Gain_kg`)) +
geom_boxplot(outlier.color = "red", outlier.shape = 19) +
labs(
x = "Animal Type",
y = "Weight Gain (kg/day)"
) +
theme_minimal()
# Combine the plots into one image in landscape orientation
combined_plot <- grid.arrange(plot_cattle, plot_goat, plot_sheep, nrow = 1)
# Display the combined plot
print(combined_plot)
#> TableGrob (1 x 3) "arrange": 3 grobs
#> z cells name grob
#> 1 1 (1-1,1-1) arrange gtable[layout]
#> 2 2 (1-1,2-2) arrange gtable[layout]
#> 3 3 (1-1,3-3) arrange gtable[layout]
This script creates a Shiny app that allows users to visualize the weight gain distribution of three livestock categories: “Cattle,” “Goat,” and “Sheep,” with outliers excluded. The dataset WG is filtered to include only rows where the P.Product column matches the selected animal. To remove outliers, the script applies the Interquartile Range (IQR) method, calculating the first quartile (Q1), third quartile (Q3), and IQR. Data points falling outside the range of Q1 - 1.5 * IQR to Q3 + 1.5 * IQR are excluded. A box plot, generated using ggplot2, dynamically displays the weight gain distribution for the selected animal, providing a clean and focused view without the influence of extreme values
library(ggplot2)
library(gridExtra)
# Function to filter data for all animals and remove outliers
filter_and_remove_outliers <- function(data) {
# Subset data for selected animals
animal_data <- data[data$P.Product %in% c("Cattle", "Goat", "Sheep"), ]
# Remove outliers for each animal type
animal_data <- animal_data %>%
group_by(P.Product) %>%
filter(
`Weight Gain_kg` > quantile(`Weight Gain_kg`, 0.25, na.rm = TRUE) - 1.5 * IQR(`Weight Gain_kg`, na.rm = TRUE) &
`Weight Gain_kg` < quantile(`Weight Gain_kg`, 0.75, na.rm = TRUE) + 1.5 * IQR(`Weight Gain_kg`, na.rm = TRUE)
) %>%
ungroup()
return(animal_data)
}
# Filter and remove outliers for all animals
animal_data <- filter_and_remove_outliers(WG)
# Create individual plots for each animal
plot_cattle <- ggplot(animal_data[animal_data$P.Product == "Cattle", ], aes(x = P.Product, y = `Weight Gain_kg`)) +
geom_boxplot() +
labs(
x = "Animal Type",
y = "Weight Gain (kg/day)"
) +
theme_minimal()
plot_goat <- ggplot(animal_data[animal_data$P.Product == "Goat", ], aes(x = P.Product, y = `Weight Gain_kg`)) +
geom_boxplot() +
labs(
x = "Animal Type",
y = "Weight Gain (kg/day)"
) +
theme_minimal()
plot_sheep <- ggplot(animal_data[animal_data$P.Product == "Sheep", ], aes(x = P.Product, y = `Weight Gain_kg`)) +
geom_boxplot() +
labs(
x = "Animal Type",
y = "Weight Gain (kg/day)"
) +
theme_minimal()
# Combine the plots into one image in landscape orientation
combined_plot <- grid.arrange(plot_cattle, plot_goat, plot_sheep, nrow = 1)
# Display the combined plot
print(combined_plot)
#> TableGrob (1 x 3) "arrange": 3 grobs
#> z cells name grob
#> 1 1 (1-1,1-1) arrange gtable[layout]
#> 2 2 (1-1,2-2) arrange gtable[layout]
#> 3 3 (1-1,3-3) arrange gtable[layout]
This work is funded under the CGIAR Livestock & Climate Initiative.