library(readxl)
library(data.table)
# files needed during cleaning up:
intake <- read_excel("data/bounds.xlsx")
# contstraints
constraints <- read_excel("data/constraints.xlsx")
# contrib per unit
cpu <- read_excel("data/contrib_per_unit.xlsx")
# nutrition reference
nutref <- read_excel("data/nutref.xlsx")4. Output Data Processing
Data
The pseudomised data is available in the public_data folder as an example. These are NOT the original values from KBS database.
These datasets are relevant:
contrib_per_unit.xlsxconstraints.xlsxbounds.xlsxnutref.xlsx- this is the nutrition reference file for comparison.
The result data, result.RData is pre-computed loaded. Please refer to 3. Run Optimisation for how to reach this result.
Load data
Load the results from previous step,
results <- readRDS('data/result.RData')
# examine the strucure of this R object
results |> str()List of 4
$ xopt : num [1:53] 8.12 138.62 4.2 13.72 20.26 ...
$ obj : num -15.4
$ status: int 1
$ extra :List of 2
..$ lambda: num [1:53] 4.52 5.32e-12 -1.27e-11 1.45e-11 -3.02e-12 ...
..$ slack : num [1:53] -5.33e-15 -4.22e+01 1.77e+01 -1.55e+01 7.43e+01 ...
The first object in this list, results$xopt is the one we need.
Compare diets
New diet chart
Create chart to compare food composition of diets (g of each food group in new vs baseline diet, percent change between diets, lower and upper intake limits aka realism constraints)
new_intake <- round(results$xopt, digits=1)
# New diet name (in this case 'new_intake' aka Nutrition,
# since this scenario includes only nutritional and realism constraints)
# Baseline <- round(constraintsdata[,2],digits=1) #Baseline diet
intake_baseline <- round(intake$mean, digits=1) #Baseline diet
intake_upr <- intake$upper_bound
intake_lwr <- intake$lower_bound
foodgroup <- intake$Foodgroup
#Percent change between new diet and baseline diet
percent_change <- round((new_intake - intake_baseline)/intake_baseline, digits=3)
percent_change [1] -0.905 0.194 0.105 0.384 0.829 1.529 0.212 1.095 0.000 -0.045
[11] 0.380 0.691 0.368 0.298 3.878 0.000 -0.208 0.295 0.317 1.361
[21] 0.034 -0.744 0.453 -0.903 0.437 -0.903 -0.905 0.061 -0.690 -0.737
[31] -0.231 -0.398 0.321 0.683 0.910 0.419 -0.169 0.516 -0.355 -0.254
[41] 0.189 -0.543 0.488 -0.902 -0.048 -0.048 0.369 -0.172 -1.000 -0.080
[51] -0.294 0.000 -0.903
# food chart for compact display
diet_result <- data.frame(
foodgroup = foodgroup,
baseline = intake_baseline,
new_intake = new_intake,
percent_change = percent_change,
lower_limit = intake_lwr,
upper_limit = intake_upr
)
head(diet_result) foodgroup baseline new_intake percent_change lower_limit upper_limit
1 Bread fine 85.3 8.1 -0.905 8.526 244.545
2 Bread coarse 116.1 138.6 0.194 11.613 289.275
3 Flours grains 3.8 4.2 0.105 0.378 20.580
4 Rice 9.9 13.7 0.384 0.987 53.130
5 Pasta 11.1 20.3 0.829 1.113 61.635
6 Breakfast cereals 17.2 43.5 1.529 1.722 99.120
Constraint on nutrients
Code for selecting names and renaming them
cpu <- dplyr::select(cpu ,
"Foodgroup",
"Energy (MJ)" = "Energy (MJ)",
"Protein, g, lower" = "Protein1",
"Protein, g, upper" = "Protein2",
"Carbohydrates, g, lower"="Available carbohydrates1",
"Carbohydrates, g, upper"="Available carbohydrates2",
"Added sugar, g"="Added Sugar",
"Dietary fiber, g"= "Dietary fiber",
"Fat, g, lower"= "Fat1",
"Fat, g, upper"= "Fat2",
"Saturated fatty acids, g"="Sum saturated fatty acids",
"Trans fatty acids, g"="Sum trans fatty acids", #Added
"n-3 fatty acids, g"="Sum n-3 fatty acids",
"ALA, g" = "Sum ALA",
"MUFA, g, lower" = "Sum monounsaturated fatty acids1",
"MUFA, g, upper" = "Sum monounsaturated fatty acids2",
"PUFA, g, lower" = "Sum polyunsaturated fatty acids1",
"PUFA, g, upper" = "Sum polyunsaturated fatty acids2",
"Vitamin A, RE µg"= "Vitamin A",
"Vitamin E, alfa-TE"= "Vitamin E",
"Thiamin (Vitamin B1), mg"= "Thiamin (Vitamin B1)",
"Riboflavin (Vitamin B2), mg"= "Riboflavin (Vitamin B2)",
"Niacin, NE"="Niacin equivalent",
"Vitamin B6, mg"="Vitamin B6",
"Folate, µg"="Folate",
"Vitamin B12, µg"="Vitamin B12",
"Vitamin C, mg"="Vitamin C",
"Vitamin D, µg" = "Vitamin D",
"Sodium, mg"="Sodium",
"Potassium, mg"="Potassium",
"Calcium, mg"="Calcium",
"Magnesium, mg"= "Magnesium",
"Phosphorus, mg"="Phosphorus",
"Iron, mg"="Iron",
"Zinc, mg"="Zinc",
"Iodine, µg"="Iodine",
"Selenium, µg"="Selenium",
"Copper, µg"="Copper",
"Alcohol, g" = "Alcohol",
"GHGE, kg CO2-eq" = "GHGE",
"FE, g P-eq" = "FE",
"ME, g N-eq" = "ME",
"TA, g SO2-eq" = "ACID",
"WU, m2" = "WU",
"LU, m3a" = "LU",
"Whole grains, g" = "Whole grains",
"Total fruit, g" = "Fruit",
"Total vegetables, g" = "Vegetables",
"Total dairy, minimum, g" = "Dairy1",
"Total dairy, maximum, g" = "Dairy2",
"Total fish, minimum, g" = "Fish1",
"Total fish, maximum, g" = "Fish2",
"Total red meat, maximum, g" = "Red meat",
"Total white meat, maximum, g" = "White meat"
)
cstr <- nutref |>
dplyr::select("Energy (MJ)",
"Protein, g, lower" = "Protein1",
"Protein, g, upper" = "Protein2",
"Carbohydrates, g, lower"="Available carbohydrates1",
"Carbohydrates, g, upper"="Available carbohydrates2",
"Added sugar, g"="Added Sugar",
"Dietary fiber, g"= "Dietary fiber",
"Fat, g, lower"= "Fat1",
"Fat, g, upper"= "Fat2",
"Saturated fatty acids, g"="Sum saturated fatty acids",
"Trans fatty acids, g"="Sum trans fatty acids", #Added
"n-3 fatty acids, g"="Sum n-3 fatty acids",
"ALA, g" = "Sum ALA",
"MUFA, g, lower" = "Sum monounsaturated fatty acids1",
"MUFA, g, upper" = "Sum monounsaturated fatty acids2",
"PUFA, g, lower" = "Sum polyunsaturated fatty acids1",
"PUFA, g, upper" = "Sum polyunsaturated fatty acids2",
"Vitamin A, RE µg"= "Vitamin A",
"Vitamin E, alfa-TE"= "Vitamin E",
"Thiamin (Vitamin B1), mg"= "Thiamin (Vitamin B1)",
"Riboflavin (Vitamin B2), mg"= "Riboflavin (Vitamin B2)",
"Niacin, NE"="Niacin equivalent",
"Vitamin B6, mg"="Vitamin B6",
"Folate, µg"="Folate",
"Vitamin B12, µg"="Vitamin B12",
"Vitamin C, mg"="Vitamin C",
"Vitamin D, µg" = "Vitamin D",
"Sodium, mg"="Sodium",
"Potassium, mg"="Potassium",
"Calcium, mg"="Calcium",
"Magnesium, mg"= "Magnesium",
"Phosphorus, mg"="Phosphorus",
"Iron, mg"="Iron",
"Zinc, mg"="Zinc",
"Iodine, µg"="Iodine",
"Selenium, µg"="Selenium",
"Copper, µg"="Copper",
"Alcohol, g" = "Alcohol",
"GHGE, kg CO2-eq" = "GHGE",
"FE, g P-eq" = "FE",
"ME, g N-eq" = "ME",
"TA, g SO2-eq" = "ACID",
"WU, m2" = "WU",
"LU, m3a" = "LU",
"Whole grains, g" = "Whole grains",
"Total fruit, g" = "Fruit",
"Total vegetables, g" = "Vegetables",
"Total dairy, minimum, g" = "Dairy1",
"Total dairy, maximum, g" = "Dairy2",
"Total fish, minimum, g" = "Fish1",
"Total fish, maximum, g" = "Fish2",
"Total red meat, maximum, g" = "Red meat",
"Total white meat, maximum, g" = "White meat"
)Compute the nutrient for the new diet, and compare with the upper and lower constraints. Make sure the names match!
#Create chart and assign column names and row names
# colnames(output_newdiet)
# colnames(cstr)
# data.frame(colnames(output_newdiet), colnames(cstr))
# make sure the match the names!
output_newdiet <- t(as.matrix(new_intake)) %*% as.matrix(cpu[,2:54])Combine the results and add tags
const_result <- t(rbind(output_newdiet, cstr))
colnames(const_result) <- c('new_diet','const_lwr', 'const_upr')
head(const_result) new_diet const_lwr const_upr
Energy (MJ) 10.49982 10.0 10.0
Protein, g, lower 107.21292 59.9 119.8
Protein, g, upper 107.21292 59.9 119.8
Carbohydrates, g, lower 299.17897 269.5 359.3
Carbohydrates, g, upper 299.17897 269.5 359.3
Added sugar, g 51.97298 0.0 59.9
It can be convenient to add some indicators for us to read easily.
# extract nutrition names
nutnames <- constraints$tag_outcome
const_result <- data.table(const_result)
const_result[, nutrient := nutnames]
#Check if results meet constraints
const_result[, is_ok := 'Yes']
const_result[new_diet < const_lwr, is_ok := 'beyond lower']
const_result[new_diet > const_upr, is_ok := 'beyond upper']
const_result[, relative_dev := 0]
const_result[is_ok == 'beyond lower', relative_dev := round((new_diet - const_lwr)/const_lwr, 3)]
const_result[is_ok == 'beyond upper', relative_dev := round((new_diet - const_upr)/const_upr, 3)]
head(const_result) new_diet const_lwr const_upr nutrient is_ok
<num> <num> <num> <char> <char>
1: 10.49982 10.0 10.0 Energy (MJ) beyond upper
2: 107.21292 59.9 119.8 Protein, g, lower Yes
3: 107.21292 59.9 119.8 Protein, g, upper Yes
4: 299.17897 269.5 359.3 Carbohydrates, g, lower Yes
5: 299.17897 269.5 359.3 Carbohydrates, g, upper Yes
6: 51.97298 0.0 59.9 Added sugar, g Yes
relative_dev
<num>
1: 0.05
2: 0.00
3: 0.00
4: 0.00
5: 0.00
6: 0.00
Departure
Diet departure is customized indicator for comparing new and current diet. They are based on customized functions, so you need to be careful with the formula and implementation.
Diet departure was computed from a formula where each diet element is manually selected and compared, then summed together. This can be compactly written as below, utilizing the vectorized computation. Please compare if this is the object you need!
currentintake <- intake_baseline
head(diet_result) foodgroup baseline new_intake percent_change lower_limit upper_limit
1 Bread fine 85.3 8.1 -0.905 8.526 244.545
2 Bread coarse 116.1 138.6 0.194 11.613 289.275
3 Flours grains 3.8 4.2 0.105 0.378 20.580
4 Rice 9.9 13.7 0.384 0.987 53.130
5 Pasta 11.1 20.3 0.829 1.113 61.635
6 Breakfast cereals 17.2 43.5 1.529 1.722 99.120
# sum(diet_result$percent_change^2) # slightly different numbers, due to rounding
# departure
diet_departure <- ((diet_result$new_intake - diet_result$baseline)/diet_result$baseline)^2 |> sum()
diet_departure[1] 33.4085
Red meat departure, need to select a subset that are relevant for red meat first:
# red meat departure
# select necessary data rows
redmeat <- diet_result[c(23, 24, 25, 26, 27),]
redmeat foodgroup baseline new_intake percent_change lower_limit
23 Ruminant meat, unprocessed 23.2 33.7 0.453 2.3205
24 Ruminant meat, processed 24.7 2.4 -0.903 2.4675
25 Pork, unprocessed 21.5 30.9 0.437 2.1525
26 Pork, processed 23.6 2.3 -0.903 2.3625
27 Red meat mixed, processed 34.6 3.3 -0.905 3.4650
upper_limit
23 153.405
24 138.600
25 139.650
26 86.100
27 151.725
redmeat_departure <- ((sum(redmeat$new_intake) - sum(redmeat$baseline))/sum(redmeat$baseline))*100
redmeat_departure[1] -43.10345
It is convenient to use a function instead of computing from formula
# define function
f_meat_departure <- function(meat_data){
meat_departure <- ((sum(meat_data$new_intake) - sum(meat_data$baseline))/sum(meat_data$baseline))*100
return(meat_departure)
}
# use the function
f_meat_departure(meat_data = redmeat)[1] -43.10345
This is giving the same value as above. Now use the formula for the other departures. First select the relevant foods, then pass them into the function.
# total meat departure
totalmeat <- diet_result[c(23, 24, 25, 26, 27, 28, 29, 30, 31),]
f_meat_departure(meat_data = totalmeat)[1] -34.68208
# ruminant meat departure
ruminantmeat <- diet_result[c(23, 24),]
f_meat_departure(meat_data = ruminantmeat)[1] -24.63466