My Eddington number calculation started out in 2008 within my spreadsheet based training diary. The only realistic way to do it then was to have a list of the numbers I was tracking and a cell that calculated how many days I’d exceeded the number. It didn’t take much to calculate each number manually and from there the cell would be flagged if it was no longer the number. When only tracking basic numbers (e.g. miles for each of bike and run) this is not too cumbersome an approach.
I soon wanted a better way to track them and to achieve this I moved my training diary in to a proper database. This allowed me to script the generation of the numbers. I tried loads of different ways of calculating them but broadly they were either by creating table joins or by scripting. The former just proved too slow so I only really used the script method. I went through several iterations before I came up with a DB design that helped me do this. The broad design was:
- I had the concept of a Period – this could be day, week, month, rolling week etc… As I added more days to the diary I just calculated each of these periods
- I then ‘flattened’ all the data I wanted to perform Eddington calculations on. This was another script to put the data in a generic form so that I could have one script to calculate any Eddington number. Each record had the following fields; Date, Sport, SubType, Period, Unit, Value. This meant I could filter this table for I wanted and perform the calculation on this in a generice method.
This table got quite big. When you think for a single day it would create hundreds of rows. If you think just for a bike workout there would not only be a row for each of the days miles, kms, heart rate, ascent, duration etc… but also for the rolling week to that point, rolling month, year, etc… This ramped up further when you think of KM vs Miles, Feet vs Metres, Mins vs Hours not to mention separating out for different bikes. currently 670,033 records in there. I called it “Base Data”
The big problem with this was ensuring the integrity of the “Base Data” and the Eddington number history. I could regenerate from the underlying data but it was very time consuming so I couldn’t do it on demand.
In the summer of 2017 I decided to teach myself SWIFT and as a project I would move my training diary. I knew with proper code, rather than script, I could properly generalise the calculation and make it so that it was quick enough to recalc on demand.
The first cut took just mirrored the approach I’d taken in the DB. [I find it funny to think how long it took to switch to thinking in relational DB terms from thinking in object oriented code and then when I went back I kept thinking in DB relational rather than OO. Anyway…]. So I set up to create Base Data generalised across my data. The first run overnight and I woke to find my computer completely stalled with an error message “Run out of memory” – it has 16gb of RAM !! A back of the envelope calculation showed me I was created 4+ million records – that together with a little memory leak for each one and it didn’t work.
I rethink and came up with a much better approach. A ‘quick calc’ which works on arrays of integers and is lightening quick – more time is taken creating the array from my data model. Still it’s quick at under a second per calculation. I also have a detailed calculation which creates a full history, all annual numbers and all contributors for ay given number. This takes slightly longer but I don’t run that across all numbers just those I’ve picked out of interest. This latter produces a nice history graph:
This is my bike miles Eddington Number.
- Yellow dots – Annual Eddington Number
- Red line – Life to date Eddington Number
- Blue Line – the +1 count – it maps how many more required for the next number. Its presented relative to the red line
- Green dots – rides contributing to the current eddington number
- Light Blue line – Maturity – a measure I’m working on. I graph it to see how smoothly it evolves. The lack of smoothness I’m coming to conclude isn’t actually unreasonable.
Current set up
I used a code that is unique per Eddington Number. The code is of the form:
The first four of those could be specific or “All” which means all values are included. Equipment and Type are dependent on the Activity. Units in general aren’t dependent on the activity other than the fact that some units are not activity related at all. Eg sleep, motivation, KG, restingHR. As such units are either “Activity Based” are “Day Based” – the latter are only included if the Activity is “All”.
Here are they full set of valid values for each:
DayType – number of values: 30
These are broadly split in two. The types that are input in to my diary to describe day:
All, Camp, Holiday, Ill, Injured, Niggle, Normal, Race, Recovery, Rest, Travel,
Types that are only used for Eddington numbers to produce some “interesting numbers”
Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
January, February, March, April, May, June, July, August, September, October, November, December.
Activity – numbers of values: 7
All, Bike, Gym, Run, Swim, Other, Walk
Equipment – number of values: 16
In my app you can add equipment for any activity type but at the moment I purely use this for bikes.
All + 15 old and current bikes
ActivityType – number of values: 20
In theory if the Activity is ALL then types across all are valid. I decided just to hard code that if the activity is ALL then the only type is All – this is something I’ll probably generalise just because it feels the right thing to do though the results I’ll get would only be duplicates of the specific activities except in the case where activity types are the same (e.g. Run:Road and Bike:Road)
All – All
Bike – Commute, OffRoad, Road, Turbo
Gym – General, PressUp
Run – Fell, OffRoad, Road, Track, Treadmill
Swim – OpenWater, Solo, Squad
Other – Aquajog, Kayaking, Stepper [note this is a throw back to DB – I will move these to activities in their own right]
Walk – Fell, OffRoad
Period – number of values: 22
I have standard periods plus “to date” periods and rolling periods. For all the weekly ones I also look at them starting each day of the week.
Std Periods: Day, Week, Month, Year
Rolling Periods: rWeek, rMonth, rYear
To Day Periods: WTD, MTD, YTD
The above week periods all assume first day of Monday. I also have:
Std Period: Wk-Tue, Wk-Wed, Wk-Thu, Wk-Fri, Wk-Sat, Wk-Sun
To date: WTD-Tue, WTD-Wed, WTD-Thu, WTD-Fri, WTD-Sat, WTD-Sun
All these periods are available for DayType “ALL” but for all other day types I only calculate Day as it makes no sense to have the others if I’m restricted to a single day of the week.
Unit – number of values: 29
Clearly some units will be zero for an activity. Eg ascent for swim
Activity Units: AscentMetres,AscentFeet, Cadence, Hours, HR, KJ, KM, Miles, Minutes, Reps, RPETSS, Seconds, TSS, Watts
Metrics: ATL, CTL, TSB, Monotony, Strain
Day units: fatigue, fatPercent, kg, lbs, motivation, restingHR, restingSDNN, restingRMSSD, sleep, sleepMinutes
Metrics are calculated per day but depend on other days. In theory this could be calculated for any combination of type and equipment but I have decided to calculate only for Activities – so only with equipment and activityType as ALL. This is really an optimisation not only for speed but for size of the underlying database. It would be simple to generalise to include these at some future point.
You’ve probably got an inkling of how many Eddington numbers this results in. I can exclude certain combinations from the calculation. So for instance I don’t bother with Activity type Commute and OffRoad as there are so few workouts of this type the Eddington number is meaningless. The net result is I have 28,069 Eddington numbers that are tracked in my database. Currently a complete recalculation of them all takes a few hours.
I can pick out specific numbers to do a more detailed calculation which produces a full history – i.e. each day that the number or “+1” was incremented. This also pulls together each annual number and all the days that contributed. Given I have 15 years of data this means in theory I could calculate 15 x 28,069 annual numbers. Certainly means there’s no end of numbers to chase and almost always possible to find a number thats acheivable.