A Quick Look at the JMCC Menus

We start off by importing some standard definitions.


In [101]:
from dds_lab import *
from collections import Counter

Next, we read in a CSV (comma separated values) file that is stored on the server. This is the week-by-week menu from JMCC. (You can also look at the same data via this GitHub file.)


In [102]:
menu_csv = pd.read_csv("../data/uoe_catering/JMCC_Student_Menu_2015-2016.csv")

We've converted the CSV file into a DataFrame called menu_csv using Pandas. Let's check the value of this DataFrame.


In [103]:
menu_csv


Out[103]:
JMCC STUDENT DINNER MENUS 2015/2016 Unnamed: 1 Unnamed: 2 Unnamed: 3 Unnamed: 4 Unnamed: 5 Unnamed: 6 Unnamed: 7 Unnamed: 8 Unnamed: 9 Unnamed: 10 Unnamed: 11 Unnamed: 12 Unnamed: 13
0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 MONDAY DINNER WEEK 1 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
3 COUNTER NaN DIETARY THEATRE MENU ITEM, NaN NaN NaN NaN NaN NaN NaN NaN NaN
4 COUNTER 1 Dish/course NaN NaN SELECTION OF ROLLS ON ALL COUNTERS NaN NaN NaN NaN NaN NaN NaN NaN NaN
5 Live Cooking Main Course NaN YES GRILLED PORK ESCALOPE WITH A CREAMY PEPPER SAUCE NaN NaN NaN NaN NaN NaN NaN NaN NaN
6 NaN Main Course NaN NaN MINCE BEEF PIE WITH A PUFF PASTRY TOPPING NaN NaN NaN NaN NaN NaN NaN NaN NaN
7 NaN Accomp V NaN OVEN RST VEGETABLES NaN NaN NaN NaN NaN NaN NaN NaN NaN
8 NaN Accomp V NaN BABY BOILED WITH THYME BUTTER NaN NaN NaN NaN NaN NaN NaN NaN NaN
9 NaN Accomp V NaN BAKED SWEET POTATO NaN NaN NaN NaN NaN NaN NaN NaN NaN
10 NaN Accomp V NaN WHOLE GREEN BEANS NaN NaN NaN NaN NaN NaN NaN NaN NaN
11 NaN Accomp V NaN MASHED POTATO NaN NaN NaN NaN NaN NaN NaN NaN NaN
12 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
13 Bottom counter Main Course V NaN PEPPERS STUFFED WITH FETA CHEESE/QUORN & RICE NaN NaN NaN NaN NaN NaN NaN NaN NaN
14 NaN NaN NaN NaN TOPPED WITH CHEESE NaN NaN NaN NaN NaN NaN NaN NaN NaN
15 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
16 COUNTER 2 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
17 NaN Main Course NaN NaN MINCE BEEF PIE WITH A PUFF PASTRY TOPPING NaN NaN NaN NaN NaN NaN NaN
18 NaN Main Course NaN NaN SALMON FILLET WITH A CHERRY TOMATO AND HERB DR... NaN NaN NaN NaN NaN NaN NaN NaN NaN
19 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
20 NaN Accomp V NaN OVEN RST VEGETABLES NaN NaN NaN NaN NaN NaN NaN NaN NaN
21 NaN Accomp V NaN BABY BOILED WITH THYME BUTTER NaN NaN NaN NaN NaN NaN NaN NaN NaN
22 NaN Accomp V NaN BAKED SWEET POTATO NaN NaN NaN NaN NaN NaN NaN NaN NaN
23 NaN Accomp V NaN WHOLE GREEN BEANS NaN NaN NaN NaN NaN NaN NaN NaN NaN
24 NaN Accomp V NaN MASHED POTATO NaN NaN NaN NaN NaN NaN NaN NaN NaN
25 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
26 Bottom counter Main Course V NaN PEPPERS STUFFED WITH FETA CHEESE/QUORN & RICE NaN NaN NaN NaN NaN NaN TOPPED WITH CHEESE NaN NaN
27 NaN Main Course V NaN MACCARONI CHEESE NaN NaN NaN NaN NaN NaN NaN NaN NaN
28 COUNTER 3 NaN NaN NaN ASSORTED BREADS AND ROLLS NaN NaN NaN NaN NaN NaN NaN NaN NaN
29 NaN Salad Bar V NaN ASSORTED SALAD BAR SELECTION NaN NaN NaN NaN NaN NaN NaN NaN NaN
... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
2675 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2676 NaN Accomp V NaN CHILLI & COCONUT RICE NaN NaN NaN NaN NaN NaN NaN NaN NaN
2677 NaN Accomp V NaN NAAN BREAD NaN NaN NaN NaN NaN NaN NaN NaN NaN
2678 NaN Accomp V NaN SPICY INDIAN POTATOES WITH CHILIES NaN NaN NaN NaN NaN NaN NaN NaN NaN
2679 NaN Accomp V NaN INDIAN CAULIFLOWER CURRY NaN NaN NaN NaN NaN NaN NaN NaN NaN
2680 NaN Accomp V NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2681 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2682 Bottom counter Main Course V NaN VEGETABLE & CHICKPEA KORMA NaN NaN NaN NaN NaN NaN NaN NaN NaN
2683 NaN NaN V NaN VEGETABLE PAKORA NaN NaN NaN NaN NaN NaN NaN NaN NaN
2684 COUNTER 3 NaN NaN NaN ASSORTED BREADS AND ROLLS NaN NaN NaN NaN NaN NaN NaN NaN NaN
2685 NaN Salad Bar NaN NaN ASSORTED SALAD BAR SELECTION NaN NaN NaN NaN NaN NaN NaN NaN NaN
2686 NaN NaN NaN NaN SEEDS AND NUTS NaN NaN NaN NaN NaN NaN NaN NaN NaN
2687 NaN NaN V NaN SEASONED MIXED RICE NaN NaN NaN NaN NaN NaN NaN NaN NaN
2688 NaN Caesars Corner V NaN MIXED SALAD LEAVES NaN NaN NaN NaN NaN NaN NaN NaN NaN
2689 NaN NaN V NaN CROUTONS NaN NaN NaN NaN NaN NaN NaN NaN NaN
2690 NaN NaN NaN NaN CEASAR DRESSING NaN NaN NaN NaN NaN NaN NaN NaN NaN
2691 Grill counter Main Course NaN YES PUNJABI BUTTERED CHICKEN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2692 NaN Main Course V NaN VEGETABLE & CHICKPEA KORMA NaN NaN NaN NaN NaN NaN NaN NaN NaN
2693 NaN Main Course NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2694 NaN Accomp V NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2695 COUNTER 4 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2696 NaN Dessert V NaN STICKY & SWEET SPICED MANGO SPONGE PUDDING WIT... NaN NaN NaN NaN NaN NaN NaN NaN NaN
2697 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2698 COUNTER 5 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2699 NaN Soup V NaN INDIAN SWEET POTATO & SWEETCORN SOUP WITH A CH... NaN NaN NaN NaN NaN NaN NaN NaN NaN
2700 NaN NaN NaN NaN CONDIMENTS NaN NaN NaN NaN NaN NaN NaN NaN NaN
2701 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2702 COUNTER 6 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2703 NaN Dessert V NaN ASSORTED FRESH FRUIT NaN NaN NaN FRUIT SALAD NaN NaN NaN NaN NaN
2704 NaN NaN V NaN ASSORTED YOGHURT NaN NaN NaN NaN NaN NaN NaN NaN NaN

2705 rows × 14 columns

You'll notice lots of cells containing 'NaN'. This stands for Not a Number, and arises because the menu file has lots of blanks in it. It's been designed for visual inspection rather than for any kind of processing.

We'll focus on the column that is currently labeled 'Unnamed: 4'.


In [104]:
food = menu_csv['Unnamed: 4']

Next, we use the function dropna to get rid of the NaN values, and also convert it a regular Python list. The notation [1:] just says that we want to drop the first item in the list.


In [105]:
food = food.dropna().tolist()[1:]

Let's look at the first 10 items in the list:


In [106]:
food[:9]


Out[106]:
['SELECTION OF ROLLS ON ALL COUNTERS',
 'GRILLED PORK ESCALOPE WITH A CREAMY PEPPER SAUCE ',
 'MINCE BEEF PIE WITH A PUFF PASTRY TOPPING ',
 'OVEN RST VEGETABLES ',
 'BABY BOILED WITH THYME BUTTER ',
 'BAKED SWEET POTATO ',
 'WHOLE GREEN BEANS ',
 'MASHED POTATO',
 'PEPPERS STUFFED WITH FETA CHEESE/QUORN & RICE ']

So this is beginning to look more interesting. One simple thing we might want to ask is: what are the most frequent words in the menu items?

We will use the split() method to convert a string like 'TOPPED WITH CHEESE' into a list of words by splitting on every space.


In [107]:
'TOPPED WITH CHEESE'.split()


Out[107]:
['TOPPED', 'WITH', 'CHEESE']

And since allcaps is UGLY, we can convert words into lower case with the lower() method.


In [108]:
'TOPPED'.lower()


Out[108]:
'topped'
The next command puts these two things together to produce a long list of lowercase words:

In [109]:
food_items = [word.lower() for item in food for word in item.split()]

Let's look at the top 10 items in our new list.


In [110]:
food_items[:9]


Out[110]:
['selection',
 'of',
 'rolls',
 'on',
 'all',
 'counters',
 'grilled',
 'pork',
 'escalope']

It would be nice to count how many occurrences of each word appear in this list. Python has a convenient way of getting frequency counts using a Counter. It works like this:


In [111]:
c = Counter(food_items)
c.most_common(20)


Out[111]:
[('with', 324),
 ('and', 220),
 ('assorted', 206),
 ('potato', 166),
 ('a', 153),
 ('grilled', 144),
 ('rolls', 113),
 ('selection', 103),
 ('tomato', 95),
 ('baked', 87),
 ('sauce', 84),
 ('salad', 75),
 ('of', 71),
 ('bacon', 66),
 ('dressing', 63),
 ('chefs', 61),
 ('choice', 61),
 ('fruit', 61),
 ('breads', 60),
 ('on', 58)]

Whoops, that's not so good. We don't care about boring words like 'with' and 'and'. So let's get rid of them. First, we make a list boring of words to ignore. Next, we redefine food_items. You can read [item for item in food_items if item not in boring] as saying: "construct the list of all items in food_items which are not in the boring list". Then we count again.


In [112]:
boring = ['a','all','with','on','of','and','item','menu','&','chefs','counters','bar','mixed','choice','assorted','selection']
food_items = [item for item in food_items if item not in boring]
c = Counter(food_items)
c.most_common(20)


Out[112]:
[('potato', 166),
 ('grilled', 144),
 ('rolls', 113),
 ('tomato', 95),
 ('baked', 87),
 ('sauce', 84),
 ('salad', 75),
 ('bacon', 66),
 ('dressing', 63),
 ('fruit', 61),
 ('breads', 60),
 ('chicken', 58),
 ('fresh', 58),
 ('vegetable', 56),
 ('yoghurt', 54),
 ('item,', 53),
 ('sweet', 53),
 ('beans', 46),
 ('rst', 43),
 ('onion', 41)]

Surprise! Potatoes are really popular with the JMCC chefs.

If we want, we can convert this frequency list back into a DataFrame, using the following commands.


In [127]:
df = pd.DataFrame.from_dict(c, orient='index')
df = df.rename(columns={0:'count'})
df


Out[127]:
count
merinuges 1
chilies 2
quarter 2
mozzarela 8
oven 6
roast 36
mango 1
fry 4
lancashire 2
brown 2
pounder 2
rissoto 4
low 1
salmon/topped 2
dog 10
dietary 1
vegegtrain 1
cut 3
suir 2
jacket 35
tortilla 3
pot 2
milanese 2
youghurt 1
19th 1
,onion 2
,vegetable 1
/diced 2
bourginion 1
,chipolatas 2
... ...
lamb 8
pizza 7
cod 19
wholemeal 8
pakora 6
cheese 21
vegetbale 1
brasied 2
) 1
split 1
florets 10
steamed 2
cheese,sourcream 2
thighs 4
soaked 1
curry 18
savoy 2
prawns 2
stilton 2
rogan 2
out 2
rings 7
cumberland 2
casserole 7
olives 2
doughnuts 1
berry 1
saute 4
acreamy 4
potato 166

495 rows × 1 columns

And we can get a nice looking table by sorting and taking the top 20 items.


In [133]:
df_sorted = df.sort_values(by='count', ascending=False).head(20)
df_sorted


Out[133]:
count
foo
potato 166
grilled 144
rolls 113
tomato 95
baked 87
sauce 84
salad 75
bacon 66
dressing 63
fruit 61
breads 60
fresh 58
chicken 58
vegetable 56
yoghurt 54
sweet 53
item, 53
beans 46
rst 43
rice 41

In [139]:
%matplotlib inline
df_sorted.plot(kind='bar')


Out[139]:
<matplotlib.axes._subplots.AxesSubplot at 0x1156d1e80>

Finally, we can write this DataFrame back to a CSV file, which we'll call food_items.csv.


In [119]:
df.to_csv('food_items.csv')