What is expreval and what do I use it for ?

Expreval gives you the possibility to access your data in an API-like fashion and process it with predifined functions. Resulting data is then formatted in a way that makes it easily importable into an analysis or plotting software such as MATLAB or Octave.

Types of access

There are 5 main types of access that you have to choose to begin with:

Activity
One single activity refered by its id. You can then process its streams of data or its properties.
Activities
All activities in a date range. You can then process their properties (but streams are not available).
PMC
Access daily TSS, CTL, ATL, TSB metrics in a date range.
Week_summaries
Access weekly summarized data (Monday through Sunday) such as activity count, summed duration, or total distance.
Metrics
Access metric data in a date range for the metric selected in the dropdown list.

Choosing none of the about gives you a basic access to the functions defines for all types.

Syntax

Expressions

In the field called expression, you can put any number of expressions with each one on its own line. Expression parsing is not sensitive to spaces.

Line ending with a semi-colon (;) will be evaluated but not produce any output. Everything after the semi-colon is considered a comment and not evaluated. On the contrary, lines without a semi-colon will have their output displayed in the output section.

Plots and charts

The functions plot_line and plot_scatter can be used to quickly visualize a data set. They expect a name which is the name of each series separated by underscores. One x series is to be provided while any number of y series can be used for that same x serie.

Quadrant analysis example below shows how to do this. Note that plotting can be relatively slow when a lot of points are to be handled. These functions are provided for quick debug but data export combined with plotting softwares should be prefered for more complex analysis.

User-defined functions

Users can define their own functions on a single line using a MATLAB-like inline definition. The definition is done similarly to the definition of a variable (name = ...) but with an @(...) listing the arguments after the equal sign. User-defined functions can then be used as any other functions, or by referencing to their name with an @. See for instance the loop example below.

Tokens

In your expressions, you can use tokens of the following types:

Numbers
Integers or fractional numbers with the dot (.) as decimal separator (eg 1.25).
Arrays
Arrays created by separating numbers with commas (,) and placing the whole inside squared brackets (eg [1, 2]).
Associative arrays
Arrays created by separating key and numbers pairs (using colon, :) with commas (,) and placing the whole inside curly brackets (eg {"CP":300,"Wbal":20e3}}).
Strings
Text characters enclosed inside double quotes (") (eg "String").
Operators
Math operators (addition +, substraction -, multiplication *, division /, power ^, modulo %) and comparison operators (greater >, smaller <, greater or equal >=, smaller or equal <=, equal ==, not equal !=).
Parenthesis
Parenthesis with matching left and right pairs (eg 1*(1+2)).
Constants
Math or PHP constants beginning with M_ or PHP_ (see http://php.net/manual/en/math.constants.php) (eg M_PI or M_E).
Functions
Functions with operands in parenthesis (see functions list below) (eg max([1, 2])). Can also be user defined as f=@(i,j) i+j.
Function references
Functions referenced to be used inside another function. Function name preceeded by @ and without argument (eg @max).

Functions

Available functions are:

Search:
NameSignatureAvailableDescriptionNotes
plot_lineArray plot_line(String chart_name, Array x_values, Array y_values)Allplots x_values vs y_values
plot_datelineArray plot_line(String chart_name, Array x_values, Array y_values)Allplots x_values (as dates) vs y_values
plot_scatterArray plot_scatter(String chart_name, Array x_values, Array y_values)Allplots x_values vs y_values
inputMixed input(String param)Allreturns value of the parameter selected at the inputParam: id | start (timestamp) | end (timestamp) | metric
saveArray save(String var_name, Mixed value)Allsaves the value allowing later recallssee also = operator
recallArray recall(String var_name)Allrecalls value saved by the save functionor simply write the variable name
roundArray round(Array number [, Number precision])Allrounds a number to the nearest integer or precision if given
ceilArray ceil(Array number)Allrounds up a number to the next integer
floorArray floor(Array number)Allrounds down a number to the previous integer
absArray abs(Array number)Allreturns the absolute value of a number
sinArray sin(Array angle_rad)Allreturns the sine of an angle in radian
cosArray cos(Array angle_rad)Allreturns the cosine of an angle in radian
tanArray tan(Array angle_rad)Allreturns the tangent of an angle in radian
asinArray asin(Array number)Allreturns the arc sine in radian
acosArray acos(Array number)Allreturns the arc cosine in radian
atanArray atan(Array number)Allreturns the arc tangent in radian
sinhArray sinh(Array number)Allreturns the hyperbolic sine
coshArray cosh(Array number)Allreturns the hyperbolic cosine
tanhArray tanh(Array number)Allreturns the hyperbolic tangent
asinhArray asinh(Array number)Allreturns the hyperbolic arc sine
acoshArray acosh(Array number)Allreturns the hyperbolic arc cosine
atanhArray atanh(Array number)Allreturns the hyperbolic arc tangent
deg2radArray deg2rad(Array deg)Allconverts degrees to radians
rad2degArray rad2deg(Array rad)Allconverts radians to degrees
expArray exp(Array number)Allreturns the exponential of a number
logArray log(Array number [, Number base])Allreturns the log of a number in base (default M_E corresponding to ln)
mean_maxArray mean_max(Array equallyspaced_stream, Array steps[, Number step])Allreturns mean maximal values of the equallyspaced_stream (with points spaced by step, or 1 second by default) at steps (negative values in stream are treated as zero)
max_filterArray max_filter(Array number, Number max)Allreturns the original array with values higher than max truncated at max
min_filterArray min_filter(Array number, Number min)Allreturns the original array with values lower than min truncated at min
sortArray sort(Array number)Allsorts an array (smallest to largest)
rsortArray rsort(Array number)Allreverse-sorts array (largest to smallest)
asortArray asort(Array number)Allsorts array keeping key-value relationships (smallest to largest values)
arsortArray arsort(Array number)Allreverse-sorts array keeping key-value relationships (largest to smallest values)display of associative array is always sorted (but for internal computations it will be reverse-sorted)
ksortArray ksort(Array number)Allsorts an array (smallest to largest keys)
krsortArray krsort(Array number)Allreverse-sorts array (largest to smallest keys)display of associative array is always sorted (but for internal computations it will be reverse-sorted)
maxNumber max(Array array)Allmax number in array
minNumber min(Array array)Allmin number in array
sumNumber sum(Array array)Allsum of numbers in array
countNumber count(Array array)Allnumber of elemets in array
medianNumber median(Array array)Allmedian of numbers in array
avgNumber avg(Array array)Allaverage of numbers in array
std_devNumber std_dev(Array array [, Number sample])Allstandard deviation of numbers in array (sample=1 to divide by N-1)
indexArray index(Array array, Mixed indexes)Allarray of the elements at indexes (associative index, positive from 0 to N-1, or negative from -N to -1)
selectArray select(Array array, Array boolean)Allarray of the elements with corresponding boolean to 1
array_diffArray array_diff(Array complete_set, Array exclude_set)Allreturns the array with elements whose value is in complete_set but not in exclude_set
array_diff_keyArray array_diff(Array complete_set, Array exclude_set)Allreturns the array with elements whose key is in complete_set but not in exclude_set
array_intersectArray array_intersect(Array set1, Array set2)Allreturns the array with elements whose value is in set1 and set2
array_intersect_keyArray array_intersect_key(Array set1, Array set2)Allreturns the array with elements whose key is in set1 and set2
array_reverseArray array_reverse(Array array)Allreturns the array with elements in reversed order
array_uniqueArray array_unique(Array array)Allreturns the array with duplicates removed
array_mergeArray array_merge(Array array1, Array array2)Allarray resulting of concatenating array1 and array2
array_combineArray array_combine(Array keys, Array values)Allassociative array from 2 non-associative arrays
array_keysArray array_keys(Array array)Allreturns the keys of an associative array
array_valuesArray unique(Array array)Allreturns the values of an associative array
array_flipArray unique(Array array)Allflipes an associative array (keys become values, and values become keys)
array_groupbyArray array_groupby(FuncRef @func, Array array)Allapplies function func to all keys in array and group those having the same result
array_reduceArray array_reduce(FuncRef @func, Array array)Allapplies reduce function to array values
array_mapArray array_map(FuncRef @func, Array params)Allapplies function func to all elements in params
delayArray delay(Array array, Number delay)Alldelay/shift array (positive towards the right, negative towards the left; padded with zeros)
integArray integ(Array array[, Number step])Allpartial integral from beginning up to index i-1 (ouput of length N+1; also known as cumsum)
diffArray diff(Array array[, Number step])Allfinite difference from beginning up to index i-1 (ouput of length N-1)
interp1Array sum(Array timepoints, Array known, Number step)Alllinear interpolation of knwon values (equally spaced by step) at defined timepoints
sma_filterArray sma_filter(Array array, Number n_step)Allsimple moving average of n_step on array (equally spaced)
expweighted_sma_filterArray expweighted_sma_filter(Array array, Number n_step, Number attenuation)Allexponentially-weighted simple moving average of n_step on array (equally spaced)
ema_filterArray ema_filter(Array array, Number n_step)Allexponential moving average of n_step on array (equally spaced)
smm_filterArray sma_filter(Array array, Number n_step)Allsimple moving median of n_step on array (equally spaced)
changepointsArray changepoints(Array stream, Number min_mean)Allchangepoints detection in stream
distributionArray distribution(Array stream, Number bin_size)Allsplits stream into distribution with bins of size bin_size
linrangeArray linrange(Number start, Number end [, Number step])Allgenerates array from start to stop with step (default +1/-1 depending on direction)see "range" implementing PHP range function. linrange kept for backward compatibility
rangeArray range(Number start, Number end [, Number step])Allgenerates array from start to stop with step (default +1/-1 depending on direction)
time_rangeArray time_range(Number start, Number end [, String step])Allgenerates array from start to stop timestamps with step (default +1/-1 day depending on direction)start and stop are timestamps, use strtotime if needed. step is everything accepted as first parameter of strtotime
onesArray ones(Number length)Allgenerates array of ones of a certain length
randArray rand([Number min, Number max [, Number array_length]])Allgenerates array of random values (defaults: 1 element between min=0 and max=1)
dateArray date(String format, Array timestamps)Allformats timestamp according to PHP date function
strtotimeArray strtotime(Array date_strings [, Array now])Allretrieve timestamps from date strings according to PHP strtotime function
ifMixed if(Mixed condition, Mixed if_true, Mixed if_false)Allchooses between if_true and if_false values depending on conditionwhen condition, if_true, if_false are arrays of same length, condition is evaluated on each element separately
configMixed config(String var_part1 [, String var_part2 [, String var_part3]])Allgets config propertyPossible calls: 1:alert_types|activity_types|BT_TSS_thresholds|metric_types|training_zones | 1:activity_types|metric_types => 2:<type> | 1:MMP_steps | 1:MMP_worldrecord => 2:men|women => 3:swim|bike|run | 1:BT_TSS_thresholds => 2:<threshold_name> | 1:training_zones => 2:<zone_name> => 3:FTP|HR|TRIMP | 1:alert_types => 2:<alert_name> => 3:threshold|short_desc|long_desc
PMC_constNumber PMC_const(String name)Allretrieves PMC constantConstant list: CTL_ema_tau | ATL_ema_tau
PD_model_parametersArray PD_model_parameters(String model, String activity_type, Array MMP_steps, Array MMP)Allretrieves PD parameters from a model for MMP values at MMP_steps in an activity of sport activity_typeModel list: Monod_2P | Monod_3P | Morton | Alvarez | WardSmith | Veloclinic | ExtendedCP
PD_model_valuesArray PD_model_values(String model, String activity_type, Array parameters, Array timepoints)Allretrieves NP values at timepoints from a PD model in an activity of sport activity_typeModel list: Monod_2P | Monod_3P | Morton | Alvarez | WardSmith | Veloclinic | ExtendedCP
activityNumber activity([String name])Activityretrieves activity propertyProperties list: title | type | subtype | race | metrics_origin_auto | metrics_origin | TSS | IF | VI | NP | AvgP | TRIMP | duration | moving_time | distance | elevation | timestamp | RPE | calories | HR_max | HR_avg | speed_max | speed_avg | description | comment | FTP | FTPower | Wprime | LTHR | athlete_height | athlete_weight | TSS_coeff
activity_streamArray activity_stream(String name[, Array times])Activityretrieves activity stream (optionally for defined timepoints only)Stream list (if available): time | distance | altitude | velocity_smooth | heartrate | cadence | watts | grade_smooth
alter_activity_streamArray alter_activity_stream(String name, Array timepoints, Array new_vals)Activityalters (already defined) activity streamStream list (if available): distance | altitude | velocity_smooth | heartrate | cadence | watts | grade_smooth
alter_new_activity_streamArray alter_new_activity_stream(String name, Array new_vals)Activitycreates non-existing activity streamStream list (if available): distance | altitude | velocity_smooth | heartrate | cadence | watts | grade_smooth
equallyspaced_streamsArray equallyspaced_streams(String stream, Number step [, Number start [, Number end [, String x_stream]]])Activityretrieves equally spaced stream by step from start to end for x_stream (default is time with step in seconds, set start=0 and end=0 to take full stream)
activity_lapsArray activity_laps()Activityretrieves activity laps (start time)
alter_activity_lapsArray alter_activity_laps(Array new_starts)Activityalters activity laps with new start times
NP_on_intervalArray NP_on_interval(Number start_time, Number, end_time)Activityretrieves metrics on intervalreturned array contains [NP, IF, AvgP, VI]
NP_from_powerArray NP_from_power(Array power)Activityconverts power to NP units (power or pace)
power_from_NPArray power_from_NP(Array NP)Activityconverts NP units (power or pace) to power
power_from_speedArray power_from_speed(Array speed [, Array slope [, Array distance [, Array inital_speed]]])Activityestimates power from speed and slope
activityArray activity([String name])Activitiesretrieves activity propertyProperties list: id | title | type | subtype | race | metric_origin_auto | metrics_origin | TSS | IF | VI | NP | AvgP | TRIMP | focus_thresholds | aTISS | anTISS | Wexp | lIW | hIW | pIW | CdA | duration | moving_time | distance | elevation | timestamp | RPE | calories | HR_max | HR_avg | speed_max | speed_avg | description | comment
daily_sumArray activity(String name[, String equals])Activitiesretrieves daily sum of property (count of activities equal if name = type | subtype)Properties list: type | subtype | TSS | IF | VI | NP | AvgP | TRIMP | duration | moving_time | distance | elevation | RPE | calories | HR_max | HR_avg | speed_max | speed_avg
PMCArray PMC(String name)PMCretrieves PMC dataProperties list: day | raceday | TSS | ATL | CTL | TSB | TMonotony | TStrain | TSS_<type> | ATL_<type> | CTL_<type> | TSB_<type> | TMonotony_<type> | TStrain_<type>
week_summaryArray week_summary(String name)Week_summariesretrieves week_summary dataProperties list: monday | sunday | count | duration | distance | count_<type> | duration_<type> | distance_<type> | MMP_<type>
metricArray metric(String name)Metricsretrieves metric dataProperties list: timestamp | value

Examples

Example 1: TSS from activity data

Note: This results to slightly different values than the reported ones as it does not account for non-moving time. It also calls power_fom_speed for all seconds and not at the original timepoints. Nevertheless, values are very close in practice.

vel = smm_filter(ema_filter(smm_filter(equallyspaced_streams("velocity_smooth", 1), 50), 30), 50); save filtered velocity
vel_del = delay(vel, 1); delay velocity for acceleration and distance in power_from_speed
grade = smm_filter(ema_filter(smm_filter(equallyspaced_streams("grade_smooth", 1) / 100, 50), 90), 50); save filtered grade (note: convert percents by dividing by 100)
power = power_from_speed(vel, grade, vel_del, vel_del); estimate power
NP = avg(min_filter(sma_filter(power, 30), 0)^4)^(1/4)
IF = NP/athlete("FTPower")
TSS = IF^2 * activity("moving_time") / 3600 * activity("TSS_coeff")

Example 2: Performance Management Chart

TSS = PMC("TSS");
ATL = ema_filter(TSS, PMC_const("ATL_ema_tau"))
CTL = ema_filter(TSS, PMC_const("CTL_ema_tau"))
TSB = CTL - ATL

Example 3: Week Duration Analysis

durations = week_summary("duration") / 3600; conversion from seconds to hours
avg_dur = avg(durations)
median_dur = median(durations)
stddev_dur = std_dev(durations)

Example 4: Change laps according to changepoint detection

watts = equallyspaced_streams("watts", 1); retrieve watts
changes = changepoints(watts, avg(watts)); find changepoints
; apply changes (!WARNING! this will change laps data without any undo possible)
alter_activity_laps(changes)

Example 5: Extract FTP from an activity

MMP = activity("MMP"); retrieve activity MMP
MMP_steps = index(config("MMP_steps"), linrange(0, count(MMP) - 1)); find MMP steps and crop to actual length
PD_model_parameters("Morton", "bike", MMP_steps, MMP)

Example 6: Quadrant analysis

sel = activity_stream("cadence") > 0;
CPV = select(activity_stream("cadence"), sel) / 60 * 0.1725 * 2 * M_PI; Circumferential Pedal Velocity (CPV, m/s)
AEPF = select(activity_stream("watts"), sel) / CPV; Average Effective Pedal Force (AEPF, N)

plot_scatter("CPV_AEPF", CPV, AEPF)

Note: Multiple y series would look like: plot_scatter("x_y1_y2", x, [y1, y2]).

Example 7: User-defined functions and loops

f = @(i,j) i+2*j;
; f(i=1,j=2)=1+2*2=5
f(1,2)
; loop over pairs [(i=1,j=2), (i=10,j=20)], f(i,j) = [5, 50]
array_map(@f, [[1,10], [2,20]])

Example 8: Monthly duration

Select activities and enter a date range (eg Start: 01-01-2014, End: 12-12-2016)
time_array = time_range(input("start"), input("end")); get input time range (1 point per day)
duration_array = daily_sum("duration")/3600; daily sum (in seconds, converted to hours)

; define group and reduce functions (see comments below)
group_func = @(t) date("Ym",t); group function: Year+month => grouped per month
reduc_func = @(a) sum(a); reduce function: sum => total duration in the period (eg month)

; group and reduce (see comments below)
period_sum = array_reduce(@reduc_func, array_groupby(@group_func, array_combine(time_array, duration_array)));

plot_line("periodIdx_duration", range(1,count(period_sum)), array_values(period_sum))

The most complicated looking line of code performs the following functions in order:

  1. array_combine: creates an array looking like {timestamp: duration}={1478896495:2.3444,...} with one pair for each day
  2. array_groupby: groups all that are in the same month transforming it to {"201601":[2.3444,3.45,...],"201602":[5.3,0,...],...}
  3. array_reduce: sums all sub-array that correspond to the same month, finally getting {"201601": 15.2, "201602": 23.335, ...}

Other variations of this can include:

  1. group_func = @(t) date("Y",t); sum over periods of years
  2. group_func = @(t) date("N",t); sum all same days of the week (find out which day of the week you are training the most, use ksort probably before plotting)
  3. reduc_func = @(a) avg(a)*7; make an average per week instead of a sum
  4. ... or combinations of those ... or many others of your imagination ...

Example 9: PMC with TSS=IF^4*duration

One can show mathematically that he TSS as defined by Coggan originally is not additive, by which it is meant that cutting your activity in 2 will lead to a different inpact on your PMC than leaving it as one (TSS(activity1+activity2) /= TSS(activity1)+TSS(activity2)). A short proof by Daniel even ended in the GoldenCheetah github repository after a discussion with Mark Liversedge (responsible of GoldenCheetah). Some people refer to it as "free TSS while coasting" or have complicated rules regarding when you should cut activities or not (look for burrito rule on the wattage forum if interested). One other approach is to look at what it would take to make the TSS additive. It is simple to show that it can be made by actually taking the same exponent for the IF in the TSS formula as the one used for the power in the NP formula.

For an exponent of 1, one gets simply that the NP is equal to the average power and the TSS is proportional to the expended energy. If you think however that the exponent 4 of the NP formula correctly models the body stresses and still want additivity, you would go to an exponent of 4. Whether this makes sense or not is up your judgment and using the original TSS formula is perhaps easier to compare with litterature. But if you want to try and compare, use the script below (selecting activities and a date range)...

; compute TSS with different IF exponents
ifs4 = array_combine(activity("timestamp"), activity("IF") ^ 4 * activity("duration") / 3600 * 100);
ifs2 = array_combine(activity("timestamp"), activity("IF") ^ 2 * activity("duration") / 3600 * 100);

; combine with zeros for days that have no activities (assumes no activity at midnight)
time = time_range(input("start"), input("end"));
act4 = array_combine(array_merge(time, array_keys(ifs4)), array_merge(0 * time, ifs4));
act2 = array_combine(array_merge(time, array_keys(ifs2)), array_merge(0 * time, ifs2));

; sum over each days
group_func = @(t) date("Ymd",t);
reduc_func = @(a) sum(a);
TSS4 = array_values(array_reduce(@reduc_func, array_groupby(@group_func, act4)));
TSS2 = array_values(array_reduce(@reduc_func, array_groupby(@group_func, act2)));

; compute PMC starting from TSS
ATL4 = ema_filter(TSS4, PMC_const("ATL_ema_tau"));
CTL4 = ema_filter(TSS4, PMC_const("CTL_ema_tau"));
TSB4 = CTL4 - ATL4;

ATL2 = ema_filter(TSS2, PMC_const("ATL_ema_tau"));
CTL2 = ema_filter(TSS2, PMC_const("CTL_ema_tau"));
TSB2 = CTL2 - ATL2;

; plots for comparison
plot_dateline("date_tss4_tss2", time * 1000, [TSS4, TSS2])
plot_dateline("date_atl4_ctl4_tsb4", time * 1000, [ATL4, CTL4, TSB4])

plot_dateline("date_atl2_ctl2_tsb2", time * 1000, [ATL2, CTL2, TSB2])

plot_dateline("date_tsb4_tsb2", time * 1000, [TSB4, TSB2])