Recommendation System in R by yhat | June 19, 2013

Recommender systems are used to predict the best products to offer to customers. These babies have become extremely popular in virtually every single industry, helping customers find products they'll like. Most people are familiar with the idea, but nearly everyone is exposed to several forms of personalized offers and recommendations each day (Google search ads being among the biggest source).

Building recommendation systems is part science, part art, and many have become extremely sophisticated. Such a system might seem daunting for those uninitiated, but it's actually fairly straight forward to get started if you're using the right tools.

This is a post about building recommender systems in R.

UPDATE: We used the beer / product recommender for a talk at PyData Boston in July.

IPython notebook here: http://bit.ly/1chuxRT.

Beer Dataset

"Respect Beer." - BeerAdvocate.com

For this example, we'll use data from Beer Advocate, a community of beer enthusiasts and industry professionals dedicated to supporting and promoting beer. The data is made available to us via Stanford's web data library. It consists of ~1.5 millions reviews posted on BeerAdvocate from 1999 to 2011.

Each record is composed of a beer's name, brewery, and metadata like style and ABV etc., along with ratings provided by reviewers. Beers are graded on appearance, aroma, palate, and taste plus users provide an "overall" grade. All ratings are on a scale from 1 to 5 with 5 being the best.

In addition to these numerical ratings, users are required to write a short paragraph of 250 to 5,000 characters describing their overall impressions. While the text does provide some excellent opportunities for analysis, we're going to focus only on the ratings for this post. You can read more about their rating system here.

Formatting the Data

This part always takes longer than you'd like, but luckily the beer dataset is pretty clean.

Not that many nulls, and the text fields are free of strange byte characters in them (always throws me off). One thing that's a little different is that the data is laid out row-wise instead of column-wise.

Records are delimited by newlines and have one key/value pair per line. I wrote a short Python script to handle parsing which leaves us with a nicely formatted .csv file.

Since I'm working with a lot of data, I decided to throw it into a database.

I'm working with Postgres, but any relational database will do the trick.

Getting the Breweries

One unfortunate part about the dataset is that it only includes a brewerid and no lookup table for the ids. These correspond with pages on the brewery profiles. For example, the Sierra Nevada Brewing Co. has a brewerid of 140 and their page on beer advocate is /profile/140 .

In any case, what we really need is the brewery name associated with each id, which means doing a little web scraping. I really didn't want to get into installing any Postgres programming clients (psycopg2 for Python), so I wrote a short bash script to grab the brewery ids.

It's far from ideal, but it's short, simple, and it works. You can skip this part if you prefer and just download the data here.

Loading it into R

We've got everything in a database. Nice!

We're going to use the excellent RPostgreSQL driver which makes it super easy to query Postgres from R . You'll notice we've got a little sub-query action going on here. All our sub-query is doing is grabbing all beers with 500+ reviews.

Let's take a peak at the data using the head command. As you can see, we've got a few Colorado Kool-Aids at the top of the heap (not surprising, it's a pretty popular beer).

You can see we've got the id, name, and brewery of each beer and the associated review data provided by a given user as denoted by the review_profilename column.

Finding Similarities

The goal for our system will be for a user to provide us with a beer that they know and love, and for us to recommend a new beer which they might like. To accomplish this, we're going to use collaborative filtering. We're going to compare 2 beers by ratings submitted by their common reviewers. Then, when one user writes similar reviews for two beers, we'll then consider those two beers to be more similar to one another.

We'll need a function which takes two beers and returns their mutual reviewers (or sameset). To do this, we'll use the intersect function in R which finds common elements between two lists or vectors.

I wrote two functions: common_reviewers_by_id to extract the sameset given two beer_ids, and common_reviewers_by_name to extract the samesets given two beer_names. For programming purposes it's easier to use common_reviewers_by_id , but for testing and spot checking, common_reviewers_by_name is handy.

Next we need a function to extract features for a given beer. Features, in this case, are the 1 to 5 numerical ratings provided by users as part of each beer's review.

Two things probably stick out in this function. (1) We're sorting the data by the reviewers username. This is so that when we extract features for say, Coors Light and Founders Double Trouble, the reviews in indicies 0, 1, 2, ..., N correspond with reviews made by the same users.

(2) We're de-duplicating the reviews based on profile name. There are a few instances of users reviewing the same beer twice. Since we want the review data across beers to be aligned, we're just going to throw out any instances of multiple reviews by a user for the same beer.

Given two beers, we look at the similarity between how reviewers clocked-in with each of the 1 to 5 ratings.

To give you a visual, take a look at the charts below.

Users who like Fat Tire tended to not like Michelob Ultra as much.

The x-y coordinates correspond with how users rated each of the two beers. For example, a person who rated Fate Tire a 4.5 overall and Michelob Ultra a 2.5 overall appears as a point found at (4.5, 2.5) in top left quadrant of the first graphic above. The size of the dots correspond to the number of reviewers that wound up in a given bucket.

Users tend to rate Fat Tire higher than Michelob Ultra, as illustrated by the majority of points found below the center line.

However, when we compare Fat Tire to Dale's Pale Ale, we get a different story. We see that reviewers tended to rate both more or less consistently. Points are closer to the center line than those found in the Fat Tire-Michelob comparison. Intuitively, this suggests that it would be better to recommend Dale's Pale Ale to someone who likes Fat Tire than to someone who likes Michelob Ultra.

Quantifying Our Beliefs

I don't need a statistical model to tell me that someone who likes Fat Tire is probably going to like Dale's Pale Ale more than Michelob Ultra. But what about picking between Dale's Pale Ale and Sierra Nevada Pale Ale? Things get a little more complicated. For this reason (and because we don't want to manually select between each beer pair), we're going to write a distance function that will quantify similarity.

For our similarity metric we're going to use a weighted average of the correlation of each metric. In other words, for each two-beer-pair we calculate the correlation of review_overall , review_aroma , review_palate , and review_taste separately. Then we take a weighted average each result to consolidate them into one number.

We're going to weight review_overall with 2 and the remainder will have a weight of 1. This gives review_overall 40% of the score (NOTE: this is totally arbitrary, you can use whatever weighting function you want. A lot of times the simplest stuff works the best in my experience).

Computing Similarity Across All 2-Beer-Pairs

To keep things simple, we're only going to compare the 20 most commonly reviewed beers in the example code. This will give us enough data to make sure everything is working as expected, but it's still a small enough sample size that it won't take too long to compute.

The first thing we do is define the 20 beers we want to use. Then we use expand.grid to create all of the combinations between the beers. Finally we remove any self-to-self comparisons (if you like Dale's Pale Ale, it wont' help you very much if we recommend Dale's Pale Ale). We're then going to use ddply to do a map/reduce style calculation on the data. Note that it's possible to parallelize ddply . Although we're not doing it here, in an upcoming post I'll show you how to do run ddply in parallel using EC2.

I wrote a short helper function find_similar_beers that accepts a beer you like and optionally a number of suggested beers and a desired style, and returns the most similar beers in a nice format.

Deploying to Yhat

Deploying this particular model was really easy. I just wrapped my find_similar_beers function in the yhat.predict function, added my apikey, and that was it. I didn't even need to use the yhat.require or yhat.transform functions.

Getting Your Recommendations

To make recommendations on the web, I wrote a quick app with Heroku and Flask that consumes the Yhat API. You can see some of that javascript below, or you can check out the standalone app here.

Beer Recommender Beer You Like: Select a beer.. 120 Minute IPA 1554 Enlightened Black Ale 21st Amendment IPA 2° Below Winter Ale 2XIPA 30th Anniversary - Fritz And Ken's Ale 312 Urban Wheat 471 IPA 60 Minute IPA #9 90 Minute IPA Abbey Belgian Style Ale Abrasive Ale Adam Aecht Schlenkerla Rauchbier Märzen Aecht Schlenkerla Rauchbier Urbock Alaskan Smoked Porter AleSmith IPA AleSmith Speedway Stout A Little Sumpin' Sumpin' Ale A Little Sumpin' Wild Allagash Curieux (Bourbon Barrel-Aged Tripel) Allagash Dubbel Ale Allagash Tripel Ale Allagash White Alpha King Pale Ale Alpha Klaus Christmas (Xmas) Porter American Amber Ale Anchor Liberty Ale Anchor Old Foghorn Anchor Porter Anchor Steam Beer Anchor Summer Beer ApriHop Arrogant Bastard Ale Ayinger Bräu Weisse Ayinger Celebrator Doppelbock Ayinger Oktober Fest-Märzen Back In Black Baltic Thunder Baltika #6 Porter Bam Bière Barney Flats Oatmeal Stout Bass Pale Ale Beamish Irish Stout Beck's Beer Geek Breakfast Beer Geek Brunch Weasel Belhaven Scottish Ale Bell's Amber Ale Bell's Best Brown Bell's Cherry Stout Bell's Consecrator Doppelbock Bell's Expedition Stout Bell's Hopslam Ale Bell's Java Stout Bell's Kalamazoo Stout Bell's Oberon Ale Bell's Pale Ale Bell's Porter Bell's Special Double Cream Stout Bell's Winter White Ale Bender Big Bear Black Stout Big Eye IPA Bitburger Premium Pils Black Albert Black Butte Porter Blanche De Chambly Blind Pig IPA Blithering Idiot Blue Moon Belgian White Blue Moon Harvest Moon Pumpkin Ale Boddingtons Pub Ale B.O.R.I.S. The Crusher Oatmeal-Imperial Stout Bourbon County Brand Stout Brooklyn Ale / Pennant Ale '55 Brooklyn Black Chocolate Stout Brooklyn Black OPS Brooklyn Brown Ale Brooklyn East India Pale Ale Brooklyn Lager Brooklyn Local 1 Brooklyn Monster Ale Brooklyn Oktoberfest Beer Brother Thelonious Brown Shugga' Brutal Bitter Ale Bud Light Budweiser Budweiser American Ale Budweiser Budvar Buffalo Bill's Pumpkin Ale Burton Baton Cadillac Mountain Stout Cane & Ebel Cantillon Kriek 100% Lambic Cantillon Rosé De Gambrinus Cappuccino Stout Cascazilla Censored Chicory Stout Chimay Grande Réserve (Blue) Chimay Première (Red) Chimay Tripel (White) Chipotle Ale Chocolate Indulgence Stout Chocolate Oak Aged Yeti Chocolate Stout Choklat Circus Boy Coffee Bender Collaboration Not Litigation Ale Consecration Coors Coors Light Corona Extra Corsendonk Pater / Abbey Brown Ale Creme Brulee (Imperial Milk Stout) Cuvée Van De Keizer Blauw (Blue) CynicAle Dale's Pale Ale Damnation Dark Horse Crooked Tree IPA Dark Horse Double Crooked Tree IPA Dark Lord Imperial Stout Darkness Dead Guy Ale Delirium Nocturnum Delirium Noël Delirium Tremens Denver Pale Ale Doggie Style Classic Pale Ale Dominion Oak Barrel Stout Don de Dieu Dos Equis Amber Lager Double Barrel Ale Double Bastard Ale Double Dog Double Pale Ale Double Jack Double Simcoe IPA Double White Ale Double Wide I.P.A. Dreadnaught IPA DreamWeaver Wheat Duchesse De Bourgogne Duck-Rabbit Milk Stout Dundee Original Honey Brown Lager Duvel Ellie's Brown Ale Éphémère (Apple) Erdinger Weissbier Erdinger Weissbier Dunkel Espresso Oak Aged Yeti Imperial Stout Fantôme Saison Fat Tire Amber Ale Festbier Festina Pêche Fire Rock Pale Ale Flower Power India Pale Ale Foster's Lager Founders Backwoods Bastard Founders Breakfast Stout Founders CBS Imperial Stout Founders Centennial IPA Founders Curmudgeon (Old Ale) Founders Devil Dancer Founders Dirty Bastard Founders Double Trouble Founders Harvest Ale Founders Imperial Stout Founders KBS (Kentucky Breakfast Stout) Founders Pale Ale Founders Porter Founders Red's Rye PA Franziskaner Hefe-Weisse Franziskaner Hefe-Weisse Dunkel Fraoch Heather Ale Fred Fresh Hop Pale Ale Fuller's 1845 Fuller's ESB Fuller's London Porter Fuller's London Pride Fuller's Vintage Ale Furious Gemini (Imperial Blended Ale) G'Knight Imperial Red Ale Golden Monkey Gonzo Imperial Porter Great Lakes Blackout Stout Great Lakes Burning River Pale Ale Great Lakes Christmas Ale Great Lakes Commodore Perry IPA Great Lakes Dortmunder Gold Great Lakes Edmund Fitzgerald Porter Great Lakes Eliot Ness Great Lakes Nosferatu Green Flash Hop Head Red Ale Green Flash Imperial India Pale Ale Green Flash Le Freak Green Flash West Coast I.P.A. Grolsch Premium Lager GUBNA Imperial IPA Guinness Draught Guinness Extra Stout (Original) Guinness Foreign Extra Stout Gulden Draak (Dark Triple) Gumballhead Hacker-Pschorr Hefe Weisse Natürtrub Hacker-Pschorr Oktoberfest-Märzen Hairy Eyeball Ale Harp Lager Harpoon IPA Harpoon Leviathan - Imperial IPA Harpoon Winter Warmer Hazed & Infused Hazelnut Brown Nectar Heavy Seas - Loose Cannon (Hop3 Ale) Heavy Seas - Peg Leg (Imperial Stout) Heavy Seas - Small Craft Warning (Über Pils) He'Brew Bittersweet Lenny's R.I.P.A. Heineken Lager Beer Hell Hath No Fury Ale Hennepin (Farmhouse Saison) Hercules Double IPA Heresy Hibernation Ale Hi.P.A. Hobgoblin Hoegaarden Original White Ale Hog Heaven Barley Wine Honker's Ale Hop 15 HopDevil Ale Hop Henge Experimental IPA Hop Ottin' IPA Hop Rod Rye Hopsickle Imperial India Pale Ale Hops Infusion Hop Stoopid Hoptical Illusion Hoptimum Double IPA - Beer Camp #19 Hop Wallop Horn Dog Barley Wine Style Ale Houblon Chouffe Dobbelen IPA Tripel Immort Ale Imperial Pumpkin Ale Imperial Stout Indian Brown Ale India Pale Ale India Pale Ale (IPA) Indica India Pale Ale In-Heat Wheat Iniquity (Imperial Black Ale) Insanity Inversion IPA IPA (India Pale Ale) Jack D'or Jahva (Imperial Coffee Stout) Jai Alai IPA Jubelale Juniper Pale Ale J.W. Lees Vintage Harvest Ale Killian's Irish Red Kirin Ichiban Köstritzer Schwarzbier Kulmbacher Reichelbrau Eisbock Labatt Blue La Chouffe La Fin Du Monde La Folie Lagunitas IPA Lagunitas Lucky 13 Mondo Large Red Ale Lagunitas PILS (Czech Style Pilsner) La Roja La Terrible La Trappe Quadrupel (Koningshoeven / Dominus) Leffe Blonde Left Hand Milk Stout Leinenkugel's Sunset Wheat Life & Limb Lindemans Framboise Lindemans Gueuze Cuvée René Lion Stout Long Hammer IPA Lukcy 13asartd Ale Mackeson Triple XXX Stout Maharaja Mama's Little Yella Pils Maredsous 10 - Triple Maredsous 8 - Dubbel Masala Mama India Pale Ale Matilda Maudite Maximus Mephistopheles' Stout Merry Monks' Ale Michelob Amber Bock Michelob Ultra Midas Touch Golden Elixir Miles Davis' Bitches Brew Miller Genuine Draft Miller High Life Miller Lite Mirror Pond Pale Ale Mocha Porter Modus Hoperandi Mojo IPA Mokah Monk's Café Flemish Sour Ale Monty Python's Holy Grail Ale Moonglow Weizenbock Moose Drool Brown Ale Mothership Wit (Organic Wheat Beer) Moylander Double IPA Murphy's Irish Stout Natural Light Negra Modelo Newcastle Brown Ale New Holland Dragon's Milk Oak Barrel Ale New Holland Mad Hatter India Pale Ale New Holland The Poet Night Stalker Noire De Chambly / Chambly Noire Northern Hemisphere Harvest Wet Hop Ale Oak Aged Yeti Imperial Stout Oaked Arrogant Bastard Ale Oat (Imperial Oatmeal Stout) Obsidian Stout Old #38 Stout Old Chub - Scottish Style Ale Olde GnarlyWine Old Engine Oil Older Viscosity Olde School Barleywine Old Heathen Imperial Stout Old Horizontal Old Jubilation Old Rasputin Russian Imperial Stout Old Ruffian Barley Wine Old Scratch Amber Lager Old Speckled Hen Old Stock Ale Old Viscosity Ommegang (Abbey Ale) Ommegang Witte Oro De Calabaza Orval Trappist Ale Pabst Blue Ribbon (PBR) Palo Santo Marron Pangaea Pannepot Parabola Paulaner Hefe-Weissbier Naturtrüb Paulaner Oktoberfest-Märzen Pauwel Kwak Péché Mortel (Imperial Stout Au Cafe) Pere Jacques Peroni Nastro Azzurro Phin & Matt's Extraordinary Ale Pilsner Urquell Pipeline Porter Piraat Ale Plead The 5th Imperial Stout Pliny The Elder Pliny The Younger Post Road Pumpkin Ale Pranqster Pride & Joy Mild Ale Prima Pils Pumking Pumpkinhead Ale Punkin Ale Pure Hoppiness Purple Haze Racer 5 India Pale Ale Raging Bitch Belgian-Style IPA Raison D'etre Raison D'extra Ranger Rare Vos (Amber Ale) Raspberry Tart Redhook ESB Red Rocket Ale Red Stripe Jamaican Lager Red & White Reissdorf Kölsch Road Dog Porter Robert The Bruce Scottish Ale Rodenbach Classic (Red) Rodenbach Grand Cru Rolling Rock Extra Pale Ruedrich's Red Seal Ale Saint Bridget's Porter Saison Dupont Salvator Doppel Bock Sam Adams Light Samichlaus Bier Samuel Adams Blackberry Witbier Samuel Adams Black Lager Samuel Adams Boston Ale (Stock Ale) Samuel Adams Boston Lager Samuel Adams Brown Ale Samuel Adams Cherry Wheat Samuel Adams Chocolate Bock Samuel Adams Cranberry Lambic Samuel Adams Cream Stout Samuel Adams Double Bock (Imperial Series) Samuel Adams Hallertau Imperial Pilsner Samuel Adams Hefeweizen Samuel Adams Holiday Porter Samuel Adams Honey Porter Samuel Adams Imperial Stout Samuel Adams Imperial White Samuel Adams Irish Red Samuel Adams Latitude 48 IPA Samuel Adams Noble Pils Samuel Adams Octoberfest Samuel Adams Old Fezziwig Ale Samuel Adams Pale Ale Samuel Adams Scotch Ale Samuel Adams Summer Ale Samuel Adams Triple Bock Samuel Adams White Ale Samuel Adams Winter Lager Samuel Smith's Imperial Stout Samuel Smith's India Ale Samuel Smith's Nut Brown Ale Samuel Smith's Oatmeal Stout Samuel Smith's Old Brewery Pale Ale Samuel Smith's, The Famous Taddy Porter Samuel Smith's Winter Welcome Ale Santa's Private Reserve Ale Sapporo Premium Beer Saranac Black Forest Saranac Black & Tan Saranac India Pale Ale Saranac Pale Ale Sawtooth Ale Schneider Aventinus Schneider Aventinus Weizen-Eisbock Schneider-Brooklyner Hopfen-Weisse Schneider Weisse Original Scrimshaw Pilsner Sculpin India Pale Ale Shakespeare Oatmeal Stout Shelter Pale Ale Shiner Bock Shock Top Belgian White Siberian Night Imperial Stout Sierra Nevada Anniversary Ale (2007-2009) Sierra Nevada Bigfoot Barleywine Style Ale Sierra Nevada Celebration Ale Sierra Nevada ESB (Early Spring Beer) Sierra Nevada Glissade Golden Bock Sierra Nevada Kellerweis Hefeweizen Sierra Nevada Pale Ale Sierra Nevada Porter Sierra Nevada Southern Hemisphere Harvest Fresh Hop Ale Sierra Nevada Stout Sierra Nevada Summerfest Lager Sierra Nevada Torpedo Extra IPA Sierra Nevada Tumbler Autumn Brown Ale Sinebrychoff Porter SkullSplitter Smithwick's Smuttynose Big A IPA (Big Beer Series) Smuttynose Imperial Stout (Big Beer Series) Smuttynose IPA "Finest Kind" Smuttynose Old Brown Dog Ale Smuttynose Pumpkin Ale Smuttynose Robust Porter Smuttynose Shoals Pale Ale Snake Dog IPA Sofie Spaten Münchner Hell (Premium Lager) Spaten Oktoberfestbier Ur-Märzen Spaten Optimator Spotted Cow Squall IPA St-Ambroise Oatmeal Stout St. Bernardus Abt 12 St. Bernardus Pater 6 St. Bernardus Prior 8 St. Bernardus Tripel St. Bernardus Witbier Stella Artois Stone 12th Anniversary Bitter Chocolate Oatmeal Stout Stone 13th Anniversary Ale Stone 14th Anniversary Emperial IPA Stone 15th Anniversary Escondidian Imperial Black IPA Stone Cali-Belgique Stone Imperial Russian Stout Stone IPA (India Pale Ale) Stone Levitation Ale Stone Pale Ale Stone Ruination IPA Stone Smoked Porter Stone Sublimely Self-Righteous Ale Storm King Stout Stoudt's Double IPA (India Pale Ale) Stoudt's Fat Dog (Imperial Oatmeal Stout) St. Peter's Cream Stout St. Rogue Red Supplication Sweetwater 420 Extra Pale Ale Sweetwater IPA Temptation Ten FIDY Terrapin Big Hoppy Monster Terrapin Coffee Oatmeal Imperial Stout Terrapin Hopsecutioner Terrapin Rye Pale Ale The Abyss Theakston Old Peculier The Angel's Share - Bourbon Barrel-Aged The Beast Grand Cru The Czar The Kaiser Theobroma The Oracle The Reverend Third Coast Old Ale Thomas Hardy's Ale (2003-2008) Thomas Hooker Liberator Doppelbock Three Philosophers Belgian Style Blend (Quadrupel) Tire Bite Golden Ale Titan IPA Trappistes Rochefort 10 Trappistes Rochefort 6 Trappistes Rochefort 8 Trappist Westvleteren 12 Trappist Westvleteren 8 Traquair House Ale Tripel Karmeliet Trippel Belgian Style Ale Troegenator Double Bock Tröegs Dead Reckoning Porter Tröegs Hopback Amber Ale Tröegs Java Head Stout Tröegs Mad Elf Tröegs Nugget Nectar Tröegs Pale Ale Tröegs Sunshine Pils Trois Pistoles Tsingtao Turbodog Two Hearted Ale UFO Hefeweizen Unearthly (Imperial India Pale Ale) Union Jack India Pale Ale Urthel Hop-It Vanilla Porter Victory At Sea Coffee Vanilla Imperial Porter V-Twelve Warsteiner Premium Dunkel Warsteiner Premium Verum Wee Heavy Weihenstephaner Hefeweissbier Weihenstephaner Hefeweissbier Dunkel Weihenstephaner Korbinian Weihenstephaner Kristallweissbier Weihenstephaner Original Weihenstephaner Vitus Wells Banana Bread Beer Westmalle Trappist Dubbel Westmalle Trappist Tripel Whirlwind Witbier White Hawk Original IPA White Rascal Widmer Hefeweizen Wilco Tango Foxtrot Winter's Bourbon Cask Ale Winter Solstice Seasonal Ale Wipeout I.P.A. Wisconsin Belgian Red World Wide Stout Xingu Black Beer XS Imperial India Pale Ale XS Old Crustacean Yakima Glory Yellow Snow IPA Yeti Imperial Stout Young's Double Chocolate Stout Young's Oatmeal Stout Yuengling Black & Tan Yuengling Traditional Lager YuleSmith (Summer) Style You Want To Try: Select a style.. American Adjunct Lager American Amber / Red Ale American Amber / Red Lager American Barleywine American Black Ale American Brown Ale American Double / Imperial IPA American Double / Imperial Pilsner American Double / Imperial Stout American IPA American Pale Ale (APA) American Pale Lager American Pale Wheat Ale American Porter American Stout American Strong Ale American Wild Ale Baltic Porter Belgian Dark Ale Belgian IPA Belgian Pale Ale Belgian Strong Dark Ale Belgian Strong Pale Ale Berliner Weissbier Bière de Garde Black & Tan Bock California Common / Steam Beer Chile Beer Cream Ale Czech Pilsener Doppelbock Dortmunder / Export Lager Dubbel Dunkelweizen Eisbock English Barleywine English Bitter English Brown Ale English India Pale Ale (IPA) English Pale Ale English Porter English Strong Ale Euro Pale Lager Extra Special / Strong Bitter (ESB) Flanders Oud Bruin Flanders Red Ale Foreign / Export Stout Fruit / Vegetable Beer German Pilsener Gueuze Hefeweizen Herbed / Spiced Beer Irish Dry Stout Irish Red Ale Japanese Rice Lager Kristalweizen Kölsch Lambic - Fruit Light Lager Maibock / Helles Bock Milk / Sweet Stout Munich Dunkel Lager Munich Helles Lager Märzen / Oktoberfest Oatmeal Stout Old Ale Pumpkin Ale Quadrupel (Quad) Rauchbier Russian Imperial Stout Rye Beer Saison / Farmhouse Ale Schwarzbier Scotch Ale / Wee Heavy Scottish Ale Scottish Gruit / Ancient Herbed Ale Tripel Vienna Lager Weizenbock Winter Warmer Witbier



Final Thoughts

A great resources for building recommender systems is Programming Collective Intelligence by Toby Segaran. The book is a few years old, but it's a phenomenal introduction to some of the basics in machine learning. Chapter 2 gives a great overview of recommendation systems and how you can use them. Another good read is Machine Learning for Hackers by Drew Conway and John Myles White. Check out chapter 10 for recommender systems.