library(readxl)
library(data.table)
# files needed during cleaning up:
<- read_excel("data/bounds.xlsx")
intake
# contstraints
<- read_excel("data/constraints.xlsx")
constraints
# contrib per unit
<- read_excel("data/contrib_per_unit.xlsx")
cpu
# nutrition reference
<- read_excel("data/nutref.xlsx") nutref
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.xlsx
constraints.xlsx
bounds.xlsx
nutref.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,
<- readRDS('data/result.RData')
results # examine the strucure of this R object
|> str() results
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)
<- round(results$xopt, digits=1)
new_intake # 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
<- round(intake$mean, digits=1) #Baseline diet
intake_baseline <- intake$upper_bound
intake_upr <- intake$lower_bound
intake_lwr <- intake$Foodgroup
foodgroup
#Percent change between new diet and baseline diet
<- round((new_intake - intake_baseline)/intake_baseline, digits=3)
percent_change 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
<- data.frame(
diet_result 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
<- dplyr::select(cpu ,
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"
)
<- nutref |>
cstr ::select("Energy (MJ)",
dplyr"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!
<- t(as.matrix(new_intake)) %*% as.matrix(cpu[,2:54]) output_newdiet
Combine the results and add tags
<- t(rbind(output_newdiet, cstr))
const_result 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
<- constraints$tag_outcome
nutnames
<- data.table(const_result)
const_result := nutnames]
const_result[, nutrient
#Check if results meet constraints
:= 'Yes']
const_result[, is_ok < const_lwr, is_ok := 'beyond lower']
const_result[new_diet > const_upr, is_ok := 'beyond upper']
const_result[new_diet := 0]
const_result[, relative_dev == '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)]
const_result[is_ok
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!
<- intake_baseline
currentintake 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_result$new_intake - diet_result$baseline)/diet_result$baseline)^2 |> sum()
diet_departure 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
<- diet_result[c(23, 24, 25, 26, 27),]
redmeat 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
<- ((sum(redmeat$new_intake) - sum(redmeat$baseline))/sum(redmeat$baseline))*100
redmeat_departure redmeat_departure
[1] -43.10345
It is convenient to use a function instead of computing from formula
# define function
<- function(meat_data){
f_meat_departure
<- ((sum(meat_data$new_intake) - sum(meat_data$baseline))/sum(meat_data$baseline))*100
meat_departure 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
<- diet_result[c(23, 24, 25, 26, 27, 28, 29, 30, 31),]
totalmeat f_meat_departure(meat_data = totalmeat)
[1] -34.68208
# ruminant meat departure
<- diet_result[c(23, 24),]
ruminantmeat f_meat_departure(meat_data = ruminantmeat)
[1] -24.63466