Published on

Thesis Thursday 4 - Analysing Recipes

Authors
  • Name
    Twitter

One of the main component of my thesis is a mapping from consumers' purchases to country related expenditure shares. This requires a method to associate each available product to a particular country. I have briefly discussed the issue in the introductory post but have made significant progress on this front that I think is worth sharing.

The recipe dataset

This recipe dataset was created by scraping recipes from allrecipes.com that are tagged to particular region or country. For each recipe, I collected information on the ingredients used, as well as other interesting but less immediately relevant characteristics like recipe time, servings, calories, number of reviews and ratings. After some simple data cleaning I am left with a collection of about 6000 recipes. I edited the geographical region tagged to each recipe to be consistent with the level of aggregation I have in the U.S. census.

A first look

Let us explore the dataset to pick up some trends and characteristics. I normally like to produce some simple summary statistics to get a better understanding of the data I have on hand.

cols <- c("recipe_time", "recipe_calorie", "recipe_reviews",
          "recipe_stars", "num_ingr")

stargazer(recipe_df[recipe_df$recipe_reviews>1, cols],
          type= "html",
          title = "Summary Statistics of Allrecipe Data (Reviews > 1)",
          summary.stat = c("min", "p25", "median", "p75", "max", "mean", "sd"),
          digits=2,
          flip=T)
Statisticrecipe_timerecipe_calorierecipe_reviewsrecipe_starsnum_ingr
Min0.001.0021.332
Pctl(25)30.00179.00104.157
Median55.00306.50294.4310
Pctl(75)100.00466.001104.6012
Max1,035.003,274.0010,6445.0030
Mean98.20343.91124.264.3310.01
St. Dev.129.92227.41336.040.414.02

Next, let us take a look at the distribution of ratings across the dataset.

ggplot(recipe_df, aes(x = recipe_stars)) + geom_density() + theme_classic()
Density plot of number of stars a recipe is rated

Not surprisingly, people who leave a rating tend to be on the generous side. The median recipe score is 4.4. There are also quite a significant fraction of recipes without any reviews. The distribution of reviews is closer to a pareto distribution or log-normal.

ggplot(recipe_df, aes(x=ln_recipe_reviews)) + geom_density() + theme_classic()
Density plot of log recipe reviews

Do recipes with more reviews score better? Apparently so.1

ggplot(recipe_df, aes(y=recipe_stars, x=ln_recipe_reviews)) + geom_point() + theme_classic()
Plot of recipe rating on log reviews

Text analysis

Now for the fun part - an analysis on the ingredients used. I will skip over the slightly tedious data cleaning procedures used to transform the recipe dataset to a coherent list of words associated with each geographical region / country.2

Let us take a look at the most common ingredient related words used across all recipes.

tidy_recipe %>%
  count(word, sort = TRUE) %>%
  filter(n > 1500) %>%
  mutate(word = reorder(word, n)) %>%
  ggplot(aes(word, n)) +
  geom_col() +
  xlab(NULL) +
  coord_flip() +
  theme_classic()
List of common words

Salt and pepper comes up on top. White and black ranks highly too. These words are normally used to describe certain ingredients (e.g. white pepper, white flour).3 Next, we can compare the frequency of words used in recipes from two different countries.

frequency <- tidy_recipe %>%
             count(geog, word) %>%
             group_by(geog) %>%
             mutate(proportion = n / sum(n)) %>%
             select(-n) %>%
             ungroup() %>%
             dcast(word ~ geog)

ggplot(frequency, aes(x = France, y = China)) +
  geom_abline(color = "gray40", lty = 2) +
  geom_text(aes(label = word), check_overlap = TRUE, vjust = 1.5) +
  scale_x_log10(labels = percent_format()) +
  scale_y_log10(labels = percent_format()) +
  theme_classic() +
  theme(legend.position="none")
Comparison of words used in recipes tagged with France and China

Observations at the top right are ingredients which are commonly used across both recipe groups while those at the bottom left are rarely used. The results are in line with my prior expectations - chinese recipes use rice, soy and sesame a lot more frequently, while french recipes tend to feature dairy ingredients more prominently.

To summarise the similarity between recipes from different regions, we can calculate the correlation between the frequency of words used across regions.

temp <- cbind(frequency$China, frequency$India, frequency$France, frequency$Italy, frequency$Japan)
colnames(temp) <- c("China", "India", "France", "Italy", "Japan")
cor_matrix <- cor(temp, use="pairwise.complete.obs")
stargazer(cor_matrix, type="html",
          title="Country correlation matrix")
ChinaIndiaFranceItalyJapan
China10.6490.5820.5390.837
India0.64910.6220.6230.571
France0.5820.62210.7260.583
Italy0.5390.6230.72610.503
Japan0.8370.5710.5830.5031

That is about it for an exploratory analysis of the recipe dataset. I plan on using it as a word corpus. By mapping product information to the relative frequency of words used, I would be able to construct a measure of product to country similarity and finally a country weighted expenditure basket.

Footnotes

  1. Of course one cannot conclude the direction of causality. It could be the case that truly better recipes are rated more highly and also receive more reviews. One could also argue that more highly rated recipes receive more reviews or even recipes with more reviews tend to appear better attracting higher scores.

  2. Basically, this involves cleaning up the text field and removing words not related to ingredients such as cooking procedures and measurement terms. Subsequently, I tokenise the entire list of ingredients such that each word is an observation by itself.

  3. In my research I consider both single words and bi-grams (two words) to get better accuracy on matching.