diff --git a/rowers/dataprep.py b/rowers/dataprep.py index 48f4556d..9f61de6f 100644 --- a/rowers/dataprep.py +++ b/rowers/dataprep.py @@ -56,7 +56,7 @@ import sys import utils import datautils -from utils import lbstoN,myqueue,is_ranking_piece +from utils import lbstoN,myqueue,is_ranking_piece,wavg from timezonefinder import TimezoneFinder @@ -2234,16 +2234,19 @@ def workout_rscore(w): df,row = getrowdata_db(id=w.id) df = clean_df_stats(df,workstrokesonly=False) + df['deltat'] = df['time'].diff() duration = df['time'].max()-df['time'].min() duration /= 1.0e3 - pwr4 = df['power']**(4.0) - normp = (pwr4.mean())**(0.25) + df['pwr4'] = df['power']**(4.0) + pwr4mean = wavg(df,'pwr4','deltat') + pwrmean = wavg(df,'power','deltat') + normp = (pwr4mean)**(0.25) if not np.isnan(normp): ftp = float(r.ftp) if w.workouttype in ('water','coastal'): ftp = ftp*(100.-r.otwslack)/100. - intensityfactor = df['power'].mean()/float(ftp) + intensityfactor = pwrmean/float(ftp) intensityfactor = normp/float(ftp) tss = 100.*((duration*normp*intensityfactor)/(3600.*ftp)) else: diff --git a/rowers/templates/workoutstats.html b/rowers/templates/workoutstats.html index e58f15a4..965a54f8 100644 --- a/rowers/templates/workoutstats.html +++ b/rowers/templates/workoutstats.html @@ -9,7 +9,9 @@

Workout Statistics for {{ workout.name }}

This is an experimental page which just lists a bunch of statistics for - your workout. This page is under rapid development. + your workout. The mean is of a metric is the mean with equal weight for + each stroke. The time weighted mean takes into account the stroke + duration.

@@ -55,6 +57,8 @@ Mean{{ value.mean|floatformat:-2 }} + + Time Weighted Mean {{ value.wmean|floatformat:-2 }} Minimum{{ value.min|floatformat:-2 }} diff --git a/rowers/utils.py b/rowers/utils.py index c10c873b..08f97d4b 100644 --- a/rowers/utils.py +++ b/rowers/utils.py @@ -319,3 +319,15 @@ def my_dict_from_instance(instance,model): thedict[fname] = (verbosename,value) return thedict + +def wavg(group, avg_name, weight_name): + """ http://stackoverflow.com/questions/10951341/pandas-dataframe-aggregate-function-using-multiple-columns + In rare instance, we may not have weights, so just return the mean. Customize this if your business case + should return otherwise. + """ + d = group[avg_name] + w = group[weight_name] + try: + return (d * w).sum() / w.sum() + except ZeroDivisionError: + return d.mean() diff --git a/rowers/views.py b/rowers/views.py index ad0fc39c..294c65ed 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -754,7 +754,7 @@ from utils import ( geo_distance,serialize_list,deserialize_list,uniqify, str2bool,range_to_color_hex,absolute,myqueue,get_call, calculate_age,rankingdistances,rankingdurations, - is_ranking_piece,my_dict_from_instance + is_ranking_piece,my_dict_from_instance,wavg ) import datautils @@ -7446,7 +7446,7 @@ def workout_stats_view(request,id=0,message="",successmessage=""): return HttpResponseRedirect(url) datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly) - + datadf['deltat'] = datadf['time'].diff() if datadf.empty: datadf,row = dataprep.getrowdata_db(id=id) @@ -7472,6 +7472,7 @@ def workout_stats_view(request,id=0,message="",successmessage=""): for field,verbosename in fielddict.iteritems(): thedict = { 'mean':datadf[field].mean(), + 'wmean': wavg(datadf, field, 'deltat'), 'min': datadf[field].min(), 'std': datadf[field].std(), 'max': datadf[field].max(),