From c55be018d75a0d996366bb5d3acfd62133698469 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Fri, 4 Aug 2017 11:14:45 +0200 Subject: [PATCH 1/5] added efficiency metric --- rowers/dataprep.py | 15 +++++++++++++++ rowers/metrics.py | 1 + rowers/models.py | 1 + rowers/views.py | 2 +- 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/rowers/dataprep.py b/rowers/dataprep.py index 5ce1a5d6..57f34316 100644 --- a/rowers/dataprep.py +++ b/rowers/dataprep.py @@ -210,6 +210,12 @@ def clean_df_stats(datadf,workstrokesonly=True,ignorehr=True, datadf.loc[mask,'pace'] = np.nan except KeyError: pass + + try: + mask = datadf['efficiency'] < 0. + datadf.loc[mask,'efficiency'] = np.nan + except KeyError: + pass try: mask = datadf['pace']/1000. < 60. @@ -1423,6 +1429,14 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True, totalangle = savgol_filter(totalangle,windowsize,3) effectiveangle = savgol_filter(effectiveangle,windowsize,3) + velo = 500./p + + ergpw = 2.8*velo**3 + efficiency = 100.*ergpw/power + + efficiency = efficiency.replace([-np.inf,np.inf],np.nan) + efficiency.fillna(method='ffill') + data['wash'] = wash data['catch'] = catch data['slip'] = slip @@ -1432,6 +1446,7 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True, data['drivelength'] = drivelength data['totalangle'] = totalangle data['effectiveangle'] = effectiveangle + data['efficiency'] = efficiency if otwpower: try: diff --git a/rowers/metrics.py b/rowers/metrics.py index 4fb00b02..c65cbcc0 100644 --- a/rowers/metrics.py +++ b/rowers/metrics.py @@ -22,6 +22,7 @@ axes = ( ('totalangle', 'Drive Length (deg)',40,140,'pro'), ('effectiveangle', 'Effective Drive Length (deg)',40,140,'pro'), ('rhythm', 'Stroke Rhythm (%)',20,55,'pro'), + ('efficiency', 'OTW efficiency (%)',0,110,'pro'), ('None', 'None',0,1,'basic'), ) diff --git a/rowers/models.py b/rowers/models.py index 9bb87132..e880ca24 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -536,6 +536,7 @@ class StrokeData(models.Model): rhythm = models.FloatField(default=1.0,null=True,verbose_name='Rhythm') totalangle = models.FloatField(default=0.0,null=True,verbose_name='Total Stroke Length (deg)') effectiveangle = models.FloatField(default=0.0,null=True,verbose_name='Effective Stroke Length (deg)') + efficiency = models.FloatField(default=-1,null=True,verbose_name='OTW Efficiency') # A wrapper around the png files class GraphImage(models.Model): diff --git a/rowers/views.py b/rowers/views.py index fc671f05..cab19bb5 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -5618,7 +5618,7 @@ def workout_flexchart3_view(request,*args,**kwargs): axchoicespro.pop('totalangle') axchoicespro.pop('effectiveangle') axchoicespro.pop('peakforceangle') - + axchoicespro.pop('efficiency') return render(request, 'flexchart3otw.html', From 2e4e6c47e026dcc249875accc41e84c51b7bbc50 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 7 Aug 2017 09:08:56 +0200 Subject: [PATCH 2/5] added add_effiency and eff calc added to otw_physics --- rowers/dataprep.py | 24 +++++++++++++++++- rowers/dataprepnodjango.py | 50 ++++++++++++++++++++++++++++++++------ 2 files changed, 65 insertions(+), 9 deletions(-) diff --git a/rowers/dataprep.py b/rowers/dataprep.py index 57f34316..cf61bbb5 100644 --- a/rowers/dataprep.py +++ b/rowers/dataprep.py @@ -460,7 +460,8 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower', if consistencychecks: a_messages.error(r.user,'Failed consistency check: '+key+', autocorrected') else: - a_messages.error(r.user,'Failed consistency check: '+key+', not corrected') + pass + # a_messages.error(r.user,'Failed consistency check: '+key+', not corrected') except ZeroDivisionError: pass @@ -1259,6 +1260,27 @@ def datafusion(id1,id2,columns,offset): return df +def add_efficiency(id=0): + rowdata,row = getrowdata_db(id=id,doclean=False) + power = rowdata['power'] + pace = rowdata['pace']/1.0e3 + velo = 500./pace + ergpw = 2.8*velo**3 + efficiency = 100.*ergpw/power + + efficiency = efficiency.replace([-np.inf,np.inf],np.nan) + efficiency.fillna(method='ffill') + rowdata['efficiency'] = efficiency + delete_strokedata(id) + if id != 0: + rowdata['workoutid'] = id + engine = create_engine(database_url, echo=False) + with engine.connect() as conn, conn.begin(): + rowdata.to_sql('strokedata',engine,if_exists='append',index=False) + conn.close() + engine.dispose() + return rowdata + # This is the main routine. # it reindexes, sorts, filters, and smooths the data, then # saves it to the stroke_data table in the database diff --git a/rowers/dataprepnodjango.py b/rowers/dataprepnodjango.py index 27cb5db7..baf32f87 100644 --- a/rowers/dataprepnodjango.py +++ b/rowers/dataprepnodjango.py @@ -14,16 +14,33 @@ import itertools from sqlalchemy import create_engine import sqlalchemy as sa -from rowsandall_app.settings import DATABASES +#from rowsandall_app.settings import DATABASES +from rowsandall_app.settings_dev import DATABASES from utils import lbstoN -user = DATABASES['default']['USER'] -password = DATABASES['default']['PASSWORD'] -database_name = DATABASES['default']['NAME'] -host = DATABASES['default']['HOST'] -port = DATABASES['default']['PORT'] +try: + user = DATABASES['default']['USER'] +except KeyError: + user = '' +try: + password = DATABASES['default']['PASSWORD'] +except KeyError: + password = '' + +try: + database_name = DATABASES['default']['NAME'] +except KeyError: + database_name = '' +try: + host = DATABASES['default']['HOST'] +except KeyError: + host = '' +try: + port = DATABASES['default']['PORT'] +except KeyError: + port = '' database_url = 'mysql://{user}:{password}@{host}:{port}/{database_name}'.format( user=user, @@ -563,6 +580,10 @@ def smalldataprep(therows,xparam,yparam1,yparam2): def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True, empower=True,debug=True): + + if rowdatadf.empty: + return 0 + rowdatadf.set_index([range(len(rowdatadf))],inplace=True) t = rowdatadf.ix[:,'TimeStamp (sec)'] t = pd.Series(t-rowdatadf.ix[0,'TimeStamp (sec)']) @@ -576,7 +597,6 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True, cumdist = rowdatadf.ix[:,'cum_dist'] power = rowdatadf.ix[:,' Power (watts)'] - averageforce = rowdatadf.ix[:,' AverageDriveForce (lbs)'] drivelength = rowdatadf.ix[:,' DriveLength (meters)'] try: @@ -590,7 +610,10 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True, forceratio = forceratio.fillna(value=0) f = rowdatadf['TimeStamp (sec)'].diff().mean() - windowsize = 2*(int(10./(f)))+1 + if f != 0: + windowsize = 2*(int(10./(f)))+1 + else: + windowsize = 1 if windowsize <= 3: windowsize = 5 @@ -674,6 +697,7 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True, peakforceangle = savgol_filter(peakforceangle,windowsize,3) driveenergy = savgol_filter(driveenergy,windowsize,3) drivelength = savgol_filter(drivelength,windowsize,3) + data['wash'] = wash data['catch'] = catch data['slip'] = slip @@ -703,11 +727,21 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True, ergpace[ergpace == np.inf] = 240. ergpace2 = ergpace.apply(lambda x: timedeltaconv(x)) + velo = 500./p + + ergpw = 2.8*velo**3 + efficiency = 100.*ergpw/power + + efficiency = efficiency.replace([-np.inf,np.inf],np.nan) + efficiency.fillna(method='ffill') + + data['ergpace'] = ergpace*1e3 data['nowindpace'] = nowindpace*1e3 data['equivergpower'] = equivergpower data['fergpace'] = nicepaceformat(ergpace2) data['fnowindpace'] = nicepaceformat(nowindpace2) + data['efficiency'] = efficiency data = data.replace([-np.inf,np.inf],np.nan) data = data.fillna(method='ffill') From de01ce53c1859b2a09bf6401840c9bf174f9ab30 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 7 Aug 2017 09:09:38 +0200 Subject: [PATCH 3/5] correct db in dataprepnodjango --- rowers/dataprepnodjango.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rowers/dataprepnodjango.py b/rowers/dataprepnodjango.py index baf32f87..e24580d5 100644 --- a/rowers/dataprepnodjango.py +++ b/rowers/dataprepnodjango.py @@ -14,8 +14,8 @@ import itertools from sqlalchemy import create_engine import sqlalchemy as sa -#from rowsandall_app.settings import DATABASES -from rowsandall_app.settings_dev import DATABASES +from rowsandall_app.settings import DATABASES +#from rowsandall_app.settings_dev import DATABASES from utils import lbstoN From bd4020d9a9f6102e45554fc49e5aace3e57794b4 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 7 Aug 2017 09:50:20 +0200 Subject: [PATCH 4/5] small changes --- rowers/dataprep.py | 3 +++ rowers/views.py | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/rowers/dataprep.py b/rowers/dataprep.py index cf61bbb5..f5456122 100644 --- a/rowers/dataprep.py +++ b/rowers/dataprep.py @@ -1001,6 +1001,9 @@ def getrowdata_db(id=0,doclean=False): else: row = Workout.objects.get(id=id) + if data['efficiency'].mean() == 0 and data['power'].mean() != 0: + data = add_efficiency(id=id) + if doclean: data = clean_df_stats(data,ignorehr=True) diff --git a/rowers/views.py b/rowers/views.py index cab19bb5..00ed647d 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -4031,7 +4031,6 @@ def workouts_view(request,message='',successmessage='', else: activity_enddate = enddate - print "aap",activity_enddate if teamid: try: From fc23750731ce9ec8777777ee69e5d5456f48ace3 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 8 Aug 2017 11:51:41 +0200 Subject: [PATCH 5/5] corrected work per stroke error (wasn't calculated when it could) --- rowers/dataprep.py | 7 ++- rowers/dataprepnodjango.py | 99 ++++++++++++++++++++++++++------------ rowers/views.py | 8 ++- 3 files changed, 81 insertions(+), 33 deletions(-) diff --git a/rowers/dataprep.py b/rowers/dataprep.py index f5456122..c1e4684e 100644 --- a/rowers/dataprep.py +++ b/rowers/dataprep.py @@ -1090,6 +1090,7 @@ def read_cols_df_sql(ids,columns): # drop columns that are not in offical list # axx = [ax[0] for ax in axes] axx = [f.name for f in StrokeData._meta.get_fields()] + for c in columns: if not c in axx: columns.remove(c) @@ -1118,10 +1119,12 @@ def read_cols_df_sql(ids,columns): ids = tuple(ids), )) + connection = engine.raw_connection() df = pd.read_sql_query(query,engine) - df = df.fillna(value=0) + df = df.fillna(value=0) + try: df['peakforce'] = df['peakforce']*lbstoN except KeyError: @@ -1425,7 +1428,7 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True, try: driveenergy = rowdatadf.ix[:,'driveenergy'] except KeyError: - driveenergy = 0*power + driveenergy = power*60/spm else: driveenergy = data['driveenergy'] diff --git a/rowers/dataprepnodjango.py b/rowers/dataprepnodjango.py index e24580d5..e985de1c 100644 --- a/rowers/dataprepnodjango.py +++ b/rowers/dataprepnodjango.py @@ -683,32 +683,76 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True, if empower: try: wash = rowdatadf.ix[:,'wash'] - catch = rowdatadf.ix[:,'catch'] - finish = rowdatadf.ix[:,'finish'] - peakforceangle = rowdatadf.ix[:,'peakforceangle'] - driveenergy = rowdatadf.ix[:,'driveenergy'] - drivelength = driveenergy/(averageforce*4.44822) - slip = rowdatadf.ix[:,'slip'] - if windowsize > 3: - wash = savgol_filter(wash,windowsize,3) - slip = savgol_filter(slip,windowsize,3) - catch = savgol_filter(catch,windowsize,3) - finish = savgol_filter(finish,windowsize,3) - peakforceangle = savgol_filter(peakforceangle,windowsize,3) - driveenergy = savgol_filter(driveenergy,windowsize,3) - drivelength = savgol_filter(drivelength,windowsize,3) - - data['wash'] = wash - data['catch'] = catch - data['slip'] = slip - data['finish'] = finish - data['peakforceangle'] = peakforceangle - data['driveenergy'] = driveenergy - data['drivelength'] = drivelength - data['peakforce'] = peakforce - data['averageforce'] = averageforce except KeyError: - pass + wash = 0*power + + try: + catch = rowdatadf.ix[:,'catch'] + except KeyError: + catch = 0*power + + try: + finish = rowdatadf.ix[:,'finish'] + except KeyError: + finish = 0*power + + try: + peakforceangle = rowdatadf.ix[:,'peakforceangle'] + except KeyError: + peakforceangle = 0*power + + + if data['driveenergy'].mean() == 0: + try: + driveenergy = rowdatadf.ix[:,'driveenergy'] + except KeyError: + driveenergy = power*60/spm + else: + driveenergy = data['driveenergy'] + + + arclength = (inboard-0.05)*(np.radians(finish)-np.radians(catch)) + if arclength.mean()>0: + drivelength = arclength + elif drivelength.mean() == 0: + drivelength = driveenergy/(averageforce*4.44822) + + try: + slip = rowdatadf.ix[:,'slip'] + except KeyError: + slip = 0*power + + totalangle = finish-catch + effectiveangle = finish-wash-catch-slip + if windowsize > 3 and windowsize