Статьи

Вкусный анализ: тематическое моделирование с использованием рецептов

Несколько месяцев назад я увидел в Твиттере ссылку на удивительный график, на котором изображены сходства различных продуктов на основе их вкусовых соединений, а также их распространенность в рецептах (см. Целое исследование,  Сеть вкусов и Принципы сочетания продуктов ). , Я подумал, что это действительно здорово и заинтересовался потенциальным использованием данных для чего-то немного другого; чтобы выяснить, какие ингредиенты имеют тенденцию коррелировать по рецептам. Я написал по электронной почте одному из авторов, Йонг-Йол Ану, который, кстати, настоящий менш, и он сообщил мне, что необработанные рецептурные данные легко доступны на его сайте!

Учитывая мою цель поиска того, какие ингредиенты коррелируют между рецептами, я подумал, что это была бы идеальная возможность использовать тематическое моделирование (здесь я использую скрытое выделение дирихле  или LDA). Обычно в тематическом моделировании у вас много работы по фильтрации. Не так с этими данными рецепта, где все слова (ингредиенты), включенные в корпус, представляют потенциальный интерес, и нет даже знаков препинания! Темы, выходящие из анализа, будут представлять группы ингредиентов, которые встречаются друг с другом в разных рецептах, и, возможно, научат меня кое-чему о кулинарии (о которой я знаю очень мало!).

Весь мой код находится внизу, поэтому здесь вы найдете только графики и мое текстовое резюме. Первое, что я сделал, это собрал 3 необработанных файла рецептов, используя python. Каждый файл состоял из одного рецепта в строке, кухня рецепта была первой записью в строке, а все остальные записи (ингредиенты) были разделены символами табуляции. В своем скрипте Python я отделил кухню от ингредиентов и создал два файла, один для рецептов, а другой для кухонь из рецептов.

Затем я загрузил рецепты в R и получил количество слов / ингредиентов. Как вы можете видеть ниже, 3 самых популярных ингредиента — яйцо, пшеница и масло. Это имеет смысл, учитывая тот факт, что примерно 70% всех рецептов относятся к «американской» кухне. Я сделал этот анализ ради новизны, и поэтому я решил, что вытащу эти ингредиенты из состава, прежде чем я продолжу. Яйцо делает меня пукающим, пшеница — это не то, что у меня есть дома в сыром виде, и масло не важно для меня для целей этого анализа!

Популярность рецепта 30 лучших ингредиентов

Вот лучшие ингредиенты без трех отфильтрованных:

Популярность рецепта 30 лучших ингредиентов - без яичной пшеницы или масла

Наконец, я запустил LDA, выделив 50 тем и топ-5 наиболее характерных ингредиентов каждой темы. Вы можете увидеть полный набор тем внизу моего поста, но я подумала, что рассмотрю некоторые, которые я нахожу интригующими. Вы, конечно, найдете другие темы интригующими, или некоторые, которые будут странными и неуместными (не стесняйтесь говорить мне в разделе комментариев). Во-первых, тема 4:

[1] "tomato"  "garlic"  "oregano" "onion"   "basil"

Вот группа ингредиентов, которая кажется совершенно итальянской. Ингредиенты, кажется, имеют смысл вместе, и поэтому я думаю, что попробую их вместе в следующий раз, когда я делаю макароны (хотя я не люблю помидоры в их первоначальном виде, только томатный соус).

Далее тема 19:

[1] "vanilla" "cream"   "almond"  "coconut" "oat"

Это привлекло мое внимание, и мне любопытно, могут ли ингредиенты иметь смысл вместе. Ваниль и сливки имеют смысл … Кажется, добавление кокоса также имеет смысл. Миндаль дал бы этому дополнительный кризис (если это не миндальное молоко!). Однако я не знаю, будет ли это вкусно, поэтому я, наверное, пройду мимо этого.

Далее тема 20:

[1] "onion"         "black_pepper"  "vegetable_oil" "bell_pepper"   "garlic"

Это выглядит вкусно! Я люблю острую пищу, поэтому мне нравится смешивать черный перец с луком, чесноком и сладким перцем!

Далее тема 23:

[1] "vegetable_oil" "soy_sauce"     "sesame_oil"    "fish"          "chicken"

Теперь мы в мясной зоне! Я все за то, чтобы положить соусы / масла на мясо, но совмещать растительное масло, соевый соус и кунжутное масло кажется излишним. Интересно, будет ли соевый соус появляться с растительным маслом или кунжутным маслом отдельно в рецептах, а не связывать их все вместе в одних и тех же рецептах? Мне всегда нравился дополнительный соленый вкус соевого соуса, хотя я знаю, что это ужасно для вас, так как в нем есть глютамат натрия. Интересно, как бы было на вкус растительное масло, соевый соус и курица? Что-то попробовать, наверняка!

Теперь тема 26:

[1] "cumin"      "coriander"  "turmeric"   "fenugreek"  "lemongrass"

Это много специй, которые я никогда не использую в своей еде. Не из-за отсутствия желания, а скорее из-за невежества и лени. Одна из моих коллег недавно прокомментировала, что тмин добавляет действительно хороший вкус к еде (я думаю, она назвала это «ближневосточной»). Я никогда не слышал ничего о других специях здесь, но почему бы не попробовать их!

Далее тема 28:

[1] "onion"       "vinegar"     "garlic"      "lemon_juice" "ginger"

Я склонен находить, что что-нибудь с интенсивным ароматом может быть очень аппетитным для меня. Специи, уксус и все, что лимонное — это то, что действительно записывается на моем языке. Таким образом, эта тема выглядит очень интересной для меня, вероятно, в качестве начинки или соуса. Интересно, что здесь появляется имбирь, так как он нейтрализует другие ароматы, поэтому мне интересно, включу ли я его в любой соус, который я делаю?

Последний! Тема 41:

[1] "vanilla"  "cocoa"    "milk"     "cinnamon" "walnut"

Они выглядят как ингредиенты для какого-то хорошего напитка (ты бы раздавил грецкие орехи? Я не уверен!)

Что ж, надеюсь, вам понравилось так же, как и мне! Это не идеальный анализ, но он определенно очень вкусный. :)  Опять же, не стесняйтесь оставлять любые комментарии о любой из комбинаций ингредиентов или вопросы, на которые, по вашему мнению, можно ответить с помощью другого анализа!

import os
rfiles = os.listdir('.')
rc = []
for f in rfiles:
    if '.txt' in f: 
    # The recipes come in 3 txt files consisting of 1 recipe per line, the 
    # cuisine of the recipe as the first entry in the line, and all subsequent ingredient
    # entries separated by a tab
        infile = open(f, 'r')
        rc.append(infile.read())
        infile.close()
all_rs = '\n'.join(rc)
import re
line_pat = re.compile('[A-Za-z]+\t.+\n')
recipe_lines = line_pat.findall(all_rs)
new_recipe_lines = []
cuisine_lines = []
for n,r in enumerate(recipe_lines):
    # First we find the cuisine of the recipe
    cuisine = r[:r.find('\t')]
    # Then we append the ingredients withou the cuisine
    new_recipe_lines.append(recipe_lines[n].replace(cuisine, ''))
    # I saved the cuisines to a different list in case I want to do some 
    # cuisine analysis later
    cuisine_lines.append(cuisine + '\n')

outfile1 = open('recipes combined.tsv', 'wb')
outfile1.write(''.join(new_recipe_lines))
outfile1.close()

outfile2 = open('cuisines.csv', 'wb')
outfile2.write(''.join(cuisine_lines))
outfile2.close()
recipes = readLines('recipes combined.tsv')

# Once I read it into R, I have to get rid of the /t
# characters so that it's more acceptable to the tm package

recipes.new = apply(as.matrix(recipes), 1, function (x) gsub('\t',' ', x))

recipes.corpus = Corpus(VectorSource(recipes.new))
recipes.dtm = DocumentTermMatrix(recipes.corpus)

# Now I filter out any terms that have shown up in less than 10 documents

recipes.dict = Dictionary(findFreqTerms(recipes.dtm,10))
recipes.dtm.filtered = DocumentTermMatrix(recipes.corpus, list(dictionary = recipes.dict))

# Here I get a count of number of ingredients in each document
# with the intent of deleting any documents with 0 ingredients

ingredient.counts = apply(recipes.dtm.filtered, 1, function (x) sum(x))
recipes.dtm.filtered = recipes.dtm.filtered[ingredient.counts > 0]

# Here i get some simple ingredient frequencies so that I can plot them and decide 
# which I'd like to filter out

recipes.m = as.matrix(recipes.dtm.filtered)
popularity.of.ingredients = sort(colSums(recipes.m), decreasing=TRUE)
popularity.of.ingredients = data.frame(ingredients = names(popularity.of.ingredients), num_recipes=popularity.of.ingredients)
popularity.of.ingredients$ingredients = reorder(popularity.of.ingredients$ingredients, popularity.of.ingredients$num_recipes)

library(ggplot2)

ggplot(popularity.of.ingredients[1:30,], aes(x=ingredients, y=num_recipes)) + geom_point(size=5, colour="red") + coord_flip() +
ggtitle("Recipe Popularity of Top 30 Ingredients") + 
theme(axis.text.x=element_text(size=13,face="bold", colour="black"), axis.text.y=element_text(size=13,colour="black",
face="bold"), axis.title.x=element_text(size=14, face="bold"), axis.title.y=element_text(size=14,face="bold"),
plot.title=element_text(size=24,face="bold"))

# Having found wheat, egg, and butter to be the three most frequent ingredients
# (and not caring too much about them as ingredients in general) I remove them 
# from the corpus and redo the document term matrix

recipes.corpus = tm_map(recipes.corpus, removeWords, c("wheat","egg","butter"))  # Go back to line 6
recipes.dtm.final = DocumentTermMatrix(recipes.corpus, list(dictionary = recipes.dict))

# Finally, I run the LDA and extract the 5 most
# characteristic ingredients in each topic... yummy!

recipes.lda = LDA(recipes.dtm.filtered, 50)
t = terms(recipes.lda,5)

     Topic 1         Topic 2   Topic 3         Topic 4   Topic 5        Topic 6    Topic 7       Topic 8     Topic 9        
[1,] "onion"         "pepper"  "milk"          "tomato"  "olive_oil"    "milk"     "milk"        "tomato"    "garlic"       
[2,] "rice"          "vinegar" "vanilla"       "garlic"  "garlic"       "nutmeg"   "pepper"      "cayenne"   "cream"        
[3,] "cayenne"       "onion"   "cocoa"         "oregano" "onion"        "vanilla"  "yeast"       "olive_oil" "vegetable_oil"
[4,] "chicken_broth" "tomato"  "onion"         "onion"   "black_pepper" "cinnamon" "potato"      "garlic"    "pepper"       
[5,] "olive_oil"     "milk"    "cane_molasses" "basil"   "vinegar"      "cream"    "lemon_juice" "pepper"    "milk"         
     Topic 10        Topic 11              Topic 12        Topic 13       Topic 14    Topic 15   Topic 16        Topic 17       
[1,] "milk"          "soy_sauce"           "vegetable_oil" "onion"        "milk"      "tamarind" "milk"          "vegetable_oil"
[2,] "cream"         "scallion"            "milk"          "black_pepper" "cinnamon"  "onion"    "vanilla"       "pepper"       
[3,] "vanilla"       "sesame_oil"          "pepper"        "vinegar"      "onion"     "garlic"   "cream"         "cream"        
[4,] "cane_molasses" "cane_molasses"       "cane_molasses" "bell_pepper"  "cayenne"   "corn"     "vegetable_oil" "black_pepper" 
[5,] "cinnamon"      "roasted_sesame_seed" "cinnamon"      "bacon"        "olive_oil" "vinegar"  "garlic"        "mustard"      
     Topic 18        Topic 19  Topic 20        Topic 21        Topic 22    Topic 23        Topic 24        Topic 25       Topic 26    
[1,] "cane_molasses" "vanilla" "onion"         "garlic"        "onion"     "vegetable_oil" "onion"         "cream"        "cumin"     
[2,] "onion"         "cream"   "black_pepper"  "cane_molasses" "garlic"    "soy_sauce"     "garlic"        "tomato"       "coriander" 
[3,] "vinegar"       "almond"  "vegetable_oil" "vinegar"       "tomato"    "sesame_oil"    "cane_molasses" "chicken"      "turmeric"  
[4,] "olive_oil"     "coconut" "bell_pepper"   "black_pepper"  "olive_oil" "fish"          "tomato"        "lemon_juice"  "fenugreek" 
[5,] "pepper"        "oat"     "garlic"        "soy_sauce"     "basil"     "chicken"       "vegetable_oil" "black_pepper" "lemongrass"
     Topic 27       Topic 28      Topic 29        Topic 30    Topic 31   Topic 32        Topic 33       Topic 34        Topic 35   
[1,] "onion"        "onion"       "onion"         "onion"     "vanilla"  "garlic"        "onion"        "onion"         "garlic"   
[2,] "garlic"       "vinegar"     "celery"        "pepper"    "milk"     "onion"         "pepper"       "garlic"        "basil"    
[3,] "black_pepper" "garlic"      "chicken"       "garlic"    "garlic"   "vegetable_oil" "garlic"       "vegetable_oil" "pepper"   
[4,] "tomato"       "lemon_juice" "vegetable_oil" "parsley"   "cinnamon" "cayenne"       "black_pepper" "black_pepper"  "tomato"   
[5,] "olive_oil"    "ginger"      "carrot"        "olive_oil" "cream"    "beef"          "beef"         "chicken"       "olive_oil"
     Topic 36        Topic 37       Topic 38        Topic 39  Topic 40      Topic 41   Topic 42        Topic 43   Topic 44       
[1,] "onion"         "onion"        "onion"         "cayenne" "garlic"      "vanilla"  "vanilla"       "scallion" "milk"         
[2,] "garlic"        "garlic"       "cream"         "garlic"  "onion"       "cocoa"    "cane_molasses" "garlic"   "tomato"       
[3,] "cayenne"       "black_pepper" "tomato"        "ginger"  "bell_pepper" "milk"     "cocoa"         "ginger"   "garlic"       
[4,] "vegetable_oil" "lemon_juice"  "cane_molasses" "rice"    "olive_oil"   "cinnamon" "oat"           "soybean"  "vegetable_oil"
[5,] "oregano"       "scallion"     "milk"          "onion"   "milk"        "walnut"   "milk"          "pepper"   "cream"        
     Topic 45       Topic 46        Topic 47        Topic 48 Topic 49        Topic 50         
[1,] "onion"        "cream"         "pepper"        "cream"  "milk"          "olive_oil"      
[2,] "cream"        "black_pepper"  "vegetable_oil" "tomato" "vanilla"       "tomato"         
[3,] "black_pepper" "chicken_broth" "garlic"        "beef"   "lard"          "parmesan_cheese"
[4,] "milk"         "vegetable_oil" "onion"         "garlic" "cocoa"         "lemon_juice"    
[5,] "cinnamon"     "garlic"        "olive_oil"     "carrot" "cane_molasses" "garlic"