diff --git a/meal-planner.org b/meal-planner.org index b177921..a9c9349 100644 --- a/meal-planner.org +++ b/meal-planner.org @@ -211,6 +211,120 @@ week. #+RESULTS: [[file:./working-data/meal-planner-servings.csv]] +* Manually Adjust Serving Quantities + +The original version of this file has /semantically duplicated/ data in it, which +the code isn't really great at detecting. So, I've made minor adjustments, like +bundling up the ’Protein’ entries into one. I, also, replace ’Whole Grain’ with +’Wholemeal’ because I don't think I've ever seen anything labelled as ’Whole +Grain’ in the UK, making it harder to search for these types of items on the +various shopping websites. + +#+NAME: servings-adjusted +| INGREDIENT | SERVING | +|-------------------------+---------| +| Almonds | 2 | +| Asparagus | 2 | +| Bacon | 1 | +| Banana | 1 | +| Banana Protein Smoothie | 1 | +| Berries | 3 | +| Broccoli | 1 | +| Brown Rice | 2 | +| Brussels Sprouts | 1 | +| Chicken | 3 | +| Cod | 1 | +| Cottage Cheese | 2 | +| Courgette | 1 | +| Eggs | 4 | +| Fruit | 3 | +| Granola | 1 | +| Greek Yogurt | 3 | +| Green Beans | 2 | +| Porridge | 2 | +| Prawns | 2 | +| Protein Powder | 6 | +| Quinoa | 4 | +| Salad | 3 | +| Salmon | 3 | +| Spinach | 2 | +| Steak | 2 | +| Sweet Potato | 2 | +| Tuna | 2 | +| Turkey | 3 | +| Vegetables | 5 | +| Walnuts | 2 | +| Wholemeal Crackers | 2 | +| Wholemeal Bread | 2 | +| Wholemeal Tortilla | 2 | + +#+begin_src lisp :var table=servings-adjusted :results output raw + (let ((filepath #P"working-data/meal-planner-servings-adjusted.csv")) + (with-open-file (stream filepath :direction :output :if-exists :supersede) + (format stream "INGREDIENT,SERVING~%") + (dolist (row table) + (format stream "~{~a~^,~}~%" row))) + (format t "[[file:./~a]]" filepath)) +#+end_src + +#+RESULTS: +[[file:./working-data/meal-planner-servings-adjusted.csv]] + +#+begin_src lisp :session + (lisp-stat:defdf *ingredient-servings* + (lisp-stat:read-csv #P"working-data/meal-planner-servings-adjusted.csv")) +#+end_src + +#+RESULTS: +: # + +#+begin_src lisp :session :results output + (format t "~d servings of ~d ingredients." + (lisp-stat:sum *ingredient-servings*:serving) + (lisp-stat:length *ingredient-servings*:ingredient)) +#+end_src + +#+RESULTS: +: 78 servings of 34 ingredients. + +#+begin_src calc :results output + 78 / 7 +#+end_src + +#+RESULTS: +: 11.1428571429 + +I can't tell if 11 items a day is a lot or not (2024-03-04 Mon). + +#+begin_src lisp :session :results file + (vega:defplot ing-serv + `(:title "Weekly Ingredient Servings" + :description "Percentage of how much each ingredient is served in a week." + :data ,*ingredient-servings* + :width 600 + :height 300 + :mark (:type :bar) + :encoding (:x (:field :serving :title "No. of Servings in Week." :type :quantitative) + :color (:field :ingredient :type :nominal :title "Ingredient" :legend (:symbol-limit 100)) + :tooltip (:field :ingredient)))) + (vega:write-html ing-serv "renders/ingredient-servings.html") +#+end_src + +#+RESULTS: +[[file:renders/ingredient-servings.html]] + +#+begin_src shell :results silent + mv ~/Downloads/visualization.png ./renders/ingredient-servings.png +#+end_src + +[[file:./renders/ingredient-servings.png]] + +The chart above shows the diet is fairly well balanced. With that said, the +chicken and vegetable servings are noticeably bigger than the others – followed +by eggs. + +* Ingredients Availability + I'm going to use ~csvlook~ to output the data into a org-mode table, which I will then manipulate manually. The ~csvlook~ snippet is to just get me started. @@ -218,47 +332,282 @@ then manipulate manually. The ~csvlook~ snippet is to just get me started. csvlook working-data/meal-planner-servings.csv #+end_src -- Merged ~Protein Powder~, ~Protein Shake~ and ~Protein Smoothie~ into one ingredient. +- Merged ~Scoop Protein Powder~, ~Protein Powder~, ~Protein Shake~ and ~Protein + Smoothie~ into one ingredient. +- I replaced ~Whole Grain Toast~ with ~Wholemeal Bread~, I think this is a UK + branding issue (I’ve only ever seen ’wholemeal’, never ’wholegrain’). +- Replaced ~Whole Grain Tortilla~ with ~Wholemeal Tortilla~, for the same reason + as the bread. - ~0~ = ~False~ (i.e. Not sold by that supermarket) - ~1~ = ~True~ +- Used =C-c +=, =C-y= to provide the column totals (instead of using formulas). + +#+NAME: food-availability +| INGREDIENT | TESCO | SAINSBURYS | MARKS-AND-SPENCER | ASDA | CO-OP | WAITROSE | BOOTHS | MORRISONS | +|---------------------------+-------+------------+-------------------+------+-------+----------+--------+-----------| +| Almonds | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | +| Asparagus | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | +| Bacon | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | +| Banana | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | +| Berries | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | +| Broccoli | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | +| Brown Rice | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | +| Brussels Sprouts | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | +| Chicken | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | +| Cod | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | +| Cottage Cheese | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | +| Courgette | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | +| Eggs | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | +| Fruit | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | +| Granola | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | +| Greek Yogurt | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | +| Green Beans | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | +| Oatmeal (Porridge) | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | +| Prawns | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | +| Protein Powder | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | +| Quinoa | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | +| Salad (in mixed bag) | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | +| Salmon | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | +| Spinach | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | +| Steak | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | +| Sweet Potato | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | +| Tuna | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | +| Turkey | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | +| Vegetables (in mixed bag) | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | +| Walnuts | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | +| Wholegrain Crackers | 1 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | +| Wholemeal Bread | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | +| Wholemeal Tortilla | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | +|---------------------------+-------+------------+-------------------+------+-------+----------+--------+-----------| +| Total | 33 | 32 | 0 | 32 | 23 | 31 | 0 | 32 | + +*Looks like Tesco is the only one selling all the ingredients.* Sainsburys, Asda +and Morrisons are viable alternatives. + +Marks and Spencer uses [[https://www.ocado.com/][Ocado]] to provide it online shopping service. Having a +quick look on their website, I noticed it say there is a ’Minimum spend of +£40’. For now, I'm going to not include them in the availability table +above. The reason why is because their prices seem more expensive than the +others in the table. If the totals for the shops listed above are above £40, +I'll come back to Ocado and see how much their prices differ. + +Booths just seems to sell recipes or fully prepared /stuff/ from what I can +gather on their website… and lots of wine. + +* Ingredients Quantity Breakdown + +I expect I will need to use these values as base values and come back to this +file after I've got an idea about what quantities are good and what need +changing. + +- ~0.25~ like values refer to using 1/4 of a pack, of said item (i.e. 0.25 of a + packet of nuts). +- ~CHICKEN~ and ~TURKEY~ like ingredients refer to chicken breasts, I don’t like + cooking whole chickens. +- ~EGGS~ refers to the number of eggs in the serving, not the number of + boxes/packs. + +#+NAME: ingredient-quantities +| DAY | ALMONDS | ASPARAGUS | BACON | BANANA | BERRIES | BROCCOLI | BROWN RICE | BRUSSELS SPROUTS | CHICKEN | COD | COTTAGE CHEESE | COURGETTE | EGGS | FRUIT | GRANOLA | GREEK YOGURT | GREEN BEANS | OATMEAL (PORRIDGE) | PRAWNS | PROTEIN POWDER | QUINOA | SALAD (IN MIXED BAG) | SALMON | SPINACH | STEAK | SWEET POTATO | TUNA | TURKEY | VEGETABLES (IN MIXED BAG) | WALNUTS | WHOLEGRAIN CRACKERS | WHOLEMEAL BREAD (TOAST) | WHOLEMEAL TORTILLA | +|-----------+---------+-----------+-------+--------+---------+----------+------------+------------------+---------+-----+----------------+-----------+------+-------+---------+--------------+-------------+--------------------+--------+----------------+--------+----------------------+--------+---------+-------+--------------+------+--------+---------------------------+---------+---------------------+-------------------------+--------------------| +| Monday | 0.25 | 0 | 0 | 0 | 0 | 0.25 | 0 | 0 | 2 | 0 | 0 | 0 | 2 | 0 | 0 | 0.25 | 0 | 0 | 0 | 0 | 0.25 | 0.25 | 1 | 0 | 1 | 0 | 0 | 0 | 0.25 | 0 | 0 | 2 | 0 | +| Tuesday | 0 | 0.25 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0.25 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0.1 | 0 | 0.25 | 1 | 0.25 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 2 | 0 | +| Wednesday | 0 | 0 | 0 | 0 | 0 | 0 | 0.5 | 0 | 0 | 0 | 0 | 0 | 2 | 0 | 0 | 0 | 0.25 | 0.2 | 0.5 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 2 | 0.25 | 0.25 | 0 | 0 | 2 | +| Thursday | 0 | 0 | 0 | 0 | 0.25 | 0 | 0 | 0 | 2 | 1 | 0 | 0.5 | 0 | 1 | 0.25 | 0.25 | 0 | 0 | 0 | 0.1 | 0.25 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.25 | 0 | 0 | 0 | 0 | +| Friday | 0.25 | 0 | 4 | 0 | 0 | 0 | 0 | 0.25 | 2 | 0 | 0 | 0 | 2 | 0 | 0 | 0.25 | 0 | 0 | 0 | 0 | 0.25 | 0.25 | 0 | 0 | 1 | 1 | 0 | 2 | 0.25 | 0 | 0 | 2 | 0 | +| Saturday | 0 | 0.25 | 0 | 1 | 0 | 0 | 0.5 | 0 | 0 | 0 | 0.25 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0.1 | 0 | 0.25 | 1 | 0.25 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | +| Sunday | 0 | 0 | 0 | 0 | 0.25 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 2 | 0 | 0 | 0 | 0.25 | 0.2 | 0.5 | 0.1 | 0.25 | 0 | 0 | 0 | 0 | 0 | 0 | 2 | 0.25 | 0.25 | 0 | 0 | 2 | +|-----------+---------+-----------+-------+--------+---------+----------+------------+------------------+---------+-----+----------------+-----------+------+-------+---------+--------------+-------------+--------------------+--------+----------------+--------+----------------------+--------+---------+-------+--------------+------+--------+---------------------------+---------+---------------------+-------------------------+--------------------| +| Total | 0.5 | 0.5 | 4 | 2 | 0.5 | 0.25 | 1. | 0.25 | 6 | 1 | 0.5 | 0.5 | 8 | 3 | 0.25 | 0.75 | 0.5 | 0.4 | 1. | 0.4 | 1. | 1. | 3 | 0.5 | 2 | 2 | 2 | 6 | 1.25 | 0.5 | 2 | 6 | 4 | +#+TBLFM: @>$2..$34=vsum(@I..@II) + +#+begin_quote +*2024-03-04 Mon:* Going to write this file to disk and work with the CSV file +going forward. This table is a bit unwieldy in this ORG file. *I've kept this +table for reference. Do not expect it to be accurate as I will not update it.* +All updates will be in the CSV file moving forward. +#+end_quote + +#+begin_src lisp :var table=ingredient-quantities :results output raw + (let ((filepath #P"working-data/meal-planner-quantities.csv")) + (with-open-file (stream filepath :direction :output :if-exists :supersede) + (dolist (row table) + (format stream "~{~a~^,~}~%" row))) + (format t "[[file:./~a]]" filepath)) +#+end_src + +#+RESULTS: +[[file:./working-data/meal-planner-quantities.csv]] + +I need to transform the =meal-planner-quantities.csv= file so I can work out how +many of each item I need to buy. For example, the ~almonds~ total is ~0.5~ (at +time of writing), this means the nearest package quantity is ~1~. I can do it +using the file above, but I can transform it and build a template, of sorts, so +I can quickly run through various shopping websites and compare prices (if I +need to). + +#+begin_src shell :results output raw + # Input CSV file path + input_csv_file="working-data/meal-planner-quantities.csv" + # Output CSV file path + output_csv_file="working-data/meal-planner-quantities-adj.csv" + # Extract header (first row) and footer (last row) + header=$(head -n 1 "$input_csv_file") + footer=$(tail -n 1 "$input_csv_file") + # Split header and footer into arrays + IFS=',' read -ra header_array <<< "$header" + IFS=',' read -ra footer_array <<< "$footer" + # Create a new CSV file with rows containing header and footer entries + echo "INGREDIENT,QUANTITY" > "$output_csv_file" + for ((i = 0; i < ${#header_array[@]}; i++)); do + echo "${header_array[i]},${footer_array[i]}" >> "$output_csv_file" + done + # Remove the 'Total' and 'Day' parts from the CSV file. + sed -i '2d' "$output_csv_file" + # Print out link to the CSV file. + echo "[[file:./$output_csv_file]]" +#+end_src + +#+RESULTS: +[[file:./working-data/meal-planner-quantities-adj.csv]] + +Having transformed the data, you should see something along the lines of the +table below. + +#+begin_src shell :results output raw + # The output will need minor adjustments to its formatting, for org-mode. + head -n 4 working-data/meal-planner-quantities-adj.csv | csvlook +#+end_src + +#+RESULTS: +| INGREDIENT | QUANTITY | +|------------+----------| +| ALMONDS | 0.5 | +| ASPARAGUS | 0.5 | +| BACON | 4.0 | + +* Explore Data and Total Costs for Tesco + +I'm going to build out the CSV file outside of this file. The reason why is it +requires quite a number of function cells to calculate totals, and quite +frankly, using org-mode table-formulas can get unwieldy quickly. + +[[file:./working-data/meal-planner-totals-tesco.csv]] + +#+begin_src shell :results output raw + csvlook working-data/meal-planner-totals-tesco.csv +#+end_src + +The table below was derived from =working-data/meal-planner-totals-tesco.csv=. I +used ~csvlook~ to output the table and I've worked on it since then. + +#+RESULTS: +| INGREDIENT | QUANTITY | PACKET-QUANTITY | TOTAL-PACKETS | PRICE-PER-PACKET | ITEM-TOTAL | +|---------------------------+----------+-----------------+---------------+------------------+------------| +| ALMONDS | 0.50 | 250 g | 1 | 3.1 | 3.10 | +| ASPARAGUS | 0.50 | 180 g | 1 | 1.6 | 1.60 | +| BACON | 4.00 | 10 | 1 | 2.25 | 2.25 | +| BANANA | 2.00 | 5 | 1 | 0.78 | 0.78 | +| BERRIES | 0.50 | 200 g | 1 | 2.75 | 2.75 | +| BROCCOLI | 0.25 | 1 | 1 | 0.82 | 0.82 | +| BROWN RICE | 1.00 | 1 kg | 1 | 1.85 | 1.85 | +| BRUSSELS SPROUTS | 0.25 | 300 g | 1 | 0.58 | 0.58 | +| CHICKEN | 6.00 | 2 | 3 | 2.6 | 7.80 | +| COD | 1.00 | 2 | 1 | 5.6 | 5.60 | +| COTTAGE CHEESE | 0.50 | 300 g | 1 | 1.55 | 1.55 | +| COURGETTE | 0.50 | 1 | 1 | 0.56 | 0.56 | +| EGGS | 8.00 | 10 | 1 | 2.9 | 2.90 | +| FRUIT | 3.00 | 6 | 1 | 0.95 | 0.95 | +| GRANOLA | 0.25 | 1 kg | 1 | 2.5 | 2.50 | +| GREEK YOGURT | 0.75 | 500 g | 1 | 1.1 | 1.10 | +| GREEN BEANS | 0.50 | 220 g | 1 | 0.89 | 0.89 | +| OATMEAL (PORRIDGE) | 0.40 | 1 kg | 1 | 0.9 | 0.90 | +| PRAWNS | 1.00 | 250 g | 1 | 2.69 | 2.69 | +| PROTEIN POWDER | 0.40 | 500 g | 1 | 18 | 18.00 | +| QUINOA | 1.00 | 300 g | 1 | 2.8 | 2.80 | +| SALAD (IN MIXED BAG) | 1.00 | 90 g | 1 | 1.35 | 1.35 | +| SALMON | 3.00 | 4 | 1 | 4 | 4.00 | +| SPINACH | 0.50 | 250 g | 1 | 1.15 | 1.15 | +| STEAK | 2.00 | 2 | 1 | 4.15 | 4.15 | +| SWEET POTATO | 2.00 | 1 | 2 | 0.42 | 0.84 | +| TUNA | 2.00 | 1 | 2 | 0.55 | 1.10 | +| TURKEY | 6.00 | 3 | 2 | 4.5 | 9.00 | +| VEGETABLES (IN MIXED BAG) | 1.25 | 1 kg | 1 | 1.65 | 1.65 | +| WALNUTS | 0.50 | 200 g | 1 | 2.75 | 2.75 | +| WHOLEGRAIN CRACKERS | 2.00 | 160 g | 1 | 2 | 2.00 | +| WHOLEMEAL BREAD (TOAST) | 6.00 | 400 g | 1 | 1 | 1.00 | +| WHOLEMEAL TORTILLA | 4.00 | 8 | 1 | 1.4 | 1.40 | +|---------------------------+----------+-----------------+---------------+------------------+------------| +| | | | | TOTAL | 92.36 | + +£92.36 is way too high for my liking. £92.36 to feed a single person? I know +it's 2024 in the UK but that seems excessive. + +#+begin_src calc :results output + 92.36 / 7 +#+end_src + +#+RESULTS: +: 13.1942857143 + +Saying that, £13.19/day, on food, doesn't seem that unrealistic. On top of that, +the £13.19/day covers all three meals. Looks like I haven't been paying +attention, or I’m naturally finding ways to keep my food costs down. + +#+begin_src calc :results output + 92.36 - 18 +#+end_src + +#+RESULTS: +: 74.36 + +Taking away the protein powder (£18 is the cheapest) leaves the total still to +high. The quantities the protein powder was listed at suggests this is not a +weekly item. On top of that, there are quite a few items which will produce +excess waste at the end of the week, if sticking to the meal planner. +So, maybe+ ++the next step is to work out how much is weekly, fortnightly and monthly?+ The +quantities are accounted for on a weekly basis. The amount of waste isn't +enough to carry me into the next week. + +#+begin_src calc :results output + 74.36 / 7 +#+end_src + +#+RESULTS: +: 10.6228571429 + +Removing the protein powder from the day spent, brings it down to +£10.62/day. Because of how likely I would be buying protein powder, maybe this +is a more realistic figure than the £13.92 one above? + +#+begin_src calc :results output + 92.36 * 4 +#+end_src + +#+RESULTS: +: 369.44 + +The total spend on food for the month (including weekly protein powder) is going +to be around £369.44/month, give-or-take a few days for the uneven amount of +days in each month. + +#+begin_src calc :results output + 74.36 * 4 +#+end_src + +#+RESULTS: +: 297.44 + +The monthly spend without the weekly protein powder is £297.44, give-or-take a +few days. I would still need to buy at least one protein powder a month, +though. + +#+begin_src calc :results output + 297.44 + 18 +#+end_src + +#+RESULTS: +: 315.44 -#+NAME: food-servings -| SERVING | INGREDIENT | QUANTITY | TESCO | SAINSBURYS | MARKS-AND-SPENCER | ASDA | CO-OP | WAITROSE | -|---------+-------------------------+----------+-------+------------+-------------------+------+-------+----------| -| 2 | Almonds | | | | | | | | -| 2 | Asparagus | | | | | | | | -| 1 | Bacon | | | | | | | | -| 1 | Banana | | | | | | | | -| 1 | Banana Protein Smoothie | | | | | | | | -| 3 | Berries | | | | | | | | -| 1 | Broccoli | | | | | | | | -| 2 | Brown Rice | | | | | | | | -| 1 | Brussels Sprouts | | | | | | | | -| 3 | Chicken | | | | | | | | -| 1 | Cod | | | | | | | | -| 2 | Cottage Cheese | | | | | | | | -| 1 | Courgette | | | | | | | | -| 4 | Eggs | | | | | | | | -| 3 | Fruit | | | | | | | | -| 1 | Granola | | | | | | | | -| 3 | Greek Yogurt | | | | | | | | -| 2 | Green Beans | | | | | | | | -| 2 | Oatmeal | | | | | | | | -| 2 | Prawns | | | | | | | | -| 5 | Protein Powder | | | | | | | | -| 4 | Quinoa | | | | | | | | -| 3 | Salad | | | | | | | | -| 3 | Salmon | | | | | | | | -| 1 | Scoop Protein Powder | | | | | | | | -| 2 | Spinach | | | | | | | | -| 2 | Steak | | | | | | | | -| 2 | Sweet Potato | | | | | | | | -| 2 | Tuna | | | | | | | | -| 3 | Turkey | | | | | | | | -| 5 | Vegetables | | | | | | | | -| 2 | Walnuts | | | | | | | | -| 2 | Whole Grain Crackers | | | | | | | | -| 2 | Whole Grain Toast | | | | | | | | -| 2 | Whole Grain Tortilla | | | | | | | | -|---------+-------------------------+----------+-------+------------+-------------------+------+-------+----------| -| | | Total | | | | | | | +That brings the total (with monthly purchase of protein powder) to £315.44, +give-or-take a few days.