Computational Gastronomy: Using the Wolfram Language to Prepare a Sumptuous Holiday Feast
In recent years there’s been a growing interest in the intersection of food and technology. However, many of the new technologies used in the kitchen are cooking tools and devices such as immersion circulators, silicone steam baskets and pressure ovens. Here at Wolfram, our approach has been a bit different, with a focus on providing tools that can query for, organize, visualize and compute data about food, cooking and nutrition.
Last Christmas I went home to Tucson, Arizona, to spend time with my family over the holidays. Because I studied the culinary arts and food science, I was quickly enlisted to cook Christmas dinner. There were going to be a lot of us at my parents’ house, so I was aware this would be no small task. But I curate food and nutrition data for Wolfram|Alpha, so I knew the Wolfram technology stack had some excellent resources for pulling off this big meal without a hitch.
Building a Christmas Dinner Survey
Our family has diverse tastes, and we had at least one vegan in the bunch last year. I wanted to make sure that everyone felt included and was served a meal they could really enjoy. So with the help of one my Wolfram colleagues, I created a Christmas dinner survey using the Wolfram Cloud and Wolfram Data Drop.
Setting Up a Databin
The Wolfram Data Drop is a great way to collect and store data from IoT (Internet of Things) devices. Whether you’re collecting weather data from your desk or mileage data from your car, Data Drop provides a great platform to collect data and store it safely in the Wolfram Cloud, and it allows you to use tools in the Wolfram Language to analyze the data at your leisure. The Wolfram Data Drop can also be used to collect data from people through web forms built in the Wolfram Language.
The first step in using the Wolfram Data Drop is to create a new databin where the data will be stored. The code shown here demonstrates how easy this is and includes options for naming the databin, specifying the administrator and setting permissions.
✕
foodSurveyDatabin = CreateDatabin[ <| "Name" -> "Christmas Dinner Survey 2017", "Administrator" -> "micahl@wolfram.com", Permissions -> "Public" |> ] |
Creating a Web Form
Making web forms in the Wolfram Language is one of my personal favorite features introduced in the last few years. While creating web forms may not generate headlines in the way functions like Classify or ImageRestyle might, the practical applications of web forms in a variety of contexts are nearly limitless—and they are a great introduction to writing code and computational thinking because the web forms are easy to design, build, test and tweak, even for people new to the Wolfram Language and programming. Using web forms is a great way to collect data because the forms are easy to fill out. We can also deploy them to the web so we can access them from desktop or mobile platforms. They also store the interpreted data in a uniform structure, making the later steps of data analysis and visualization much, much easier.
Using FormFunction, DatabinAdd, and CloudDeploy, we can build a custom survey that collects and interprets data. This is then stored using WDF (Wolfram Data Framework), which utilizes the Wolfram Knowledgebase and knows about Entities (such as cities and foods), as well as quantities, images and networks.
(Here’s a downloadable notebook with the code to build this web form.)
Sending Out Emails Programmatically
After I built the survey, I needed an easy way to invite my family members to respond to it. Using tools available in the Wolfram Language, I was able to quickly design a form email that I could deploy in just a few lines of code that would also address each family member individually. I constructed an Association with the names and email addresses of my family members and wrote a brief email asking the recipient to fill out the Christmas dinner survey. Then I used SendMail to slot in the name, email address, email body and survey hyperlink, and sent the emails en masse to everyone invited to Christmas dinner.
✕
emailBody = "We're looking forward to having you over for Christmas dinner! As \ we'll be hosting a decent-sized crowd, I've created a brief survey \ where you can fill out any dietary preferences, restrictions, \ allergies, or recommendations. Here's the link: \ https://www.wolframcloud.com/"; hyperlink = Hyperlink[ "Christmas Dinner Survey 2017", "https://www.wolframcloud.com/objects/micahl20160425165842Rywd/\ Christmas2017/survey" ]; SendMail[ <| "To" -> #EmailAddress, "Subject" -> "Christmas Dinner Survey 2017", "Body" -> StringTemplate["Dear ``,\n\n``"][#Name, emailBody], "Signature" -> "Thanks,\nMicah" |> ] & /@ emailList |
A European Holiday Food Map
Gathering food preferences from my family was just the beginning. I always want to wow the people I cook for, so I also needed some inspiration for enhancing the dishes I’d serve. Using tools in the Wolfram Language, it’s easy to build visual aids to assist with culinary experimentation, which often helps get my creative juices flowing. I’ve personally put loads of food images into Wolfram|Alpha, and I know that the Wolfram Language has access to the treasure trove of content in Wolfram|Alpha. So I thought I’d play around with this resource.
Data visualizations come in many forms these days, and the Wolfram Language provides numerous built-in functions to create them, such as WordCloud and ImageCollage. But I thought I’d take a holiday-food visualization one step further…
I was thinking about how particular holiday dishes and preparations are associated with the nations where they originated. Certain ingredients may be revered or taboo, depending on the local culture and religion. Geography also plays an important role due to the environmental needs of source ingredients. For example, rye and oats are the grains of choice in Northern Europe because wheat doesn’t grow well in that colder climate. I decided using a map with images of traditional holiday dishes could lead to the “aha” moment I was looking for.
To get started, I curated image URLs of holiday dishes from European countries and gathered them in an Association. Associations are a great way to store data because they are very fast and have labeled Key/value pairs, which are easy to understand and query. Next, I used Put to create a package file that stores Wolfram Language expressions as a separate file that allows for better organization of data and code. Then, using CloudObject and CopyFile, I uploaded the package file to the Wolfram Cloud, setting SetPermisions to "Public", which allows anyone to access the data. Finally, I used a simple CloudGet on the CloudObject to download the data from the Wolfram Cloud directly into a notebook.
The next steps in the process take the image data from the Wolfram Cloud and visualize the images using geography data built into the Wolfram Language. Using functions such as GeoGraphics and GeoStyling, I was able to construct a map of images of traditional holiday foods displayed over their home nations. The Tooltip function provides a clean and nifty way to display the name of each dish without cluttering the map with textual labels or unsightly keys. ImageAssemble tiles the images into a collage, so the dish in question is easier to see when displayed on its country of origin. And EdgeForm defines the borders of each country, making the image easier to recognize as a map of Europe.
To collect the images I searched Creative Commons. From there, I simply grabbed the image file name, author attribution, dish name and country and placed them in a List of Associations.
Creating this map requires a bit of curation. I assembled the images I needed in this package file you can download. Just make sure you place it in the same file directory as the target notebook.
✕
SetDirectory[NotebookDirectory[]]; euroHolidayFoodsCloudObject = CloudObject["EuropeanHolidayFoodMap/EuropeanChristmasDishImages.m"]; CopyFile["EuropeanChristmasDishImages.m", euroHolidayFoodsCloudObject]; SetPermissions[euroHolidayFoodsCloudObject, "Public"]; euroHolidayFoods = CloudGet[euroHolidayFoodsCloudObject] |
(This output is only a sample set with the first three entries. The package file I mentioned earlier has the complete set.)
The map is a slick bit of code that uses GeoGraphics to tile each image over its source country. Tooltip allows you to hover the cursor over each country to see a pop-up of the associated food.
✕
GeoGraphics[ { With[ {image = Import[#ImageURL]}, { GeoStyling[ { "Image", ImageAssemble[ ConstantArray[ Replace[ ImageTrim[ image, {{.2, .2}, {.8, .8}}, DataRange -> {{0, 1}, {0, 1}} ], i_Image /; (ImageDimensions[i][[1]] > 1000) :> ImageResize[i, 500] ], {3, 3} ] ] } ], EdgeForm[Gray], Tooltip[Polygon[#Country], Framed[Row@{#DishName, " ", image}, FrameStyle -> Gray]] } ] & /@ euroHolidayFoods }, GeoRange -> \!\(\* NamespaceBox["LinguisticAssistant", DynamicModuleBox[{Typeset`query$$ = "europe", Typeset`boxes$$ = TemplateBox[{"\"Europe\"", RowBox[{"Entity", "[", RowBox[{"\"GeographicRegion\"", ",", "\"Europe\""}], "]"}], "\"Entity[\\\"GeographicRegion\\\", \\\"Europe\\\"]\"", "\"geographic region\""}, "Entity"], Typeset`allassumptions$$ = {{ "type" -> "Clash", "word" -> "europe", "template" -> "Assuming \"${word}\" is ${desc1}. Use as \ ${desc2} instead", "count" -> "3", "Values" -> {{ "name" -> "GeographicRegion", "desc" -> "a continent", "input" -> "*C.europe-_*GeographicRegion-"}, { "name" -> "CountryClass", "desc" -> "a class of countries", "input" -> "*C.europe-_*CountryClass-"}, { "name" -> "Word", "desc" -> "a word", "input" -> "*C.europe-_*Word-"}}}}, Typeset`assumptions$$ = {}, Typeset`open$$ = {1, 2}, Typeset`querystate$$ = { "Online" -> True, "Allowed" -> True, "mparse.jsp" -> 3.091041`6.941649759140215, "Messages" -> {}}}, DynamicBox[ToBoxes[ AlphaIntegration`LinguisticAssistantBoxes["", 4, Automatic, Dynamic[Typeset`query$$], Dynamic[Typeset`boxes$$], Dynamic[Typeset`allassumptions$$], Dynamic[Typeset`assumptions$$], Dynamic[Typeset`open$$], Dynamic[Typeset`querystate$$]], StandardForm], ImageSizeCache->{217., {7., 17.}}, TrackedSymbols:>{ Typeset`query$$, Typeset`boxes$$, Typeset`allassumptions$$, Typeset`assumptions$$, Typeset`open$$, Typeset`querystate$$}], DynamicModuleValues:>{}, UndoTrackedVariables:>{Typeset`open$$}], BaseStyle->{"Deploy"}, DeleteWithContents->True, Editable->False, SelectWithContents->True]\), ImageSize -> Large, GeoRangePadding -> {{Quantity[-50, "Kilometers"], Quantity[-600, "Kilometers"]}, {Quantity[-250, "Kilometers"], -Quantity[575, "Kilometers"]}} ] |
Computational Solutions to Culinary Conundrums
My Christmas dinner project was well on its way. I had dish requests. I had some ideas for sprucing them up with some unusual ingredients and preparation methods from “the old countries.” But would I have everything I needed to make it all happen?
Running out of ingredients while cooking a meal for a large group is one of the most discouraging and frustrating experiences for home cooks. Cooking during the holidays can be especially challenging because many grocery stores and markets are closed or have limited hours. Plus, when cooking a large meal, it can be difficult to find the time to duck out and purchase the missing ingredients due to the various tasks that must be carefully attended throughout meal preparation. Fortunately, the Wolfram Language has you covered.
My brother Caleb requested carrot purée as a side dish for Christmas dinner. I was eager to cater to my brother’s request, but I also wanted to add a creative twist to the dish—however, I needed a bit of assistance. Despite years of studying food, I couldn’t remember all the different colors carrots can be. But the Wolfram Language knows about thousands of foods, including carrots. In a couple of lines of code, it was easy to access data including an image of the food and information about interior and exterior colors. I found data about price look-up codes (commonly referred to as PLUs), allowing for a quick confirmation that the gnarled root vegetable in my hand was a carrot and not a parsnip.
Before I ran to the grocery store, I wanted a backup plan in case they didn’t have any non-orange carrots. Fortunately, a simple query in the Wolfram Language can provide carefully curated ingredient substitutions. With some extra code, the ingredients are visualized in a grid, which helps to digest the data more easily. The list is also ordered so the first ingredient (or set of ingredients) listed is considered the best substitution and the second ingredient listed is the second-best substitute. As someone who taught cooking classes for several years, I know that one of the most common meal-killers rears its ugly head when people are missing that one crucial ingredient. But with the help of the Wolfram Language, it’s like having a professional chef with you in the kitchen to suggest substitutions and much more, such as offering proportion calculations, providing nutrition information, giving you descriptions of new ingredients… the list goes on!
(Here’s a downloadable notebook with the code to build the visualizeReplacements function.)
Keep Wolfram in Your Kitchen
Our big family Christmas dinner last year was a tremendous hit. Whenever I cook at my parents’ house I always encounter a few curve balls, so I appreciated the help I got from the Wolfram tech stack. My family wanted me to do it again this year, but I decided to stay in Illinois, eat some takeout Chinese food and watch kung fu movies instead. However, if you have an ambitious holiday cooking project ahead of you, I encourage you to experiment in the kitchen with the Wolfram Language.
A great place to start exploring computational gastronomy is the Wolfram|Alpha Examples page for food and nutrition. There you can see the wide variety of Wolfram|Alpha query fields on cooking, ingredients and other food-related data, as well as food-themed Wolfram|Alpha blog posts. If the Wolfram|Alpha app isn’t on your smartphone, it should be… especially when you’re in the thick of meal prep and could use some data or number crunching! Happy computational cooking!
Appendix: Creating the Lead Image
My dad studied art in college, so growing up, my parents’ house was always full of art made by my dad and friends of his from college. In addition, I always try to take pictures of the dishes I’ve prepared when I cook a nice meal for friends and family. Since this blog is about cooking and family, I thought, “Why not combine these images using my favorite new Wolfram Language function, ImageRestyle?” In one quick line of code, any image can be rendered in the style of another image or list of images. So I simply took a picture of a dish I prepared and blended it with a list containing a painting by my dad’s friend Mike and the same picture of a dish I prepared (the original image is added to keep the colors in the final image brighter) and voilà, I get an image that looks like a painting of food (and without a drop of paint on my clothes).
Here’s another dish I photographed and then restyled using the same technique and art piece:
I got a kick out of this, very fun read.