You are on page 1of 20

MINI PROJECT

NUTRITION CALCULATOR
CALCULATE NUTRITION FOR RECIPES
( To develop a mini project in R using shiny ibraries)

Session- 2020-21

Submitted to:- Submitted by:-


Mrs. Bhawana Parihar Lokesh Lohani (170180101028)
Assistant Professor (CSE) Harish Kunjwal( 170180107014)
BTKIT, Dwarahat CSE, 4th Year
MINI PROJECT
NUTRITION CALCULATOR - CALCULATE NUTRITION
FOR RECIPES

Aim: -To develop a mini project in R using shiny libraries.


Title: -
NUTRITION CALCULATOR - CALCULATE NUTRITION FOR
RECIPES Theory:-
Nutrient Calculator is a dashboard where users can easily search and add multiple
ingredients from the CNF database, calculate overall nutrient data such as calories and
sodium, and see nutrient amounts as percentage recommended daily values. Total calorie
count, nutrients amounts which surpass 100% and 50% daily value are highlighted in at
the top. Macronutrients, minerals and vitamins as % daily value are visualized on
separated bar graphs.
When it comes to understanding food, nutrition is one of the most commonly discussed
topics. What foods are good for me? What should I eat to live a long and healthy life?
Therefore, I tend to stay clear from ‘what’s good for you discussions’ and ‘what’s
healthy for you’. That said, I do like understanding my food and its analysis. he energy
content can be determined using a simple calculation.
We know which groups of molecules contribute to the energy content of a food. We also
know how much energy each of these types of molecules contain and will give to your
body.

It has been defined by analysis how much energy one gram of these different groups
of molecules will give to your body (the regulation mentioned above also contains this
information). Calculating energy content is then done as follows:

1. Determine how much of each component is in the food (we’ll come back to that
later)
2. Multiply this value with the energy content for each specific component.
3. Count up all these numbers and voila, you have the energy content of your food!
Let’s do an example here:
This calculator was developed to use food groups to meet your nutritional needs.
However, you can easily use this tool to count grams of protein, fat, or
carbohydrates.
Library used in this project:-
 library(shiny)
The Shiny package comes with eleven built-in examples that demonstrate how Shiny
works. This article reviews the first three examples, which demonstrate the basic
structure of a Shiny app.
Shiny applications have two components, a user interface object and a server function,
that are passed as arguments to the shinyApp function that creates a Shiny app object
from this UI/server pair. The source code for both of these components is listed below.
In subsequent sections of the article we’ll break down Shiny code in detail and explain
the use of “reactive” expressions for generating output.

ui <- fluidPage(

# App title ----


titlePanel("Hello Shiny!"),

# Sidebar layout with input and output definitions ----

sidebarLayout(

# Sidebar panel for inputs ----


sidebarPanel(
# Input: Slider for the number of bins ----
sliderInput(inputId = "bins", label
= "Number of bins:",
min = 1,
max = 50,
value = 30)
),
# Main panel for displaying outputs ----
mainPanel(

# Output: Histogram ----


plotOutput(outputId = "distPlot")
)
)
)

 library(dplyr)
To explore the basic data manipulation verbs of dplyr, we’ll use the dataset starwars.
This dataset contains 87 characters and comes from the Star Wars API, and is
documented in ?
starwars
dim(starwars)
#> [1] 87 14
starwars
#> # A tibble: 87 x 14
#> name height mass hair_color skin_color eye_color birth_year sex gender

#> <chr> <int> <dbl> <chr> <chr><chr> <dbl> <chr> <chr>


#> 1 Luke… 172 77 blond fair blue 19 male mascu…
#> 2 C-3PO 167 75 <NA> gold yellow 112 none mascu…
#> 3 R2-D2 96 32 <NA> white, bl… red 33 none mascu…
#> 4 Dart… 202 136 none white yellow 41.9 male mascu…
#> # … with 83 more rows, and 5 more variables: homeworld <chr>, species
<chr>, #> # films <list>, vehicles <list>, starships <list>
Single table verbs
dplyr aims to provide a function for each basic verb of data manipulation. These verbs
can be organised into three categories based on the component of the dataset that they
work with:

o Rows:
o filter() chooses rows based on column
o values. slice() chooses rows based on
o location. arrange() changes the order of the
o rows. Columns:
o select() changes whether or not a column is included.
o rename() changes the name of columns.
o mutate() changes the values of columns and creates new columns.
o relocate() changes the order of the columns.
o Groups of rows:
o summarise() collapses a group into a single row.

The pipe
All of the dplyr functions take a data frame (or tibble) as the first argument. Rather than
forcing the user to either save intermediate objects or nest functions, dplyr provides the
%>% operator from magrittr. x %>% f(y) turns into f(x, y) so the result from one step is
then “piped” into the next step. You can use the pipe to rewrite multiple operations that
you can read left-to-right, top-to-bottom (reading the pipe operator as “then”).

Patterns of operations
The dplyr verbs can be classified by the type of operations they accomplish (we
sometimes speak of their semantics, i.e., their meaning). It’s helpful to have a good
grasp of the difference between select and mutate operations.
Selecting operations
One of the appealing features of dplyr is that you can refer to columns from the tibble as
if they were regular variables. However, the syntactic uniformity of referring to bare

column names hides semantical differences across the verbs. A column symbol supplied
to select() does not have the same meaning as the same symbol supplied to mutate().
Mutating operations
Mutate semantics are quite different from selection semantics. Whereas select()
expects column names or positions, mutate() expects column vectors. We will set
up a smaller tibble to use for our examples.
df <- starwars %>% select(name, height, mass)
When we use select(), the bare column names stand for their own positions in the
tibble. For mutate() on the other hand, column symbols represent the actual
column vectors stored in the tibble.
 library(DT)
DataTables is used ubiquitously to display tabular data interactively within
webpages. DataTables allows entire tables or individual columns to be filtered.
DataTables provides a number of extensions for adding additional features,
including responsivenes
Data manipulation operations such as subset, group, update, join etc., are all
inherently related. Keeping these related operations together allows for:
o concise and consistent syntax irrespective of the set of operations you
would like to perform to achieve your end goal.
o performing analysis fluidly without the cognitive burden of having to map
each operation to a particular function from a potentially huge set of functions
available before performing the analysis.
o automatically optimising operations internally, and very effectively, by
knowing precisely the data required for each operation, leading to very fast and
memory efficient code.
Briefly, if you are interested in reducing programming and compute time
tremendously, then this package is for you. The philosophy that data.table adheres
to makes this possible. Our goal is to illustrate it through this series of vignettes.
data.table is an R package that provides an enhanced version of data.frames,
which are the standard data structure for storing data in base R. In the Data
section above, we already created a data.table using fread(). We can also create
one using the data.table() function. Here is an example:

DT = data.table(
ID = c("b","b","b","a","a","c"),
a = 1:6,
b = 7:12,
c = 13:18
)
DT
# ID a b c
# 1: b 1 7 13
# 2: b 2 8 14
# 3: b 3 9 15
# 4: a 4 10 16
# 5: a 5 11 17
# 6: c 6 12 18

class(DT$ID)
# [1] "character"
 library(ggplot2)
ggplot2 is a plotting package that makes it simple to create complex plots from data in a
data frame. It provides a more programmatic interface for specifying what variables to
plot, how they are displayed, and general visual properties. Therefore, we only need
minimal changes if the underlying data change or if we decide to change from a bar plot
to a scatterplot. This helps in creating publication quality plots with minimal amounts of
adjustments and tweaking.
ggplot2 functions like data in the 'long' format, i.e., a column for every dimension, and a
row for every observation. Well-structured data will save you lots of time when making
figures with ggplot2
ggplot graphics are built step by step by adding new elements. Adding layers in this
fashion allows for extensive flexibility and customization of plots.
To build a ggplot, we will use the following basic template that can be used for different
types of plots:

ggplot(data = <DATA>, mapping = aes(<MAPPINGS>)) + <GEOM_FUNCTION>()

 use the ggplot() function and bind the plot to a specific data frame using the data
argument

ggplot(data = surveys_complete)
ggplot(data = surveys_complete, mapping = aes(x = weight, y = hindfoot_length))

 add 'geoms' – graphical representations of the data in the plot (points, lines, bars).
ggplot2 offers many different geoms; we will use some common ones today, including:

 geom_point() for scatter plots, dot plots, etc.


 geom_boxplot() for, well, boxplots!
 geom_line() for trend lines, time series, etc.
To add a geom to the plot use + operator. Because we have two continuous variables,
let's use geom_point() first:

ggplot(data = surveys_complete, aes(x = weight, y = hindfoot_length)) +


geom_point()

 library(shinydashboard)
At this point comes into the light the most simple and open source way of
creating dashboards, i.e. RShiny dashboards.
You can create amazing interactive dashboards using any sort of data imported or
created using R programming. The most important thing about Shiny dashboards
are that they are simple to create with reusing the templates and moulding the
designs by tweaking the codes.

A dashboard has three parts: a header, a sidebar, and


a body. library(shiny)
library(shinydash
board) ui <-
dashboardPage(
dashboardHeader(),
dashboardSidebar(),
dashboardBody()
)
server <- function(input,
output) { } shinyApp(ui,
server)

You can also create two different files such as ui.R and app.R and then calling
through R console by using shinyApp() function.

Sidebar

Here comes a very interesting part of the dashboard UI where you can create
different styles of widgets for taking in the inputs or filtering the data.

Radio button Input Widget —

This widget maps each value with an operation in the main body or filtering out
the data with help of functions defined in server app.
Note: The interactive part of dashboard that changes on based of these inputs will be
explained in the subsequent articles.
sidebar <- dashboardSidebar(
sidebarMenu(
sidebarSearchForm(textId = "searchText",buttonId =
"searchButton", label = "Search..."),
menuItem("List of Data Classification", tabName = "list", icon =
icon("list")), radioButtons("rtype","Choose Categories :",
choiceNames = c('By Sources', 'By Data Types','By Modeling', 'By
Processing', 'Data at Rest vs Data in Motion'),
choiceValues = c(1,2,3,4,5))

 library(plotly)
Plotly is a technical computing company headquartered in Montreal, Quebec, that
develops online data analytics and visualization tools. Plotly provides online graphing,
analytics, and statistics tools for individuals and collaboration, as well as scientific
graphing libraries for Python, R, MATLAB, Perl, Julia, Arduino, and REST.

Python Plotly Library

Plotly’s Python graphing library makes interactive graphs online and allows us to save
them offline if need be.

Why Plotly

Plotly has got some amazing features that make it better than other graphing libraries:

Using Plotly

To use plotly in any of the Python scripts, we will need to import plotly as: import plotly

A few more steps and you are ready to use plotly. For Online usage, you need to set up
credentials. This can be done as:

plotly.tools.set_credentials_file(username='YourUsernameHere',
api_key='YourAPIkeyHere')

For offline usage, we need to call plot like the following for offline

usage: plotly.offline.plot()
Code :-

library(shiny)
library(dplyr)
library(DT)
library(ggplot2)
library(shinydashboard)
library(plotly)

# load files from Canada Nutrient File

nutr_files <- list.files(pattern = "*.rda")


lapply(nutr_files,load,.GlobalEnv)
# format quantities for ingredients

# take everything before parenthesis


ca_measure_name$units <- regmatches(ca_measure_name$MeasureDescription,
regexpr("^([^(]+)", ca_measure_name$MeasureDescription, perl = T))
# only take what is in parenthesis
ca_measure_name$description <- gsub("^([^(]+)", "", ca_measure_name$MeasureDescription,
perl = T)
# extract numeric values
r <- regexpr("^[[:digit:]]+[\\/\\.][[:digit:]]",
ca_measure_name$units) out <-
rep(NA,nrow(ca_measure_name))
out[r!=-1] <-
regmatches(ca_measure_name$units, r)
ca_measure_name$numeric <- out
# convert fractions to decimal
fractions <- grep("\\/", ca_measure_name$numeric)
ca_measure_name$numeric[fractions] <- sapply(ca_measure_name$numeric[fractions],
function(x) eval(parse(text=x)))
# fill in blank numeric values
ca_measure_name$numeric[is.na(ca_measure_name$numeric)] <- 1
# everything numberic
ca_measure_name$numeric <- round(as.numeric(ca_measure_name$numeric), 5)
# now remove numbers from units
ca_measure_name$units <- gsub("^[[:digit:]]+[\\/\\.][[:digit:]]", "", ca_measure_name$units)

# format ingredient choices


ca_food_choices <- ca_food_name$FoodID
names(ca_food_choices) <- ca_food_name$FoodDescription

# format daily values

daily_value <- read.table("daily_values.txt", sep = "\t", header=T, stringsAsFactors = F)

ui <- dashboardPage(
dashboardHeader(title = "Nutrition Calculator"),
dashboardSidebar(
selectizeInput(
'food_id', '1. Ingredient', choices = ca_food_choices,
options = list(
placeholder = 'Type to search for ingredient',
onInitialize = I('function() { this.setValue(""); }')
)
),
conditionalPanel('input.food_id != ""',
selectizeInput('measure_unit', '2. Measure Unit', choices = c("Select an ingredient" =
"")), numericInput('quantity', '3. Quantity', value = 1, min = 0, step = 1)),
actionButton("add", "Add ingredient"),
actionButton("remove", "Remove ingredient"),
numericInput("serving", "Number of servings contained", min = 0.01, step = 1, value = 1),
tags$p("Note: All nutrient information is based on the Canadian Nutrient File. Nutrient amounts do
not account for variation in nutrient retention and yield losses of ingredients during preparation. %
daily values (DV) are taken from the Table of Daily Values from the Government of Canada. This
data should not be used for nutritional labeling.")
),
dashboardBody(
fluidRow(
valueBoxOutput("calories"),
valueBoxOutput("over_nutrient"),
valueBoxOutput("rich_nutrient")
),
fluidRow(
box(title = "Ingredients",
solidHeader = T,
width = 4,
collapsible = T,
div(DT::DTOutput("ing_df"), style = "font-size:
70%;")), box(title = "Macronutrients", solidHeader = T,
width = 8, collapsible = T,
plotlyOutput("macro_plot"))

), # row
fluidRow(
box(title = "Nutrition Table",
solidHeader = T,
width = 4,
collapsible = T,
collapsed = F,
tags$p(textOutput("serving", inline = T)),
div(DT::DTOutput("nutrient_table"), style = "font-size: 70%;")),
box(title = "Minerals", solidHeader = T,
width = 8, collapsible = T,
plotlyOutput("mineral_plot"))
),# row
fluidRow(
box(title = "Vitamins", solidHeader=T,
width = 12, collapsible = T,
plotlyOutput("vitamin_plot"))
) # row
) # body
# Define server logic required to draw a
histogram server <- function(input, output,
session) {
# make reactive to store ingredients
ing_df <- shiny::reactiveValues()
ing_df$df <- data.frame("quantity" = numeric(),
"units" = character(),
"ingredient_name" = character(),
"FoodID" = numeric(),
stringsAsFactors = F)
ing_df$measure <- data.frame("numeric" = numeric(),
"units" = character(),
"description" = character(),
"ConversionFactorValue" = numeric(),
"MeasureID" = numeric(),
"FoodID" = numeric(),
stringsAsFactors = F)
# step 1 get singular ingredient
measure_df <- eventReactive(input$food_id,{
measure_df <- ca_food_name[ca_food_name$FoodID==input$food_id,
"FoodID"] %>% left_join(ca_conversion_factor) %>%
left_join(ca_measure_name) %>%
select(numeric, units, description, ConversionFactorValue, MeasureID, FoodID)
# measure_df <- rbind(measure_df, c(numeric = 100, units = "g",
description = "", ConversionFactorValue = 1, MeasureID = ""))
me
asur
e_df
})

# step 2 update the measure unit for singular ingredient


observe({
units <- unique(paste(measure_df()$units, measure_df()$description))
updateSelectInput(session, "measure_unit", "2. Measure Unit", choices = units)
})
# step 3 update the ingredient
dataframe
observeEvent(input$remove, {
isolate(ing_df$df<-ing_df$df[-(nrow(ing_df$df)),])
isolate(ing_df$measure <- ing_df$measure[-
nrow(ing_df$measure),]) })
observeEvent(input$add, {
isolate(ing_df$df[nrow(ing_df$df) + 1,] <-
c(input$quantity,
input$measure_unit,
names(ca_food_choices[ca_food_choices ==
input$food_id]), as.numeric(input$food_id)))
# get actual working ingredient dataframe
for dplyr input_measure <- measure_df()
input_measure <- input_measure[paste(measure_df()$units, measure_df()$description) ==
input$measure_unit, ]
if(nrow(input_measure) > 1){
input_measure <-
input_measure[which(abs(input_measure$numeric-
input$quantity)==min(abs(input_measure$numeric-
input$quantity))),]
}
isolate(ing_df$measure[nrow(ing_df$measure) + 1, ] <- input_measure)
# update choices
updateNumericInput(session, 'quantity', '3. Quantity', 1)
updateSelectizeInput(session, 'measure_unit', '2. Measure Unit')
updateSelectInput(session, 'food_id', '1. Ingredient', choices = ca_food_choices)
})
# main nutrition data frame
nutrition_df <- reactive({
measure_food_df <- ing_df$measure ing_quant
<- ing_df$df measure_food_df$quantity <-
ing_quant$quantity measure_food_df <-
measure_food_df %>%
left_join(ca_nutrient_amount) %>%
left_join(ca_nutrient_name) %>%
# filter(NutrientID %in% select_nutrients) %>%
mutate(NutrientName = tolower(NutrientName)) %>%
mutate(NutrientValue = as.numeric(NutrientValue) * as.numeric(ConversionFactorValue)
* as.numeric(quantity) / as.numeric(numeric) / input$serving) %>%
select(NutrientName, NutrientValue, NutrientID, NutrientUnit, ConversionFactorValue,
quantity, FoodID) %>%
group_by(NutrientName) %>%
summarize(Value = round(sum(NutrientValue, na.rm =
T),2), Unit = first(NutrientUnit),
NutrientID = first(NutrientID))
measure_food_df
})
# display nutrients necessary for
label nutrient_table <- reactive({
select_nutrients <- c(208, 204, 606, 605, 601, 307, 205, 291, 269, 203, 814, 401, 301, 303)
measure_food_df <- nutrition_df() %>% filter(NutrientID %in% select_nutrients)
measure_food_df <- measure_food_df[order(match(measure_food_df$NutrientID,
select_nutrients)),] %>%
select(NutrientName, Value, Unit)
measure_food_df
}) # df with dv%
dv_df <- reactive({
dv_df <- daily_value %>% left_join(nutrition_df())
# hack for total sat fats and trans fats
dv_df$Value[2] <- sum(nutrition_df()$Value[nutrition_df()$NutrientID %in% c(605, 606)], na.rm =
T)
dv_df$Unit[2] <- "g"
dv_df$pct_dv <- round(dv_df$Value / dv_df$DV, 3) *
100 dv_df
})
output$macro_plot <- renderPlotly({
df_macro <- dv_df() %>% filter(Group == "macronutrients")
plot_macro <- ggplot(df_macro) +
geom_col(aes(x = Nutrient, y = pct_dv, fill = pct_dv))
+ labs(x = "Nutrient", y = "% Daily Value") +
theme_gray() + ylim(0, NA) + geom_hline(yintercept = 100) + scale_fill_gradient(low =
"green",
high = "red",
limits = c(0, 100),
na.value = "darkred",
name = "% Daily Value") +
theme(panel.background = element_rect(fill =
"mintcream"), legend.position = "none") +
scale_x_discrete(labels = c("Cholesterol", "Fat", "Fibre", "Sodium", "Sugars",
"Saturated and \n Trans Fats"))
ggplotly(plot_macro)
})
output$mineral_plot <- renderPlotly({
df_min <- dv_df() %>% filter(Group == "mineral")
plot_min <- ggplot(df_min) +
geom_col(aes(x = Nutrient, y = pct_dv, fill =
pct_dv)) + labs(x = "Nutrient", y = "% Daily
Value") + theme_gray() +
ylim(0, NA) +
geom_hline(yintercept = 100) +
scale_fill_gradient(low = "red",
high = "green",
limits = c(0, 100),
na.value = "khaki1",
name = "% Daily Value") +
theme(
legend.position = "none",
panel.background = element_rect(fill = "lightyellow"))
ggplotly(plot_min)
})
output$vitamin_plot <- renderPlotly({
df_vit <- dv_df() %>% filter(Group == "vitamin")
req(input$quantity)
plot_vit <- ggplot(df_vit) +
geom_col(aes(x = Nutrient, y = pct_dv, fill =
pct_dv)) + geom_hline(yintercept = 100) +
labs(x = "Nutrient", y = "% Daily Value") +
theme_gray() +
ylim(0, NA) +
scale_fill_gradient(low = "red",
high = "green",
limits = c(0, 100),
na.value = "khaki1") +
theme(legend.position = "none",
panel.background = element_rect(fill =
"aliceblue"), axis.text.x = element_text(angle =
45, vjust = 1, hjust=1))
ggplotly(plot_vit)
})
# dt indicator
output$ing_df <- DT::renderDataTable(ing_df$df[,1:3],
# colnames = c("Quantity", "Units",
"Ingredient"), rownames=F, options =
list(pageLength = 5))
output$nutrient_table <- DT::renderDataTable(nutrient_table())
# value boxes
output$calories <- renderValueBox({
valueBox(paste0(nutrition_df()$Value[nutrition_df()$NutrientID == 208], "kcal"),
"Calories", icon = icon("fire"), color = "yellow")
})
output$over_nutrient <- renderValueBox({
nutrition_df <- dv_df() %>%
# filter(NutrientID %in% c(601, 204, 307, 269, 0)) %>
% tidyr::drop_na(pct_dv) %>% filter(pct_dv > 100)
if(nrow(nutrition_df) > 0){
valueBox("Over Daily Value", HTML(paste0(nutrition_df$Nutrient, sep="<br>")), icon
= icon("exclamation-triangle"), color = "red")
} else {
if(nrow(nutrition_df) > 0){
valueBox("High levels* of ", HTML(paste0(c(nutrition_df$Nutrient,"*above 50%
recommended DV"), sep="<br>")), icon = icon("exclamation-triangle"), color = "green")
} else {
valueBox(HTML("All nutrients"), "below 50% recommended DV", icon =
icon("exclamation-triangle"), color = "orange")
}
})
output$serving <- renderText(paste("for 1 serving (", input$serving, "servings in recipe)"))
}
# Run the application shinyApp(ui
= ui, server = server)

Output:-

Application In Viewer:-

You might also like