Food and Nutrition II

Andrew Steinacher
Brandon Kocher
Wolfram Research

Join the Conversation #WolframTechConf

Overview

Review of Food in the Wolfram Language

Food domains

EntityValue features

Applications

New Food data

Packaged Foods

International Foods

Cooking Equipment

Nutrients

Dietary Reference Intakes

Food Properties

Customizable qualifiers

Meal Tracking

Overview

Examples

Visualizations

Review: Food

A "Food" entity is a specific food that has nutritional information from our data sources.

RandomEntity["Food",3]

EntityValue["Food","EntityCount"]

Nutritional Properties

EntityValue["Food","PropertyCount"]

EntityValue["Food","Properties"]//RandomSample[#,5]&

Non-nutritional properties

Attribute properties are used to tag foods for use in lookups:

EntityValue[(Entity["Apples, raw, red delicious, with skin", Entity["Food","ApplesRawRedDeliciousWithSkin::v353h"], "Entity[\"Food\", \"ApplesRawRedDeliciousWithSkin::v353h\"]", "Food"]),{(EntityProperty["food type", EntityProperty["Food","FoodType"], "EntityProperty[\"Food\", \"FoodType\"]"]),(EntityProperty["variety", EntityProperty["Food","Variety"], "EntityProperty[\"Food\", \"Variety\"]"]),(EntityProperty["nutrition label", EntityProperty["Food","NutritionLabel"], "EntityProperty[\"Food\", \"NutritionLabel\"]"])},"PropertyAssociation"]

Review: FoodType

A "FoodType" entity is a more generic food that has no specific nutritional information, but instead represents a class of foods that could come in many variations.

RandomEntity["FoodType",5]

There are significantly fewer FoodTypes than Foods:

EntityValue["FoodType","EntityCount"]

Properties

EntityValue["FoodType","Properties"]

EntityValue[(Entity["longan", Entity["FoodType","Longan"], "Entity[\"FoodType\", \"Longan\"]", "food type"]),{(EntityProperty["basic food groups", EntityProperty["FoodType","BasicFoodGroups"], "EntityProperty[\"FoodType\", \"BasicFoodGroups\"]"]),(EntityProperty["image", EntityProperty["FoodType","Image"], "EntityProperty[\"FoodType\", \"Image\"]"]),(EntityProperty["transparency", EntityProperty["FoodType","Transparency"], "EntityProperty[\"FoodType\", \"Transparency\"]"]),(EntityProperty["food state", EntityProperty["FoodType","FoodState"], "EntityProperty[\"FoodType\", \"FoodState\"]"])},"PropertyAssociation"]

EntityValue[(Entity["banana", Entity["FoodType","Banana"], "Entity[\"FoodType\", \"Banana\"]", "food type"]),{(EntityProperty["basic food groups", EntityProperty["FoodType","BasicFoodGroups"], "EntityProperty[\"FoodType\", \"BasicFoodGroups\"]"]),(EntityProperty["image", EntityProperty["FoodType","Image"], "EntityProperty[\"FoodType\", \"Image\"]"]),(EntityProperty["transparency", EntityProperty["FoodType","Transparency"], "EntityProperty[\"FoodType\", \"Transparency\"]"]),(EntityProperty["food state", EntityProperty["FoodType","FoodState"], "EntityProperty[\"FoodType\", \"FoodState\"]"])},"PropertyAssociation"]

Review: Implicit Entities

An implicit entitiy acts as a single entity that represents all entities of a domain that match the specified property values.

refriedBeans=Entity["Food",{(EntityProperty["preparation", EntityProperty["Food","Preparation"], "EntityProperty[\"Food\", \"Preparation\"]"])->(Entity["refried", Entity["FoodPreparation","Refried"], "Entity[\"FoodPreparation\", \"Refried\"]", "preparation"]),(EntityProperty["food type", EntityProperty["Food","FoodType"], "EntityProperty[\"Food\", \"FoodType\"]"])->(Entity["bean", Entity["FoodType","Bean"], "Entity[\"FoodType\", \"Bean\"]", "food type"])}]

RandomEntity[refriedBeans,4]

Implicit entities behave similar to explicit ones:

EntityValue[refriedBeans,(EntityProperty["relative protein content", EntityProperty["Food","RelativeTotalProteinContent"], "EntityProperty[\"Food\", \"RelativeTotalProteinContent\"]"])]

Use TakeLargest to find the most calorie dense foods:

EntityList[Entity["Food","RelativeTotalCaloriesContent"->TakeLargest[5]]]

Vegetables with white/cream insides, weighted by iron content:

imageCollageData=Rule[QuantityMagnitude[#1],#2]&@@@Select[EntityValue[,{(EntityProperty["relative iron content", EntityProperty["FoodType","RelativeIronContent"], "EntityProperty[\"FoodType\", \"RelativeIronContent\"]"]),(EntityProperty["image", EntityProperty["FoodType","Image"], "EntityProperty[\"FoodType\", \"Image\"]"])}],FreeQ[_Missing]];

ImageCollage[imageCollageData,"Fit",Background->White,ImagePadding->4]

Review: EntityInstance

An EntityInstance allows for the representation of a quantity of food.

someRefriedBeans=EntityInstance[refriedBeans,Quantity[200, "Grams"]]

Some properties depend on the quantity specified:

someRefriedBeans[(EntityProperty["absolute calorie content", EntityProperty["Food","AbsoluteTotalCaloriesContent"], "EntityProperty[\"Food\", \"AbsoluteTotalCaloriesContent\"]"])]

Create a nutrition label:

someRefriedBeans[(EntityProperty["nutrition label", EntityProperty["Food","FoodItemNutritionLabel"], "EntityProperty[\"Food\", \"FoodItemNutritionLabel\"]"])]

Review: EntityGroup

An EntityGroup can be used to represent meals and snacks.

meal=EntityGroup[{
EntityInstance[, (Quantity[300, "g", grams, "Grams"])],
EntityInstance[,(Quantity[3, "oz", ounces, "Ounces"])],
EntityInstance[, (Quantity[150, "g", grams, "Grams"])]
}];

The properties are applied to the group as a whole:

EntityValue[meal,{(EntityProperty["absolute carbohydrate content", EntityProperty["Food","AbsoluteTotalCarbohydratesContent"], "EntityProperty[\"Food\", \"AbsoluteTotalCarbohydratesContent\"]"]),(EntityProperty["absolute calorie content", EntityProperty["Food","AbsoluteTotalCaloriesContent"], "EntityProperty[\"Food\", \"AbsoluteTotalCaloriesContent\"]"]),(EntityProperty["absolute protein content", EntityProperty["Food","AbsoluteTotalProteinContent"], "EntityProperty[\"Food\", \"AbsoluteTotalProteinContent\"]"])},"PropertyAssociation"]

meal[(EntityProperty["nutrition label", EntityProperty["Food","FoodItemNutritionLabel"], "EntityProperty[\"Food\", \"FoodItemNutritionLabel\"]"])]

Review: Apples vs Oranges

Blog post: Comparing apples and oranges in the Wolfram Language

url="http://blog.wolfram.com/2016/06/30/comparing-apples-and-oranges-with-the-wolfram-language/";
Hyperlink[url]

BarcodeImage[url,"QR",300]

Manipulate

(* Important one-time data *)
ClearAll[propToPClass,allGroupedProperties,allRelativeProperties];
foods={
->"Apple",
->"Banana",
->"Beet",
->"Blueberry",
->"Kale",
->"Lemon",
->"Lime",
->"Mango",
->"Orange",
->"Tomato",
->"Watermelon"
};
propToPClass=Join[Association[Flatten[KeyValueMap[Thread[#2->#1]&,AssociationMap[EntityProperties,EntityValue["Food","PropertyClasses"]]]]],<|_->None|>];
allGroupedProperties=Flatten[EntityProperties/@{(EntityPropertyClass["calories", EntityPropertyClass["Food","Calories"], "EntityPropertyClass[\"Food\", \"Calories\"]"]),(EntityPropertyClass["carbohydrates", EntityPropertyClass["Food","Carbohydrates"], "EntityPropertyClass[\"Food\", \"Carbohydrates\"]"]),(EntityPropertyClass["fats", EntityPropertyClass["Food","Fats"], "EntityPropertyClass[\"Food\", \"Fats\"]"]),(EntityPropertyClass["minerals", EntityPropertyClass["Food","Minerals"], "EntityPropertyClass[\"Food\", \"Minerals\"]"]),(EntityPropertyClass["vitamins", EntityPropertyClass["Food","Vitamins"], "EntityPropertyClass[\"Food\", \"Vitamins\"]"])}];
allRelativeProperties=Select[EntityProperties["Food"],(StringMatchQ[Last[#],"Relative"~~___~~"Content"])&];

ClearAll[getData];
getData[food1:Entity["Food",_],food2:Entity["Food",_],iProps:Except[_Rule|_RuleDelayed]:All,opts:OptionsPattern[]]:=Module[{props,data1,data2},

(* Get FoodItem data *)
props=Replace[iProps,{
Full->allRelativeProperties,
All->allGroupedProperties
}];

{data1,data2}=KeyIntersection[DeleteMissing/@EntityValue[{food1,food2},props,"PropertyAssociation"]];

];
allIndividualData=DeleteMissing/@EntityValue[foods[[All,1]],allGroupedProperties,"EntityPropertyAssociation"];

allFoodTypeData0=EntityValue[First[Cases[#,Entity["FoodType",___],Infinity,1]]&/@foods[[All,1]],{(EntityProperty["name", EntityProperty["FoodType","Name"], "EntityProperty[\"FoodType\", \"Name\"]"]),(EntityProperty[(RowDefault["image", " | ", (RowWithSeparators[;, ";", "Small"])]), EntityProperty["FoodType","Image",{"Size"->"Small"}], "EntityProperty[\"FoodType\", \"Image\", {\"Size\" -> \"Small\"}]"]),(EntityProperty["typical outside colors", EntityProperty["FoodType","TypicalOutsideColors"], "EntityProperty[\"FoodType\", \"TypicalOutsideColors\"]"])},"EntityPropertyAssociation"];
allFoodTypeData=MapAt[Last,allFoodTypeData0,{All,Key@(EntityProperty["typical outside colors", EntityProperty["FoodType","TypicalOutsideColors"], "EntityProperty[\"FoodType\", \"TypicalOutsideColors\"]"])}];

ClearAll[differencePlot];
differencePlot[food1_,food1_,opts:OptionsPattern[]]="Please select different foods";
differencePlot[food1_,food2_,opts:OptionsPattern[]]:=Module[{relativeDiffs,props,trulyDifferentData0,trulyDifferentData,tooltippedData,positiveData,negativeData,plot,foodType1,foodType2,differenceGrid,foodTypeInformation,foodTypeImages,data1,data2},

{data1,data2}=KeyIntersection[Values[allIndividualData[[{Key[food1],Key[food2]}]]]];
{foodType1,foodType2}=First[Cases[#,Entity["FoodType",_],Infinity,1]]&/@{food1,food2};


relativeDiffs=Quiet[((data1-data2)/((Merge[{data1,data2},Mean])))*100.,{Power::infy,Infinity::indet}]/.{Indeterminate->0.};

(* Set up a difference threshold *)
trulyDifferentData0=Select[relativeDiffs,(Abs[QuantityMagnitude[#]]>10)&]//Sort//Reverse;

(* Remove 200% difference items? These are the zero vs non-zero properties *)
(*trulyDifferentData0=Select[trulyDifferentData0,(Abs[QuantityMagnitude[#]]<200)&];*)


trulyDifferentData=KeyValueMap[{Quantity[Round[#2,0.1],"Percent"],#1,propToPClass[#1]}&,trulyDifferentData0]/._Missing->"other";
trulyDifferentData=MapIndexed[(Join[#2,#1])&,trulyDifferentData];

trulyDifferentData={Tooltip[{#1,#2},#3],#4}&@@@trulyDifferentData;

trulyDifferentData=GroupBy[trulyDifferentData,Last->First];

(* Inject all of the classes so that they have the same PlotMarkers no matter what *)
trulyDifferentData=Merge[
{
trulyDifferentData,
\[LeftAssociation](EntityPropertyClass["calories", EntityPropertyClass["Food","Calories"], "EntityPropertyClass[\"Food\", \"Calories\"]"])->{-1,0},(EntityPropertyClass["carbohydrates", EntityPropertyClass["Food","Carbohydrates"], "EntityPropertyClass[\"Food\", \"Carbohydrates\"]"])->{-1,0},(EntityPropertyClass["fats", EntityPropertyClass["Food","Fats"], "EntityPropertyClass[\"Food\", \"Fats\"]"])->{-1,0},(EntityPropertyClass["minerals", EntityPropertyClass["Food","Minerals"], "EntityPropertyClass[\"Food\", \"Minerals\"]"])->{-1,0},(EntityPropertyClass["vitamins", EntityPropertyClass["Food","Vitamins"], "EntityPropertyClass[\"Food\", \"Vitamins\"]"])->{-1,0}\[RightAssociation]
},Flatten];
trulyDifferentData=KeySortBy[trulyDifferentData,StringLength[Last[#]]&];

plot=ListPlot[
trulyDifferentData,
opts,

PlotLegends->Placed[Automatic,{Left,Bottom}],
FillingStyle->Reverse[{
allFoodTypeData[foodType1,(EntityProperty["typical outside colors", EntityProperty["FoodType","TypicalOutsideColors"], "EntityProperty[\"FoodType\", \"TypicalOutsideColors\"]"])],
allFoodTypeData[foodType2,(EntityProperty["typical outside colors", EntityProperty["FoodType","TypicalOutsideColors"], "EntityProperty[\"FoodType\", \"TypicalOutsideColors\"]"])]
}/.{Entity["ColorSet",color_String]:>Symbol[color]}],
PlotTheme->{"Detailed","OpenMarkersThick","LargeLabels"},
Axes->True,
Filling->Axis,
FrameLabel->{"","Relative difference (%)"},
FrameTicks->{None,Automatic},
Joined->False,

(* Force the placeholders off screen *)
PlotRange->{{0,All},{-210,210}},

(*AspectRatio->Full,*)
ImageSize->{576,362},
(*ImageSize->Full,*)

Prolog->{

Inset[allFoodTypeData[foodType1,(EntityProperty[(RowDefault["image", " | ", (RowWithSeparators[;, ";", "Small"])]), EntityProperty["FoodType","Image",{"Size"->"Small"}], "EntityProperty[\"FoodType\", \"Image\", {\"Size\" -> \"Small\"}]"])],{Center,Top},Scaled[{0.3,1.33}],{100,100}],
Inset[allFoodTypeData[foodType2,(EntityProperty[(RowDefault["image", " | ", (RowWithSeparators[;, ";", "Small"])]), EntityProperty["FoodType","Image",{"Size"->"Small"}], "EntityProperty[\"FoodType\", \"Image\", {\"Size\" -> \"Small\"}]"])],{Center,Bottom},Scaled[{0.3,-0.33}],{100,100}]

},

PlotLabel->(Capitalize[allFoodTypeData[foodType1,(EntityProperty["name", EntityProperty["FoodType","Name"], "EntityProperty[\"FoodType\", \"Name\"]"])]]<>" vs. "<>Capitalize[allFoodTypeData[foodType2,(EntityProperty["name", EntityProperty["FoodType","Name"], "EntityProperty[\"FoodType\", \"Name\"]"])]])

];

(*plot*)

(* Add top difference properties on each end in a Grid *)
trulyDifferentData0=Quantity[Round[#,1],"Percent"]&/@trulyDifferentData0;

Block[{spec=UpTo[5]},
differenceGrid=Grid[
List@@@Join[
Normal[TakeLargest[trulyDifferentData0,spec]],
Normal[Reverse@TakeSmallest[trulyDifferentData0,spec]]
]/.{q_Quantity:>TraditionalForm[q]},

Frame->True,
Dividers->{{1->True,-1->True},{6->True}}
];
];


Row[{plot," ",differenceGrid}]

];

(*
comparisonManipulate=Manipulate[

Quiet[differencePlot[food1,food2]],

{{food1,foods[[1,1]],""},foods,ControlType->SetterBar,Appearance->"Horizontal"},
{{food2,foods[[10,1]],""},foods,ControlType->SetterBar,Appearance->"Horizontal"},
SynchronousUpdating->False,
ControlPlacement->{Top,Top},
SaveDefinitions->True,

Initialization:>(
foods=foods;
allIndividualData=allIndividualData;
allFoodTypeData=allFoodTypeData;
),
ContentSize->Large

];

comparisonManipulate
*)

differencePlot[,]

New Food Data: Packaged Foods

Marketing Descriptions

Examples

EntityValue[{(Entity["Minute Maid 100% Apple Juice", Entity["Food","MinuteMaid100AppleJuice::5djp3"], "Entity[\"Food\", \"MinuteMaid100AppleJuice::5djp3\"]", "food"]),(Entity["Tyson Chicken Nuggets", Entity["Food","TysonChickenNuggets::s599v"], "Entity[\"Food\", \"TysonChickenNuggets::s599v\"]", "food"]),(Entity["Mission Wraps Zesty Garlic Herb", Entity["Food","MissionWrapsZestyGarlicHerb::y5xpp"], "Entity[\"Food\", \"MissionWrapsZestyGarlicHerb::y5xpp\"]", "food"])},(EntityProperty["marketing description", EntityProperty["Food","MarketingDescription"], "EntityProperty[\"Food\", \"MarketingDescription\"]"]),"EntityAssociation"];
Grid[List@@@Normal[%],Frame->All]

n-grams

GraphicsRow[{,,},ImageSize->Full]

New Food Data: Packaged Foods

FoodTypes from descriptions

“pumpkin”, “pumpkin spice”

GraphicsRow[{foodTypesFromInput["pumpkin",{Entity["FoodType","Pumpkin"],Entity["FoodType","Spice"]}],foodTypesFromInput["pumpkin spice",{Entity["FoodType","Pumpkin"],Entity["FoodType","Spice"]}]},ImageSize->Large]

“organic”, “natural”

“halloween”, “easter”

New Food Data: International Foods

Overview

We’ve added about 25,000 international foods from more than 20 countries:

EntityValue[,"EntityCount"]

Use GeoRegionValuePlot to look at foods for European countries:

countries=Complement[Union@@EntityValue["FoodDataSource","Countries"],{}];
countryCounts=AssociationMap[EntityValue[Entity["Food","DataSourceCountries"->#],"EntityCount"]&,countries];
GeoRegionValuePlot[KeyDrop[countryCounts,{(Entity["United States", Entity["Country","UnitedStates"], "Entity[\"Country\", \"UnitedStates\"]", "country"]),(Entity["Canada", Entity["Country","Canada"], "Entity[\"Country\", \"Canada\"]", "country"])}],GeoLabels->(Tooltip[#1,Row[{#2,": ",#4, " foods"},Frame->All]]&)]

New Food Data: International Foods

Nutritional information

labels=AssociationMap[EntityValue[EntityInstance[#,(Quantity[100, "g", grams, "Grams"])],(EntityProperty[(RowDefault["nutrition label", " | ", (RowWithSeparators[;, ";", "Large"])]), EntityProperty["Food","FoodItemNutritionLabel",{"ImageSize"->"Large"}], "EntityProperty[\"Food\", \"FoodItemNutritionLabel\", {\"ImageSize\" -> \"Large\"}]"])]&,{(Entity["Foie gras, duck, whole, canned", Entity["Food","FoieGrasDuckWholeCanned::vt9hb"], "Entity[\"Food\", \"FoieGrasDuckWholeCanned::vt9hb\"]", "food"]),(Entity["Soprassata (pork meat, salted, raw)", Entity["Food","SoprassataPorkMeatSaltedRaw::37wb7"], "Entity[\"Food\", \"SoprassataPorkMeatSaltedRaw::37wb7\"]", "food"]),(Entity["Souvlaki (pitta bread, pork roasted, tomato)", Entity["Food","SouvlakiPittaBreadPorkRoastedTomato::mck4c"], "Entity[\"Food\", \"SouvlakiPittaBreadPorkRoastedTomato::mck4c\"]", "food"])}];
Grid[Transpose[List@@@Normal[labels]],Frame->All]

New Food Data: International Foods

Multiple Languages

We have orignal language names and English translations:

EntityValue[(Entity["Giouvarlakia soup (minced meat and rice balls)", Entity["Food","GiouvarlakiaSoupMincedMeatAndRiceBalls::ns3m9"], "Entity[\"Food\", \"GiouvarlakiaSoupMincedMeatAndRiceBalls::ns3m9\"]", "food"]),(EntityProperty[(RowDefault["name", " | ", (RowWithSeparators[;, ";", (Entity["Greek", Entity["Language","Greek"], "Entity[\"Language\", \"Greek\"]", "language"])])]), EntityProperty["Food","Name","Language"->Entity["Language","Greek"]], "EntityProperty[\"Food\", \"Name\", \"Language\" -> Entity[\"Language\", \"Greek\"]]"])]

Here are a few example translations:

foodsByLanguage=<|
(Entity["French", Entity["Language","French"], "Entity[\"Language\", \"French\"]", "language"])->{(Entity["Foie gras, duck, whole, canned", Entity["Food","FoieGrasDuckWholeCanned::vt9hb"], "Entity[\"Food\", \"FoieGrasDuckWholeCanned::vt9hb\"]", "food"]),(Entity["Croissant, ordinary, from bakery", Entity["Food","CroissantOrdinaryFromBakery::35jr6"], "Entity[\"Food\", \"CroissantOrdinaryFromBakery::35jr6\"]", "food"]),(Entity["Belgium endive (chicory, witloof), raw", Entity["Food","BelgiumEndiveChicoryWitloofRaw::2h733"], "Entity[\"Food\", \"BelgiumEndiveChicoryWitloofRaw::2h733\"]", "food"]),(Entity["Sauce, hollandaise", Entity["Food","SauceHollandaise::5t8m5"], "Entity[\"Food\", \"SauceHollandaise::5t8m5\"]", "food"])},
(Entity["Greek", Entity["Language","Greek"], "Entity[\"Language\", \"Greek\"]", "language"])->{(Entity["Baklava", Entity["Food","Baklava::5y372"], "Entity[\"Food\", \"Baklava::5y372\"]", "food"]),(Entity["Peinirli (sausage and cheese)", Entity["Food","PeinirliSausageAndCheese::58gw9"], "Entity[\"Food\", \"PeinirliSausageAndCheese::58gw9\"]", "food"]),(Entity["Vegetables stuffed with rice (tomatoes, peppers, zucchini)", Entity["Food","VegetablesStuffedWithRiceTomatoesPeppersZucchini::547b2"], "Entity[\"Food\", \"VegetablesStuffedWithRiceTomatoesPeppersZucchini::547b2\"]", "food"]),(Entity["Mousaka", Entity["Food","Mousaka::chg2t"], "Entity[\"Food\", \"Mousaka::chg2t\"]", "food"])},
(Entity["Italian", Entity["Language","Italian"], "Entity[\"Language\", \"Italian\"]", "language"])->{(Entity["Brioches", Entity["Food","Brioches::q2375"], "Entity[\"Food\", \"Brioches::q2375\"]", "food"]),(Entity["Garlic, fresh", Entity["Food","GarlicFresh::nkz3w"], "Entity[\"Food\", \"GarlicFresh::nkz3w\"]", "food"]),(Entity["Tomato puree' (dry matter 30 percent)", Entity["Food","TomatoPureeDryMatter30Percent::n29ys"], "Entity[\"Food\", \"TomatoPureeDryMatter30Percent::n29ys\"]", "food"]),(Entity["Marzipan", Entity["Food","Marzipan::3pd4y"], "Entity[\"Food\", \"Marzipan::3pd4y\"]", "food"])},
(Entity["Norwegian", Entity["Language","Norwegian"], "Entity[\"Language\", \"Norwegian\"]", "language"])->{(Entity["Salmon, smoked", Entity["Food","SalmonSmoked::s6gxv"], "Entity[\"Food\", \"SalmonSmoked::s6gxv\"]", "food"]),(Entity["Fish, dried, alkaline cured, boiled", Entity["Food","FishDriedAlkalineCuredBoiled::g8wqn"], "Entity[\"Food\", \"FishDriedAlkalineCuredBoiled::g8wqn\"]", "food"]),(Entity["Bread, wholemeal flour, bake-off, ready to eat", Entity["Food","BreadWholemealFlourBakeOffReadyToEat::hvg36"], "Entity[\"Food\", \"BreadWholemealFlourBakeOffReadyToEat::hvg36\"]", "food"]),(Entity["Cheese, hard, Jarlsberg", Entity["Food","CheeseHardJarlsberg::v9524"], "Entity[\"Food\", \"CheeseHardJarlsberg::v9524\"]", "food"])}
|>;
Dataset[Association[KeyValueMap[Rule[#1,EntityValue[#2,EntityProperty["Food", "Name", {"Language" -> #1}],"EntityAssociation"]]&,foodsByLanguage]]]//Rasterize

New Food Data: International Foods

LanguaL alignment

LanguaL is an international food thesaurus, with nearly 7000 detailed terms for cooking methods, food sources, product types and more:

Dataset[(Entity["BOILED", Entity["LanguaLTerm","G0014"], "Entity[\"LanguaLTerm\", \"G0014\"]", "LanguaL term"])[{(EntityProperty["descriptor", EntityProperty["LanguaLTerm","Descriptor"], "EntityProperty[\"LanguaLTerm\", \"Descriptor\"]"]),(EntityProperty[(RowDefault["descriptor", " | ", (RowWithSeparators[;, ";", (Entity["French", Entity["Language","French"], "Entity[\"Language\", \"French\"]", "language"])])]), EntityProperty["LanguaLTerm","Descriptor",{"Language"->Entity["Language","French"]}], "EntityProperty[\"LanguaLTerm\", \"Descriptor\", {\"Language\" -> Entity[\"Language\", \"French\"]}]"]),(EntityProperty["description", EntityProperty["LanguaLTerm","Description"], "EntityProperty[\"LanguaLTerm\", \"Description\"]"]),(EntityProperty[(RowDefault["description", " | ", (RowWithSeparators[;, ";", (Entity["German", Entity["Language","German"], "Entity[\"Language\", \"German\"]", "language"])])]), EntityProperty["LanguaLTerm","Description",{"Language"->Entity["Language","German"]}], "EntityProperty[\"LanguaLTerm\", \"Description\", {\"Language\" -> Entity[\"Language\", \"German\"]}]"]),(EntityProperty["languages", EntityProperty["LanguaLTerm","Languages"], "EntityProperty[\"LanguaLTerm\", \"Languages\"]"])},"PropertyAssociation"]]

Rasterize[%]

Foods are tagged with LanguaL terms by compilers:

(Entity["Giouvarlakia soup (minced meat and rice balls)", Entity["Food","GiouvarlakiaSoupMincedMeatAndRiceBalls::ns3m9"], "Entity[\"Food\", \"GiouvarlakiaSoupMincedMeatAndRiceBalls::ns3m9\"]", "food"])[(EntityProperty["LanguaL terms", EntityProperty["Food","LanguaLTerms"], "EntityProperty[\"Food\", \"LanguaLTerms\"]"])]

New Food Data: International Foods

Implicit Entities: Countries with the most cheese

Implicit entities allow for easy lookups like this:

cheeseCountryData=EntityValue[,(EntityProperty["data source countries", EntityProperty["Food","DataSourceCountries"], "EntityProperty[\"Food\", \"DataSourceCountries\"]"])];
With[{cheeseCounts=Counts[Flatten[cheeseCountryData]]},
GeoRegionValuePlot[cheeseCounts,GeoLabels->(Tooltip[#1,Row[{#2,": ",#4, " cheeses"},Frame->All]]&)]
]

New Food Data: International Foods

Nutrient comparisons by country

Calories

Row[Rasterize/@{(Legended[, (LineLegend["Canada", "Ireland", "Finland", "Lithuania", "France", "United Kingdom", "Sweden", "Poland", "Portugal", "Italy", "Netherlands", "Denmark", "Norway", "Greece", "Slovakia"])]),(Legended[, (BarLegend)])}]

Protein

Row[Rasterize/@{(Legended[, (LineLegend["Ireland", "Finland", "Greece", "Poland", "Canada", "Netherlands", "Lithuania", "Sweden", "France", "Switzerland", "Latvia", "Slovakia", "Iceland", "Norway", "Serbia"])]),(Legended[, (BarLegend)])}]

New Food Data: Cooking Equipment

We’ve curated over 2700 cooking equipment items:

EntityValue["CookingEquipment","EntityCount"]

Implicit entity lookups can be done on physical properties:

EntityValue[,(EntityProperty["typical blade length", EntityProperty["CookingEquipment","TypicalBladeLength"], "EntityProperty[\"CookingEquipment\", \"TypicalBladeLength\"]"]),"EntityAssociation"]

knifeLengthData=EntityValue[Entity["CookingEquipment", {"TypicalBladeLength" -> GreaterThan[Quantity[0.1, "Inches"]], "CookingEquipmentType" -> "Knife"}],"TypicalBladeLength","EntityAssociation"];

ListPlot[Reverse[KeyValueMap[Tooltip[#2,#1]&,Sort[knifeLengthData]]],ImageSize->Large,PlotTheme->"Detailed",FrameLabel->{"Rank","Blade Length (in)"},PlotLabel->"Typical Knife Blade Length"]

New Food Data: Cooking Equipment

ImageCollage, WordCloud

Row[{
Manipulate[
ImageCollage[EntityValue[EntityClass["CookingEquipment", (EntityProperty["cooking equipment type", EntityProperty["CookingEquipment","CookingEquipmentType"], "EntityProperty[\"CookingEquipment\", \"CookingEquipmentType\"]"])->type],(EntityProperty[(RowDefault["image", " | ", (RowWithSeparators[;, ";", "Small"])]), EntityProperty["CookingEquipment","Image","Size"->"Small"], "EntityProperty[\"CookingEquipment\", \"Image\", \"Size\" -> \"Small\"]"])],Background->White],
{{type,(Entity["whisk", Entity["CookingEquipmentType","Whisk"], "Entity[\"CookingEquipmentType\", \"Whisk\"]", "cooking equipment type"]),"Equipment Type:"},Entity["CookingEquipmentType",#]&/@{"Whisk","Tongs","Peeler","Spatula"}}],

With[{data=EntityValue[,{(EntityProperty["name", EntityProperty["CookingEquipment","Name"], "EntityProperty[\"CookingEquipment\", \"Name\"]"]),(EntityProperty["image", EntityProperty["CookingEquipment","Image"], "EntityProperty[\"CookingEquipment\", \"Image\"]"]),(EntityProperty["typical blade length", EntityProperty["CookingEquipment","TypicalBladeLength"], "EntityProperty[\"CookingEquipment\", \"TypicalBladeLength\"]"])}]},
WordCloud[{If[MatchQ[#2,Except[_Missing]],Tooltip[#1,#2],#1],#3}&@@@data,ImageSize->Large]
]
},ImageSize->All]

Nutrients: Dietary Reference Intakes

There are properties for five Dietary Reference Intake types:

EntityValue[{(Entity["zinc", Entity["Nutrient","Zinc"], "Entity[\"Nutrient\", \"Zinc\"]", "nutrient"]),(Entity["vitamin C", Entity["Nutrient","VitaminC"], "Entity[\"Nutrient\", \"VitaminC\"]", "nutrient"]),(Entity["magnesium", Entity["Nutrient","Magnesium"], "Entity[\"Nutrient\", \"Magnesium\"]", "nutrient"])},{"EstimatedAverageRequirement","RecommendedDietaryAllowance","AdequateIntake","DailyValue","TolerableUpperIntakeLevel"},"Dataset"]//Rasterize

They can be visualized with built-in properties for values by age:

ClearAll[binnedByAge];
binnedByAge[nutrient_:Entity["Nutrient",_String],opts___]:=Module[{functions,props},
props={(EntityProperty["tolerable upper intake level binned by age", EntityProperty["Nutrient","TolerableUpperIntakeLevelBinned"], "EntityProperty[\"Nutrient\", \"TolerableUpperIntakeLevelBinned\"]"]),(EntityProperty["recommended daily value binned by age", EntityProperty["Nutrient","DailyValueBinned"], "EntityProperty[\"Nutrient\", \"DailyValueBinned\"]"]),(EntityProperty["recommended dietary allowance binned by age", EntityProperty["Nutrient","RecommendedDietaryAllowanceBinned"], "EntityProperty[\"Nutrient\", \"RecommendedDietaryAllowanceBinned\"]"]),(EntityProperty["estimated average requirement binned by age", EntityProperty["Nutrient","EstimatedAverageRequirementBinned"], "EntityProperty[\"Nutrient\", \"EstimatedAverageRequirementBinned\"]"]),(EntityProperty["adequate intake binned by age", EntityProperty["Nutrient","AdequateIntakeBinned"], "EntityProperty[\"Nutrient\", \"AdequateIntakeBinned\"]"])};
props=props/.EntityProperty[type_,prop_]:>EntityProperty[type,prop,{opts}];
functions=EntityValue[nutrient,props];

ListPlot[
Transpose[Table[
List@@@Thread[age->Through[functions[Quantity[age,"Years"]]]],
{age,Join[Range[0,2,1/12.],Range[3,100]]}
]],
Joined->True,
PlotRange->{{0,100},{0,Automatic}},
PlotRangePadding->Scaled[0.02],
Frame->True,
FrameLabel->{"Age (yr)","g/day"},
PlotLegends->StringTrim[StringReplace[props[[All,2]],{"Binned"->"",lc:LetterCharacter?UpperCaseQ:>(" "<>ToLowerCase[lc])}]],
AspectRatio->5/10,
ImageSize->Large,
PlotTheme->"Detailed",
PlotStyle->{Darker[Red,0.1],Orange,Darker[Green,0.5],Lighter[Blue,0.3],Black},
PlotLabel->"Dietary Reference Intakes for "<>Capitalize[CommonName[nutrient]]
]

];

binnedByAge[Entity["Nutrient", "Zinc"]]

Nutrients: Food Properties

Daily Value Percent

Daily Value percent properties allow for monitoring daily consumption:

EntityInstance[,(Quantity[50, "g", grams, "Grams"])][(EntityProperty["vitamin C daily value percent", EntityProperty["Food","VitaminCDailyValuePercent"], "EntityProperty[\"Food\", \"VitaminCDailyValuePercent\"]"])]

CompassPlot

This kind of plot can be useful to visualize a food’s overall nutritional composition:

EntityInstance[,(Quantity[150, "g", grams, "Grams"])][(EntityProperty["compass plot", EntityProperty["Food","CompassPlot"], "EntityProperty[\"Food\", \"CompassPlot\"]"])]

Nutrients: Customization

Pregnancy, Young children

CompassPlot and other properties can be customized by age, gender, and pregnancy stage:

GraphicsRow[{

EntityValue[EntityGroup[{EntityInstance[,(Quantity[50.`, "g", grams, "Grams"])],EntityInstance[,(Quantity[100.`, "g", grams, "Grams"])],EntityInstance[,(Quantity[50.`, "g", grams, "Grams"])],EntityInstance[,(Quantity[100.`, "g", grams, "Grams"])],EntityInstance[,(Quantity[100.`, "g", grams, "Grams"])]}],
(EntityProperty[(RowDefault["compass plot", " | ", (RowWithSeparators[;, ";", (Entity["female", Entity["Gender","Female"], "Entity[\"Gender\", \"Female\"]", "gender"]), (Entity["pregnant", Entity["PregnancyStage","Pregnant"], "Entity[\"PregnancyStage\", \"Pregnant\"]", "pregnancy stage"]), {"TotalProtein","Iron","Magnesium","Sodium","Zinc","FoodFolate"}])]), EntityProperty["Food","CompassPlot",{"Gender"->Entity["Gender","Female"],"PregnancyStage"->Entity["PregnancyStage","Pregnant"],"Nutrients"->{"TotalProtein","Iron","Magnesium","Sodium","Zinc","FoodFolate"}}], "EntityProperty[\"Food\", \"CompassPlot\", {\"Gender\" -> Entity[\"Gender\", \"Female\"], \"PregnancyStage\" -> Entity[\"PregnancyStage\", \"Pregnant\"], \"Nutrients\" -> {\"TotalProtein\", \"Iron\", \"Magnesium\", \"Sodium\", \"Zinc\", \"FoodFolate\"}}]"])],

EntityValue[
EntityGroup[{EntityInstance[,Quantity[250,"Grams"]]}],
(EntityProperty[(RowDefault["compass plot", " | ", (RowWithSeparators[;, ";", (Quantity[5, "mo", months, "Months"]), {"TotalCarbohydrates","FoodFolate","VitaminC","Calcium","Potassium"}, (Entity["adequate intake", Entity["DietaryReferenceIntakeType","AdequateIntake"], "Entity[\"DietaryReferenceIntakeType\", \"AdequateIntake\"]", "dietary reference intake type"])])]), EntityProperty["Food","CompassPlot",{"Age"->(Quantity[5, "mo", months, "Months"]),"Nutrients"->{"TotalCarbohydrates","FoodFolate","VitaminC","Calcium","Potassium"},"DietaryReferenceIntakeType"->Entity["DietaryReferenceIntakeType","AdequateIntake"]}], "EntityProperty[\"Food\", \"CompassPlot\", {\"Age\" -> Quantity[5, \"Months\"], \"Nutrients\" -> {\"TotalCarbohydrates\", \"FoodFolate\", \"VitaminC\", \"Calcium\", \"Potassium\"}, \"DietaryReferenceIntakeType\" -> Entity[\"DietaryReferenceIntakeType\", \"AdequateIntake\"]}]"])
]
},ImageSize->Large]

DietaryReferenceIntakeTypes

You can also use different DRI types:

driCompassPlots=AssociationMap[
(EntityValue[
EntityGroup[{EntityInstance[Entity["Food",{"FoodType"->ContainsExactly[{"Kale"}],"AddedFoodTypes"->ContainsExactly[{}]}],Quantity[250.`,"Grams"]]}],EntityProperty["Food","CompassPlot",{
"DietaryReferenceIntakeType"->#,
"Nutrients"->{"Iron","Calcium","TotalCalories","Phosphorus","Copper","VitaminC"}
}]])&,
Entity["DietaryReferenceIntakeType",#]&/@{"EstimatedAverageRequirement","RecommendedDietaryAllowance","DailyValue","TolerableUpperIntakeLevel"}
];
Grid[Transpose[List@@@Normal[driCompassPlots]],Frame->All]

Meal Tracking

Set up

Create a databin

Upon creating a new databin, you get an email sent to you with all of this information, in case you need it in the future.
You can also access/manage all of your databins at datadrop.wolframcloud.com/admin.

bin=CreateDatabin["Name"->"Meal tracking for Andrew Steinacher"]

You can always select a bin using it’s short ID:

bin=Databin["fTbEpJuk"]

Create webforms

cloudPath = "MealTracking/Andrew_Steinacher";

(* Create a function to upload the data received from the form *)
ClearAll[logMealData];
logMealData[data_]:=Module[{processedData},

(* Overwrite the timestamps so they are stored according to the "Date" field in the form *)
processedData=Append[#,<|"Timestamp"->#Date+Quantity[1,"Second"]|>]&/@data;

(* Use DatabinUpload to add multiple entries at once *)
DatabinUpload[bin,processedData];
];

(* Create a form specification to gather the necessary data *)
mealTrackingFormSpec="Foods"->RepeatingElement[
CompoundElement[
<|
"Amount"-><|"Interpreter"->"ComputedQuantity"|"Number","Required"->False,"Hint"->"1 serving"|>,

(* String also allows failure cases *)
"Food"-><|"Interpreter"->"Food"|"String","Hint":>RandomChoice[{"apple","bread","milk","eggs"}]|>,

(* Choose which meal it goes to, making the default choice based on time of day *)
"Meal"-><|
"Interpreter"->{"Breakfast","Lunch","Dinner","Snack","Beverage"},

"Default":>Switch[DateValue["Hour"],
_?(Between[{4,10}]),"Breakfast",
_?(Between[{11,14}]),"Lunch",
_?(Between[{16,20}]),"Dinner",
_,"Snack"
]
|>,

(* Allow entering data for previous days *)
"Date"-><|"Interpreter"->"ComputedDate","Input"->"today"|>
|>
]
];

(* Create a page with a redirect hyperlink back to the form *)
ClearAll[resultPage];
resultPage[redirectPath_String]:=Module[{},
Column[{
"Data logged successfully!",
Hyperlink["Click here to log more foods",FileNameJoin[{$CloudRootDirectory,redirectPath}]]
}]
];

(* Package the form together in FormFunction *)
mealTrackingForm=FormFunction[mealTrackingFormSpec,(logMealData[#Foods];resultPage[cloudPath])&,"HTMLThemed",
AppearanceRules-><|
"Title"->bin["Name"],
"Description":>Column[{
"Hello! Use this form to track your meals.",
("You last logged "<>Replace[ToLowerCase[#Meal],(m:"snack"|"beverage"):>("a "<>m)]<>" "<>Replace[#Date,{Today->"today",Yesterday->"yesterday",d_:>("on "<>DateString[d,"Date"])}]<>".")&@bin["Latest"][["Data",{"Meal","Date"}]]
}
]
|>
];

cloudObject=CloudDeploy[mealTrackingForm,cloudPath,Permissions->"Public"]

The URL given can always be used to access the form on a computer, but it is much easier if we can use it elsewhere too.

You can make the form easily accessible with built-in tools, like URLShorten:

URLShorten[cloudObject]

You can even make them easy to read for mobile phones and tablets using BarcodeImage to generate QR codes that can easily be read by most QR/barcode reading applications:

BarcodeImage[First[cloudObject],"QR"]

barcodeCloudPath=cloudPath<>"_Barcode";
barcodeForm=FormFunction[
{
"Amount"-><|"Interpreter"->"ComputedQuantity"|"Number","Required"->False,"Hint"->"1 serving"|>,
"Meal"-><|
"Interpreter"->{"Breakfast","Lunch","Dinner","Snack","Beverage"},

"Default":>Switch[DateValue["Hour"],
_?(Between[{4,10}]),"Breakfast",
_?(Between[{11,14}]),"Lunch",
_?(Between[{16,20}]),"Dinner",
_,"Snack"
]
|>,
"Date"-><|"Interpreter"->"ComputedDate","Input"->"today"|>,
{"UPC","Barcode image"}->AutoSubmitting[Restricted["Barcode","UPC_A"]]
},

With[{temp=Append[#,"Food"->Replace[#UPC,upc_String:>Entity["Food",EntityProperty["Food","UniversalProductCodes"]->upc]]]},
logMealData[{temp}];
resultPage[barcodeCloudPath]
]&,
AppearanceRules-><|
"Title"->bin["Name"],
"Description":>Column[{
"Hello! Use this form to track your meals.",
("You last logged "<>Replace[ToLowerCase[#Meal],(m:"snack"|"beverage"):>("a "<>m)]<>" "<>Replace[#Date,{Today->"today",Yesterday->"yesterday",d_:>("on "<>DateString[d,"Date"])}]<>".")&@bin["Latest"][["Data",{"Meal","Date"}]]
}
]
|>
];

barcodeCloudObject=CloudDeploy[barcodeForm,barcodeCloudPath,Permissions->"Public"]

BarcodeImage[barcodeCloudObject[[1]],"QR"]

Examples

GraphicsRow[{,},ImageSize->Full]

Visualizations

Code

ClearAll[getData];
getData[date_DateObject]:=Module[{data},

(* Get data for a single day *)
data=Normal[Databin["fTbEpJuk",{date,DatePlus[date,1]}]];

(* Get data that's ready to go *)
data=Select[data,MatchQ[#Food,Entity["Food",_]]&];

(* Gather all of the data for the day *)
data=EntityGroup[EntityInstance[#Food,Replace[#Amount,_?NumberQ:>Quantity[1,"Servings"]]]&/@data];

data

];

lastWeek=With[{first=Today},DateRange[DatePlus[first, -7],first]];

dataByDate=AssociationMap[getData,lastWeek];

myNutrients={(EntityProperty["vitamin C daily value percent", EntityProperty["Food","VitaminCDailyValuePercent"], "EntityProperty[\"Food\", \"VitaminCDailyValuePercent\"]"]),(EntityProperty["sodium daily value percent", EntityProperty["Food","SodiumDailyValuePercent"], "EntityProperty[\"Food\", \"SodiumDailyValuePercent\"]"]),(EntityProperty["potassium daily value percent", EntityProperty["Food","PotassiumDailyValuePercent"], "EntityProperty[\"Food\", \"PotassiumDailyValuePercent\"]"]),(EntityProperty["iron daily value percent", EntityProperty["Food","IronDailyValuePercent"], "EntityProperty[\"Food\", \"IronDailyValuePercent\"]"])};

nutrientPercents=EntityValue[#,myNutrients,"PropertyAssociation"]&/@dataByDate;

ClearAll[gauge];
gauge[x_,color_]:=VerticalGauge[{100,x},{0,200},PlotTheme->"Minimal",GaugeStyle->{Opacity[0.35,Blue],color},Background->Black];

ClearAll[myGaugeCluster];
myGaugeCluster[percents_Association]:=With[{data=MapThread[Tooltip[gauge[#1,#2],#3]&,{Values[percents][[All,1]],{Orange,Red,Yellow,White},Keys[percents]}]},
GraphicsRow[data]
];

Result

Grid[Transpose[List@@@Normal[myGaugeCluster/@nutrientPercents]],Frame->All,Alignment->Center,Spacings->None,ItemSize->Full,Background->Black]

Review

Review of Food in the Wolfram Language

Food domains

EntityValue features

Applications

New Food data

Packaged Foods

International Foods

Cooking Equipment

Nutrients

Dietary Reference Intakes

Food Properties

Customizable qualifiers

Meal Tracking

Overview

Examples

Visualizations