Code
import pandas as pd
url = 'https://eds-217-essential-python.github.io/data/banana_index.csv'
df = pd.read_csv(url, index_col='entity')
df.shape(160, 16)
π The Banana Index: Ranking the Cost of Food
Work the exercise first, then come here. The code below is one correct answer, not the only one. If your code looks different but produces the same numbers, you were right.
The written answers matter more than the code. You can already tell whether your code ran. What you cannot check on your own is whether you read the result correctly, and that is what the green Answer boxes are for. Compare your markdown cells against them.
β¬ οΈ Back to the exercise
(160, 16)
['emissions_kg',
'emissions_1000kcal',
'emissions_100g_protein',
'emissions_100g_fat',
'land_use_kg',
'land_use_1000kcal',
'Land use per 100 grams of protein',
'Land use per 100 grams of fat',
'Bananas index (kg)',
'Bananas index (1000 kcalories)',
'Bananas index (100g protein)']
1. How many foods are in the dataset, and how many columns are left after the drop?
160 foods and 11 columns. The file arrived with 16 columns and you dropped 5, so 11 remain. The number of rows never changed, because dropping columns does not remove foods.
2. Display the first few rows. What do the three columns whose names begin with Bananas index contain?
| emissions_kg | emissions_1000kcal | emissions_100g_protein | emissions_100g_fat | land_use_kg | land_use_1000kcal | Land use per 100 grams of protein | Land use per 100 grams of fat | Bananas index (kg) | Bananas index (1000 kcalories) | Bananas index (100g protein) | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| entity | |||||||||||
| Ale | 0.488690 | 0.317338 | 0.878525 | 2.424209 | 0.811485 | 0.601152 | 1.577687 | 3.065766 | 0.559558 | 0.362340 | 0.113771 |
| Almond butter | 0.387011 | 0.067265 | 0.207599 | 0.079103 | 7.683045 | 1.296870 | 3.608433 | 1.495297 | 0.443134 | 0.076804 | 0.026885 |
| Almond milk | 0.655888 | 2.222230 | 13.595512 | 4.057470 | 1.370106 | 2.675063 | 12.687839 | 4.600530 | 0.751002 | 2.537364 | 1.760651 |
| Almonds | 0.602368 | 0.105029 | 0.328335 | 0.119361 | 8.230927 | 1.423376 | 4.261040 | 1.610136 | 0.689721 | 0.119923 | 0.042520 |
| Apple juice | 0.458378 | 0.955184 | 29.152212 | 19.754980 | 0.660629 | 1.382839 | 43.232158 | 26.246743 | 0.524851 | 1.090638 | 3.775280 |
Each one is a ratio, not a measurement. It says how many times the bananaβs climate cost a food carries, and the three columns differ only in what is being held constant: a kilogram of food, a thousand calories of food, or a hundred grams of protein. So a score of 20 means twenty bananasβ worth, on whichever basis that column uses. The raw emissions columns those ratios were built from are still in the table, which is what lets you check the arithmetic yourself.
3. Run .info(). Two columns have missing values. Which, and how many?
<class 'pandas.core.frame.DataFrame'>
Index: 160 entries, Ale to Yoghurt
Data columns (total 11 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 emissions_kg 160 non-null float64
1 emissions_1000kcal 160 non-null float64
2 emissions_100g_protein 158 non-null float64
3 emissions_100g_fat 160 non-null float64
4 land_use_kg 160 non-null float64
5 land_use_1000kcal 160 non-null float64
6 Land use per 100 grams of protein 158 non-null float64
7 Land use per 100 grams of fat 160 non-null float64
8 Bananas index (kg) 160 non-null float64
9 Bananas index (1000 kcalories) 160 non-null float64
10 Bananas index (100g protein) 160 non-null float64
dtypes: float64(11)
memory usage: 15.0+ KB
emissions_kg 0
emissions_1000kcal 0
emissions_100g_protein 2
emissions_100g_fat 0
land_use_kg 0
land_use_1000kcal 0
Land use per 100 grams of protein 2
Land use per 100 grams of fat 0
Bananas index (kg) 0
Bananas index (1000 kcalories) 0
Bananas index (100g protein) 0
dtype: int64
emissions_100g_protein and Land use per 100 grams of protein, two missing values each.
Both are per-protein measures, and that is not a coincidence. A food with no protein at all cannot have a per-protein figure, because the division has nothing to divide by. The missing values are telling you something true about the food rather than something careless about the data collection. This is worth pausing on: a null is sometimes a fact.
4. What is the value of Bananas index (kg) for Bananas itself? Say why that number is what it is.
1.0, and it could not be anything else. The index is each foodβs emissions divided by the bananaβs emissions, so the bananaβs own row is its emissions divided by itself. The banana is the unit of the scale, in the same way that a metre is one metre long.
5, 6 and 7. The three top-10 lists.
entity
Beef steak 148.563324
Beef mince 108.816189
Beef meatballs 81.052853
Beef burger 61.803856
Lamb chops 35.383303
Lamb (leg) 35.198904
Lamb burgers 30.833345
Cottage cheese 28.944313
Parmesan cheese 27.499275
Prawns 23.943772
Name: Bananas index (kg), dtype: float64
entity
Beef steak 77.752629
Beef mince 54.049393
Beef meatballs 35.782826
Cottage cheese 33.508657
Prawns 30.063042
Lettuce 28.915534
Raspberries 28.105624
Beef burger 25.011498
Lamb (leg) 17.335509
Coconut milk 14.670763
Name: Bananas index (1000 kcalories), dtype: float64
entity
Coconut milk 33.320155
Sugar 20.685802
Grapes 15.128521
Beef steak 8.312805
Raspberries 7.878815
Beef mince 6.878002
Marmalade 6.495684
Butter 6.192303
Lettuce 5.173570
Beef meatballs 4.612929
Name: Bananas index (100g protein), dtype: float64
8. Describe in two or three sentences how the three lists differ. Which foods are near the top of all three? Which appear on only one, and can you say why?
Ranked per kilogram, the list is meat: four beef products, three lamb, two cheeses and prawns. Ranked per thousand calories, the beef stays but lamb largely drops out and lettuce and raspberries appear, because they are mostly water and you have to eat a great deal of either to obtain a thousand calories. Ranked per hundred grams of protein, the list stops being about meat at all: coconut milk, sugar, grapes and marmalade take the top places, because they contain almost no protein and so the division blows the ratio up.
Beef is near the top of all three, which is the one robust finding. Sugar appears only in the protein ranking, and the reason is arithmetic rather than agriculture: it is the denominator approaching zero, not the numerator being large.
9. Print the three lists and compare by eye. Which foods appear in all three?
print('Top 10 for Bananas index (kg):')
print(df['Bananas index (kg)'].sort_values(ascending=False).head(10))
print('\nTop 10 for Bananas index (1000 kcalories):')
print(df['Bananas index (1000 kcalories)'].sort_values(ascending=False).head(10))
print('\nTop 10 for Bananas index (100g protein):')
print(df['Bananas index (100g protein)'].sort_values(ascending=False).head(10))Top 10 for Bananas index (kg):
entity
Beef steak 148.563324
Beef mince 108.816189
Beef meatballs 81.052853
Beef burger 61.803856
Lamb chops 35.383303
Lamb (leg) 35.198904
Lamb burgers 30.833345
Cottage cheese 28.944313
Parmesan cheese 27.499275
Prawns 23.943772
Name: Bananas index (kg), dtype: float64
Top 10 for Bananas index (1000 kcalories):
entity
Beef steak 77.752629
Beef mince 54.049393
Beef meatballs 35.782826
Cottage cheese 33.508657
Prawns 30.063042
Lettuce 28.915534
Raspberries 28.105624
Beef burger 25.011498
Lamb (leg) 17.335509
Coconut milk 14.670763
Name: Bananas index (1000 kcalories), dtype: float64
Top 10 for Bananas index (100g protein):
entity
Coconut milk 33.320155
Sugar 20.685802
Grapes 15.128521
Beef steak 8.312805
Raspberries 7.878815
Beef mince 6.878002
Marmalade 6.495684
Butter 6.192303
Lettuce 5.173570
Beef meatballs 4.612929
Name: Bananas index (100g protein), dtype: float64
Beef steak, Beef mince and Beef meatballs. Those three, and nothing else, survive all three ways of asking the question.
10. What does it mean for a food to be near the top of all three lists at once? What would it mean for a food to top one list and appear on neither of the others? Name one food of each kind.
A food at the top of all three is expensive however you measure it. Its ranking does not depend on the choice of denominator, so no reasonable objection about the basis of comparison rescues it. Beef steak is the clearest case: first by weight, first by calorie, fourth by protein.
A food that tops one list only is telling you about the denominator, not about the food. Sugar is second per hundred grams of protein and appears nowhere else, because sugar has essentially no protein, and a small number divided by a number close to zero is large. The ranking is real arithmetic and a misleading claim about sugarβs climate cost.
The practical lesson is that the choice of denominator is an argument, not a technicality. Pick the one that matches what the food is actually for. Compare protein sources per gram of protein and staples per calorie.
11. Every food with a Bananas index (kg) above 10. How many are there?
34 foods, which is a little over one fifth of the dataset.
12 and 13. The ~ version, the < 1 version, and why they differ.
(26, 11)
(25, 11)
26 and 25. The difference is exactly one row, and it is Bananas itself.
~(df['Bananas index (kg)'] > 1) keeps every food that is not above 1, which includes foods that equal 1. df['Bananas index (kg)'] < 1 is strictly less than, so it drops them. Only one food in this dataset scores exactly 1.0, and by construction it is the banana.
The general point is that ~(x > 1) means x <= 1, not x < 1. Negating a strict comparison gives you a non-strict one, and the boundary value moves from one side to the other. Whenever a filter returns one row more or fewer than you expected, check the boundary first.
14. Two-condition filter: above 5 per kilogram and below 1 per 100 g of protein.
(16, 11)
| Bananas index (kg) | Bananas index (100g protein) | |
|---|---|---|
| entity | ||
| Halloumi cheese | 18.517731 | 0.911172 |
| Mackerel | 15.579540 | 0.923830 |
| Tuna | 14.971502 | 0.643961 |
| Pork chops | 13.927860 | 0.959339 |
| Cod | 12.485389 | 0.917841 |
| Salmon | 11.922582 | 0.625777 |
| Chicken thighs | 11.429423 | 0.692125 |
| Pork sausages | 11.184385 | 0.895281 |
| Chicken wings | 10.973219 | 0.661129 |
| Cod fish fingers | 10.663750 | 0.899990 |
| Chicken breast | 10.616967 | 0.585148 |
| Chicken sausages | 9.348264 | 0.699427 |
| Cod fishcakes | 8.949080 | 0.847154 |
| Chicken burger | 6.222579 | 0.474208 |
| Olive oil | 5.936487 | 0.000000 |
| Eggs | 5.079982 | 0.407148 |
15. What do these foods have in common? What is the argument this table makes about how we should compare foods?
16 foods, and almost all of them are animal protein: fish (tuna, cod, salmon, mackerel), poultry (chicken breast, thighs, wings, sausages, burger), pork, eggs, and halloumi. Olive oil is the one that does not fit the pattern, and it is here because its protein figure is essentially zero in the other direction, which is worth noticing rather than glossing over.
The argument is this: these foods look costly when you weigh them and cheap when you weigh what you eat them for. Nobody eats 100 g of chicken as a substitute for 100 g of lettuce; they eat it as a source of protein. Comparing per kilogram flatters foods that are mostly water and penalises foods that are dense in the thing you actually want. Choosing a denominator is choosing what question you are asking, and this table is what that choice looks like when it changes the answer.
16, 17 and 18.
np.float64(2.71787661)
entity
Beef steak 223.690135
Beef mince 147.166781
Beef meatballs 94.463012
Beef burger 54.560874
Cottage cheese 34.694337
Lettuce 34.681560
Lamb (leg) 21.444985
Lentils 20.535191
Lamb chops 16.711250
Chilli con carne 16.398791
Name: land_use_1000kcal, dtype: float64
Beef steak
82.30327093473166
A banana uses 2.72 square metres of land per thousand calories. The most land-hungry food is beef steak, at 223.7 square metres per thousand calories, which is 82.3 times the bananaβs land use.
Note that .idxmax() gave you the label and not the number. That is the whole reason it exists: paired with .loc[], it lets you ask βwhich one?β and then βhow much?β as two separate, readable steps.
19. Compare the land-use top 10 with the emissions top 10 from question 5. Are they the same foods? Name one that appears on one list and not the other.
Seven of the ten are the same: beef steak, beef mince, beef meatballs, beef burger, cottage cheese, lamb (leg) and lamb chops. So the two measures agree far more than they disagree, and beef sits at the top of both.
Three appear only in the land-use list: lentils, lettuce and chilli con carne. Lentils are the interesting one. They take a lot of land per calorie and emit comparatively little, so a ranking built on carbon alone would tell you nothing about them. Three appear only in the emissions list: prawns, parmesan cheese and lamb burgers. Prawns are the mirror image, since much of their footprint comes from fuel and refrigeration rather than from land.
Land and carbon are correlated but not interchangeable. A food can be costly in one and unremarkable in the other, and which you measure determines which foods you notice.
20. Build a table of just the cheeses, then find the cheese with the highest Bananas index (1000 kcalories).
(12, 11)
entity
Cottage cheese 33.508657
Mozzarella cheese 7.723795
Parmesan cheese 7.272295
Goatβs cheese 7.102344
Blue cheese 6.980327
Cheddar cheese 6.172845
Halloumi cheese 6.031632
Macaroni cheese 5.989042
Ricotta cheese 5.939072
Feta cheese 5.757135
Chocolate cheesecake 1.368605
Cheesecake 0.798615
Name: Bananas index (1000 kcalories), dtype: float64
Cottage cheese, at 33.51. It is far above the rest: the next cheese, mozzarella, is 7.72, so cottage cheese is more than four times the second-place figure. Cottage cheese is largely water, so a thousand calories of it is a great deal of cheese.
21. How many rows did .filter(like='heese') give you? Is every one of them a cheese? What does this tell you about matching on text?
['Blue cheese',
'Cheddar cheese',
'Cheesecake',
'Chocolate cheesecake',
'Cottage cheese',
'Feta cheese',
'Goatβs cheese',
'Halloumi cheese',
'Macaroni cheese',
'Mozzarella cheese',
'Parmesan cheese',
'Ricotta cheese']
12 rows, and no, they are not all cheeses. Cheesecake and Chocolate cheesecake are desserts, and Macaroni cheese is a cooked dish rather than a cheese. Only nine of the twelve are cheese in the sense you meant.
Matching on a substring matches strings, not meanings. .filter(like='heese') did exactly what you asked and had no way to know what you intended. This is why you print the labels and read them after any text-based selection, rather than trusting the row count. The habit of looking at what a filter caught, and not just how much it caught, will save you repeatedly.
22. Where does the top cheese sit in the whole datasetβs calorie ranking? Check it against your answer to question 6.
entity
Beef steak 77.752629
Beef mince 54.049393
Beef meatballs 35.782826
Cottage cheese 33.508657
Prawns 30.063042
Lettuce 28.915534
Raspberries 28.105624
Beef burger 25.011498
Lamb (leg) 17.335509
Coconut milk 14.670763
Name: Bananas index (1000 kcalories), dtype: float64
Fourth out of 160, behind beef steak, beef mince and beef meatballs. It is visible in your question 6 answer, which is the check: cottage cheese was already sitting in that top 10, and finding it again from the cheese table means both calculations agree.
Cottage cheese is the only non-meat food in the top four of the calorie ranking, which is what makes the cheese section worth having.
There is no single right answer here. A strong response cites at least three numbers you computed, names at least one disagreement between the rankings, and reaches a conclusion the numbers support. Here is one that would earn full marks.
Cutting bananas is not where the gains are. A banana scores 1.0 on the kilogram index by definition, and it sits near the bottom of every ranking we computed. Beef steak scores 148.6 on the same scale, so a kilogram of steak carries roughly 149 times a kilogram of bananasβ emissions, and 82.3 times the land use per thousand calories. Bananas are also unusually efficient per calorie, which matters because we eat them for calories rather than for protein. Removing them from a diet changes a very small number and creates a gap that has to be filled by something else.
The dataset does make bananas look worse under one measure. Per hundred grams of protein they score above several meats, since a banana has almost no protein and the division inflates the ratio. That disagreement between the rankings is the point rather than a flaw. Coconut milk, sugar and grapes top the protein ranking for the same arithmetic reason, and nobody would argue that sugar is a worse climate choice than beef. The comparison is only meaningful when the denominator matches what the food is for, and bananas are not a protein source.
If my friend wants a change that shows up in the numbers, the place to look is the 34 foods scoring above 10 per kilogram, which is a little over a fifth of this dataset and is dominated by beef, lamb and hard cheeses. Substituting one of those a week would move more carbon than eliminating bananas entirely.
If you compare your notebook against this key, look for these four things before you look at anything else.
β¬ οΈ Back to the exercise