-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
prepare_nutrients.py
90 lines (74 loc) · 3.98 KB
/
prepare_nutrients.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# count the number of leaf ingredients in the product
# for each nutrient, store in nutrients the number of leaf ingredients that have a nutrient value
# and the sum of the percent_max of the corresponding ingredients
def count_ingredients(ingredients, nutrients):
count = 0
for ingredient in ingredients:
if ('ingredients' in ingredient):
# Child ingredients
child_count = count_ingredients(ingredient['ingredients'], nutrients)
if child_count == 0:
return 0
count = count + child_count
else:
count = count + 1
ingredient_nutrients = ingredient.get('nutrients')
if (ingredient_nutrients is not None):
for off_id in ingredient_nutrients:
proportion = ingredient_nutrients[off_id]['percent_max'] # Use the maximum in a range for weighting
existing_nutrient = nutrients.get(off_id)
if (existing_nutrient is None):
nutrients[off_id] = {'ingredient_count': 1, 'unweighted_total': proportion}
else:
existing_nutrient['ingredient_count'] = existing_nutrient['ingredient_count'] + 1
existing_nutrient['unweighted_total'] = existing_nutrient['unweighted_total'] + proportion
return count
def assign_weightings(product):
# Determine which nutrients will be used in the analysis by assigning a weighting
product_nutrients = product.get('nutriments', {})
count = product['recipe_estimator']['ingredient_count']
computed_nutrients = product['recipe_estimator']['nutrients']
for nutrient_key in computed_nutrients:
computed_nutrient = computed_nutrients[nutrient_key]
# Get nutrient value per 100g of product
product_nutrient = product_nutrients.get(nutrient_key + '_100g', None)
if product_nutrient is None:
computed_nutrient['notes'] = 'Not listed on product'
continue
computed_nutrient['product_total'] = product_nutrient
# Energy is derived from other nutrients, so don't use it
if nutrient_key == 'energy':
computed_nutrient['notes'] = 'Energy not used for calculation'
continue
# Sodium is computed from salt, so don't use it, to avoid double counting
if nutrient_key == 'sodium':
computed_nutrient['notes'] = 'Sodium not used for calculation'
continue
if product_nutrient == 0 and computed_nutrient['unweighted_total'] == 0:
computed_nutrient['notes'] = 'All zero values'
continue
if computed_nutrient['ingredient_count'] != count:
computed_nutrient['notes'] = 'Not available for all ingredients'
continue
# Favor Sodium over salt if both are present
#if not 'error' in nutrients.get('Sodium (mg/100g)',{}) and not 'error' in nutrients.get('Salt (g/100g)', {}):
# nutrients['Salt (g/100g)']['error'] = 'Prefer sodium where both present'
# Weighting based on size of ingredient, i.e. percentage based
# Comment out this code to use weighting specified in nutrient_map.csv
try:
if product_nutrient > 0:
computed_nutrient['weighting'] = 1 / product_nutrient
else:
computed_nutrient['weighting'] = min(0.01, count / computed_nutrient['unweighted_total']) # Weighting below 0.01 causes bad performance, although it isn't that simple as just multiplying all weights doesn't help
except Exception as e:
computed_nutrient['notes'] = e
if nutrient_key == 'xsalt':
computed_nutrient['weighting'] = 100
continue
computed_nutrient['weighting'] = 1
def prepare_nutrients(product):
nutrients = {}
count = count_ingredients(product['ingredients'], nutrients)
product['recipe_estimator'] = {'nutrients':nutrients, 'ingredient_count': count}
assign_weightings(product)
return