Building a recommendation system for restaurant dishes with R Shiny
We prototyped a personalized menu recommendation system that suggests dishes you might like when visiting a new restaurant.
In our previous post, we collected over a thousand responses on what menu items people like to order at different restaurants. Using these preferences, we built a prototype food recommendation engine that would be the backbone of our final food recommendation app. Jump to see our fully functioning demo here at https://tastespace.shinyapps.io/tastespace/ or feel free to read through the post to learn about how we built it.
If you would like to know more about what we are trying to build, read our first post here.
The data
In the past week, over 1,000 people participated in our survey and indicated over 30,708 food preferences. By collecting information about what people would like to eat at 8 different restaurants, we are able to build a recommendation app that suggests what you might like to eat at different dining locations.
The 8 different cuisines and restaurants we covered included: an American diner, family-style, Chinese, Thai, Indian, Japanese & Korean, and two pub-style restaurants. Combined we had over 700 menu items that people were able to choose from.
Most menu items were selected at least once, meaning that every dish would eventually get “ordered” by some person or another. Nevertheless, as shown in the histogram below, most menu items were popular among less than 5% of the people surveyed, illustrating the diverse food preferences across people.
Only a dozen items out of 700 dishes were liked by more than 15% of the surveyed people. The two most popular items, liked by 20% of the total people, were the regular Naan and the Garlic Naan at an Indian restaurant. From my personal experience, it’s very hard to imagine a time I didn’t order a naan when visiting an Indian restaurant. The next popular dishes were the “Build your own Omelet” at a diner, Mac and cheese at an American restaurants, and the half pound beef burger and Cheese Nachos at a pub restaurant.
The algorithm
As mentioned in our last post, the aim of a recommender system is to predict future item preferences for a user. We want to predict foods a user may find appealing at a restaurant they have never been to. To build such recommendation system, we used the recommenderLab
package in R.
Through this package, we implemented an item-based collaborative filtering algorithm. This consists of two stages: a model-building stage and a recommendation stage. In the model-building stage, the similarity between all item pairs is computed. This can be done in any number of ways with different similarity metrics (cosine similarity, Pearson correlation, etc). Our data, however, is binary (either a person selected the item or did not) so we used Jaccard similarity which essentially compares how often chosen dishes are shared across individuals. Once the between-item similarity is calculated, a list of recommendations can be made based on dish choices that the user provides.
Below is a code snippet to show how easy it is to build an item-based collaborative filtering system using recommenderLab
. “bin_ratings_clean.RData” is the binary rating matrix data from our survey . The columns represent menu items and each row represents an individual. A cell contains a “1” if the individual selected the item and a “0” if they did not.
After a model is estimated, it is ready to take your preferences from one restaurant and use that to predict what you might like at other restaurants. However, you might be thinking that these recommendations can get boring if it allows you to stay in your comfort zone all the time. This is why we are going to add in a spontaneity feature. While we probably want recommendations optimized for us the majority of time, it can be fun to try something totally random we’d never think of. After all, variety is the spice of life!
A hybrid system with spontaneity
Creating such a hybrid system can also be implemented withrecommenderLab
. We just need to combine two recommender systems (the item-based system from above and a random system) using the `HybridRecommender` function and include some weights to specify how “random” or “item-based” you want the system to be. The code snippet below shows you how we’ve created a hybrid system with a touch of randomness.
Generating recommendations
Now, it’s time to make some recommendations! The function we’ll use to do this will have 4 inputs: the name of the restaurant, the food dish(es) you want to order, whether or not to use the hybrid system (injecting randomness), and how much to weights the spontaneity if hybridized.
Information about the restaurant name and food items will be used to construct our data to feed the model. This amounts to a vector of 0s and 1s with 1s representing the food item(s) chosen at a particular restaurant. Once we have this, we can convert it into an object of class “binaryRatingMatrix”, feed it into our model, and get the resulting predictions. The entire function can be viewed by following the link below:
https://gist.github.com/dasilvaa10/3e1cd989c2b16aee6ae39edc9823406e/
Now, we are ready to make some recommendations! Let’s say, in the past, we went to Murphy’s Pub and ordered Prince Edward Island mussels for an appetizer and Blackened Salmon as an entree. Now, we want to explore other restaurants in the immediate area, but aren’t sure what we’d like to order. We can simulate using what we’ve enjoyed in the past to get recommendations with the code below:
Our recommendations from the system are pictured below consisting of primarily seafood based dishes which makes perfect sense given that the only the the recommender “knows” about us is that we like salmon and mussels. The function is programmed to return the top 5 recommendations for each restaurant; however, our training data may be sparse to do that in some cases.
Next, we can inject a little randomness in our recommendations to shake things up a bit.
We can still see that we are primarily receiving seafood recommendations as we weighted the hybrid system more heavily toward the item based method. However, some interesting new recommendations such as buffalo wings and maple french toast are also present. The random component in the hybrid system also helps deal with the sparsity problem in our training data that effects the item-based predictions.
It may be the case that selecting a dish in a truly random way isn’t the perfect way to add an adventurous component to the food recommendation system. A better way could be to do something like selecting a dish that falls just outside of someone’s top 5 best recommended items at each restaurant. The nice thing about RecommenderLab is that you can easily create your own algorithm and add it in the framework via a simple registration procedure!
Here is the link to code for how we embedded the model into the Shiny.
Conclusion
In sum, we’ve shared some insights about people’s food preferences across the restaurants we surveyed and how that data can be used to build a personalized food recommendation system using recommenderLab
in R and with Shiny. We also tested a spontaneity feature to encourage people to try something new beyond their comfort zone. Once again make sure to checkout our demo app in the link below and let us know if you have any suggestions or feedback.
Written by Alex da Silva and Jin Hyun Cheong
TasteSpace team
Jin Hyun Cheong, Eshin Jolly, Alex da Silva and Marissa Clark.