VAISHNAVI REDDY TUDI (s4196984)
19 October, 2025
-RPubs link : http://rpubs.com/vaishnavi_reddy/
Context: This study looks at a collection of online recipes to understand what makes some recipes more complex than others. Complexity here means how long or detailed a recipe is to prepare.
Focus: Two simple factors are used to measure complexity — the number of ingredients a recipe has and the number of preparation steps it includes.
Goal: The aim is to find out whether recipes with more ingredients also tend to have more steps. In other words, does adding more ingredients make a recipe harder to prepare?
This presentation explores how the number of ingredients and preparation steps are connected by using simple statistics and graphs.
It demonstrates how basic regression analysis can be used to find patterns and relationships in real data.
The slides are designed to be short, clear, and easy to follow while still showing proper academic structure. .
I will import the recipe dataset and look at the data to understand what it contains.
I will describe the main variables like number of ingredients and number of steps.
I will use summary statistics and graphs to explore how the variables are related.
I will run a regression model to test if more ingredients mean more steps in a recipe.
-Source: 1_Recipe_csv (1).csv (provided dataset).
-Observation: Each row represents one recipe.
-num_ingredients – number of ingredients.
-num_steps – number of preparation steps.
-category – recipe category.
#--- Load Packages and Read Dataset ---
if(!require(janitor)) install.packages("janitor")
if(!require(dplyr)) install.packages("dplyr")
if(!require(knitr)) install.packages("knitr")
library(janitor)
library(dplyr)
library(knitr)
#Read dataset
recipes <- read.csv("1_Recipe_csv (1).csv", stringsAsFactors = FALSE)
#Clean column names
recipes <- janitor::clean_names(recipes)#--- Dataset Dimensions ---
dims <- data.frame(Rows = nrow(recipes), Columns = ncol(recipes))
knitr::kable(dims, caption = "Dataset Dimensions")| Rows | Columns |
|---|---|
| 62126 | 8 |
#--- Sample of Dataset (Compact View) ---
shorten <- function(x, n = 45){
ifelse(is.na(x), NA, ifelse(nchar(x) > n, paste0(substr(x, 1, n-3), "..."), x))
}
key_cols <- intersect(c("recipe_title","category","subcategory","num_ingredients","num_steps"),
names(recipes))
if(length(key_cols) == 0) key_cols <- names(recipes)[1(5, ncol(recipes))]
preview <- head(recipes[, key_cols, drop = FALSE], 6)
for(col in key_cols){
if(is.character(preview[[col]])) preview[[col]] <- shorten(preview[[col]], 45)
}
knitr::kable(preview, caption = "Sample of Dataset (Compact View)")| recipe_title | category | subcategory | num_ingredients | num_steps |
|---|---|---|---|---|
| Air Fryer Potato Slices with Dipping Sauce | Air Fryer Recipes | Air Fryer Recipes | 9 | 5 |
| Gochujang Pork Belly Bites | Air Fryer Recipes | Air Fryer Recipes | 5 | 4 |
| 3-Ingredient Air Fryer Everything Bagel Ch… | Air Fryer Recipes | Air Fryer Recipes | 3 | 4 |
| Air Fryer Everything Bagel Chicken Cutlets | Air Fryer Recipes | Air Fryer Recipes | 9 | 9 |
| Air Fryer Honey Sriracha Salmon Bites | Air Fryer Recipes | Air Fryer Recipes | 5 | 5 |
| Air Fryer Corn on The Cob | Air Fryer Recipes | Air Fryer Recipes | 6 | 4 |
#--- Column Type Summary and Numeric Summary ---
#Column Type Summary
col_summary <- data.frame(
Column = names(recipes),
Type = sapply(recipes, function(x) class(x)[1])
)
knitr::kable(col_summary, caption = "Column Type Summary (Clean Output)")| Column | Type | |
|---|---|---|
| recipe_title | recipe_title | character |
| category | category | character |
| subcategory | subcategory | character |
| description | description | character |
| ingredients | ingredients | character |
| directions | directions | character |
| num_ingredients | num_ingredients | integer |
| num_steps | num_steps | integer |
#Numeric Summary
numeric_cols <- names(recipes)[sapply(recipes, is.numeric)]
if(length(numeric_cols) > 0){
num_summary <- data.frame(
Variable = numeric_cols,
Min = unname(sapply(recipes[numeric_cols], function(x) min(x, na.rm = TRUE))),
Mean = unname(sapply(recipes[numeric_cols], function(x) mean(x, na.rm = TRUE))),
Median = unname(sapply(recipes[numeric_cols], function(x) median(x, na.rm = TRUE))),
Max = unname(sapply(recipes[numeric_cols], function(x) max(x, na.rm = TRUE))),
Missing = unname(sapply(recipes[numeric_cols], function(x) sum(is.na(x))))
)
num_summary$Mean <- round(num_summary$Mean, 2)
num_summary$Median <- round(num_summary$Median, 2)
knitr::kable(num_summary, caption = "Numeric Summary of Key Variables")
} else {
cat("No numeric variables found in this dataset.")
}| Variable | Min | Mean | Median | Max | Missing |
|---|---|---|---|---|---|
| num_ingredients | 1 | 9.02 | 9 | 35 | 0 |
| num_steps | 1 | 4.66 | 4 | 25 | 0 |
#**The dataset includes some main variables that describe each recipe.
1)recipe_title: The name of the recipe.
2)category: The main group a recipe belongs to, such as “Air Fryer”, “Desserts”, or “Breakfast”.
3)subcategory: A smaller group under each category that gives more detail about the recipe type.
4)num_ingredients: Shows how many ingredients are used in a recipe.
5)num_steps: Shows how many preparation steps are needed to complete the recipe.
-The columns category and subcategory were changed into factors, because they represent types or groups, not numbers. Each level of these factors stands for a different kind of recipe.
-The columns num_ingredients and num_steps were kept as numbers, because they show counts that can be used in calculations like averages or totals.
#**Before starting the analysis, a few cleaning steps were done to make the data ready:
1)Fixed column names so they are easier to use.
2)Changed data types to the correct formats (for example, text to factor, numbers to numeric).
3)Removed rows with missing or wrong values.
4)Checked that all numeric values are correct and make sense.
-These cleaning steps helped make sure the dataset is tidy, accurate, and ready to be used for graphs, summaries, and statistical tests.
-These plots show the overall distributions of ingredients and steps.
-A scatterplot is used to observe any visible relationship.
par(mfrow = c(1,2))
hist(recipes$num_ingredients, main = "Number of Ingredients", xlab = "Ingredients", col = "lightblue")
hist(recipes$num_steps, main = "Number of Steps", xlab = "Steps", col = "lightgreen")par(mfrow = c(1,1))
plot(recipes$num_ingredients, recipes$num_steps,
main = "Steps vs Ingredients",
xlab = "Number of Ingredients",
ylab = "Number of Steps",
pch = 19, col = "blue")
abline(lm(num_steps ~ num_ingredients, data = recipes), col = "red", lwd = 2)-Summary by category showing average ingredients and steps.
by_cat <- recipes %>%
group_by(category) %>%
summarise(
n_recipes = n(),
avg_ingredients = mean(num_ingredients, na.rm = TRUE),
avg_steps = mean(num_steps, na.rm = TRUE)
) %>%
arrange(desc(n_recipes))
knitr::kable(head(by_cat, 10), caption = "Top 10 Categories by Count and Average Complexity")| category | n_recipes | avg_ingredients | avg_steps |
|---|---|---|---|
| Main Dishes | 3387 | 10.421022 | 5.496605 |
| Healthy Recipes | 2237 | 7.724184 | 4.003129 |
| Appetizers And Snacks | 2084 | 7.903551 | 4.198656 |
| Cakes | 1954 | 9.615148 | 5.618219 |
| Cookies | 1849 | 8.843699 | 5.214711 |
| Beef Recipes | 1400 | 10.569286 | 5.130000 |
| Breads | 1352 | 9.116124 | 5.714497 |
| Desserts | 1288 | 7.242236 | 5.356367 |
| Breakfast And Brunch | 1223 | 8.147997 | 4.270646 |
| Pork | 1205 | 10.507054 | 5.139419 |
-Bar chart of the top 10 categories by number of recipes.
#Make sure data is available
if (!exists("recipes")) {
recipes <- read.csv("1_Recipe_csv (1).csv", stringsAsFactors = FALSE)
}
#Only proceed if 'category' exists
if ("category" %in% names(recipes)) {
#Count recipes per category and take top 10
library(dplyr)
top10 <- recipes %>%
count(category, name = "n_recipes") %>%
arrange(desc(n_recipes)) %>%
slice_head(n = 10)
#Draw bar plot
barplot(height = top10$n_recipes,
names.arg = as.character(top10$category),
las = 2, cex.names = 0.8,
main = "Top 10 Categories (Count of Recipes)",
ylab = "Count")
} else {
cat("Note: 'category' column not found in the dataset; cannot plot top categories.\n")
}num_steps = β0 + β1 * num_ingredients + ε# Safety check: reload data if missing
if (!exists("recipes")) {
recipes <- read.csv("1_Recipe_csv (1).csv", stringsAsFactors = FALSE)
}
#Simple Linear Regression Model
model1 <- lm(num_steps ~ num_ingredients, data = recipes)
#Show model code and clean table result
summary(model1)$coefficients["num_ingredients", , drop = FALSE] |>
as.data.frame() |>
tibble::rownames_to_column("Variable") |>
knitr::kable(
caption = "T-Test for num_ingredients in Simple Linear Regression"
)| Variable | Estimate | Std. Error | t value | Pr(>|t|) |
|---|---|---|---|---|
| num_ingredients | 0.1238493 | 0.0023683 | 52.29418 | 0 |
#Show key model metrics neatly
summary_table <- data.frame(
R_Squared = round(summary(model1)$r.squared, 4),
Adj_R_Squared = round(summary(model1)$adj.r.squared, 4),
F_Statistic = round(summary(model1)$fstatistic[1], 3),
P_Value = signif(pf(summary(model1)$fstatistic[1],
summary(model1)$fstatistic[2],
summary(model1)$fstatistic[3],
lower.tail = FALSE), 4)
)
knitr::kable(summary_table, caption = "Model Summary Statistics")| R_Squared | Adj_R_Squared | F_Statistic | P_Value | |
|---|---|---|---|---|
| value | 0.0422 | 0.0421 | 2734.681 | 0 |
The model is adjusted for recipe category (top 10 + “Other”) to check if cooking styles affect the relationship.
This helps control for different cooking styles.
#Adjust for top 10 categories
top10_cats <- names(sort(table(recipes$category), decreasing = TRUE))[1:10]
recipes$category_limited <- ifelse(recipes$category %in% top10_cats, recipes$category, "Other")
recipes$category_limited <- factor(recipes$category_limited)
#Multiple Linear Regression
model2 <- lm(num_steps ~ num_ingredients + category_limited, data = recipes)
#Clean coefficient table
summary(model2)$coefficients["num_ingredients", , drop = FALSE] |>
as.data.frame() |>
tibble::rownames_to_column("Variable") |>
knitr::kable(
caption = "T-Test for num_ingredients (Adjusted for Category)"
)| Variable | Estimate | Std. Error | t value | Pr(>|t|) |
|---|---|---|---|---|
| num_ingredients | 0.1172847 | 0.0023704 | 49.47811 | 0 |
#Add R-squared and F-stat summary for adjusted model
summary_table2 <- data.frame(
R_Squared = round(summary(model2)$r.squared, 4),
Adj_R_Squared = round(summary(model2)$adj.r.squared, 4),
F_Statistic = round(summary(model2)$fstatistic[1], 3),
P_Value = signif(pf(summary(model2)$fstatistic[1],
summary(model2)$fstatistic[2],
summary(model2)$fstatistic[3],
lower.tail = FALSE), 4)
)
knitr::kable(summary_table2, caption = "Adjusted Model Summary Statistics")| R_Squared | Adj_R_Squared | F_Statistic | P_Value | |
|---|---|---|---|---|
| value | 0.0667 | 0.0666 | 403.778 | 0 |
#Discussion
Findings: The results show that recipes with more ingredients usually have more preparation steps. This means there is a clear positive connection between the two variables.
Strengths: The dataset has many recipes from different categories, which helps make the results more reliable. The method used is simple and easy to understand.
Limitations: The study only looks at two things — ingredients and steps. It does not include cooking time, skill level, or other details that could affect complexity.
Future Work: In the future, more factors like cooking time or ingredient type can be added to make the analysis more accurate and detailed.
Recipes that have more ingredients usually take more steps to prepare.
This shows that the number of ingredients is a good way to understand how complex a recipe is.
Even when recipe categories are considered, this pattern stays the same.