diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index d697eba6..94a4548b 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -403,6 +403,34 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False): if rowdata.empty: return "","No Valid Data Available","","" + + # quick linear regression + # peakforce = slope*peakforceangle + intercept + slope, intercept, r,p,stderr = linregress(rowdata['peakforceangle'],rowdata['peakforce']) + theta = np.arctan(slope) + + a = rowdata['peakforceangle']-rowdata['peakforceangle'].mean() + F = rowdata['peakforce']-rowdata['peakforce'].mean() + + R = np.array([[np.cos(theta),np.sin(theta)], + [-np.sin(theta),np.cos(theta)]]) + Rinv = np.array([[np.cos(theta),np.sin(theta)], + [-np.sin(theta),np.cos(theta)]]) + + x = R[0,0]*a+R[0,1]*F + y = R[1,0]*a+R[1,1]*F + + + x05 = x.quantile(q=0.05) + x25 = x.quantile(q=0.25) + x75 = x.quantile(q=0.75) + x95 = x.quantile(q=0.95) + + y05 = y.quantile(q=0.05) + y25 = y.quantile(q=0.25) + y75 = y.quantile(q=0.75) + y95 = y.quantile(q=0.95) + try: catchav = rowdata['catch'].mean() except KeyError: @@ -427,6 +455,7 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False): except KeyError: peakforceav = 0 + try: averageforceav = rowdata['averageforce'].mean() except KeyError: @@ -436,6 +465,19 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False): peakforceangleav = rowdata['peakforceangle'].mean() except KeyError: peakforceangleav = 0 + + peakforceangle05 = rowdata['peakforceangle'].quantile(q=0.05) + peakforce05 = rowdata['peakforce'].quantile(q=0.05) + + peakforceangle25 = rowdata['peakforceangle'].quantile(q=0.25) + peakforce25 = rowdata['peakforce'].quantile(q=0.25) + + peakforceangle75 = rowdata['peakforceangle'].quantile(q=0.75) + peakforce75 = rowdata['peakforce'].quantile(q=0.75) + + peakforceangle95 = rowdata['peakforceangle'].quantile(q=0.95) + peakforce95 = rowdata['peakforce'].quantile(q=0.95) + x = [catchav, catchav+slipav, @@ -455,11 +497,31 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False): y = y, )) + sourcetrend = ColumnDataSource( + data = dict( + x = [peakforceangle25,peakforceangle75], + y = [peakforce25,peakforce75] + ) + ) + + sourcefit = ColumnDataSource( + data = dict( + x = rowdata['peakforceangle'], + y = slope*rowdata['peakforceangle']+intercept + ) + ) source2 = ColumnDataSource( rowdata ) + sourcepoints = ColumnDataSource( + data = dict( + peakforceangle = rowdata['peakforceangle'], + peakforce = rowdata['peakforce'] + ) + ) + plot = Figure(tools=TOOLS, toolbar_sticky=False,toolbar_location="above") @@ -493,7 +555,10 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False): avf = Span(location=averageforceav,dimension='width',line_color='blue', line_dash=[6,6],line_width=2) + plot.circle('peakforceangle','peakforce',source=sourcepoints,color='cyan') plot.line('x','y',source=source,color="red") + plot.line('x','y',source=sourcetrend,color="blue") + plot.line('x','y',source=sourcefit,color="green") plot.add_layout(avf) @@ -585,6 +650,7 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False): callback = CustomJS(args = dict( source=source, source2=source2, + sourcepoints=sourcepoints, avf=avf, avflabel=avflabel, catchlabel=catchlabel, @@ -598,6 +664,7 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False): ), code=""" var data = source.data var data2 = source2.data + var datapoints = sourcepoints.data var x = data['x'] var y = data['y'] @@ -636,11 +703,15 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False): var peakforceav = 0 var count = 0 + datapoints['peakforceangle'] = [] + datapoints['peakforce'] = [] for (i=0; i=minspm && spm1[i]<=maxspm) { if (distance1[i]>=mindist && distance1[i]<=maxdist) { if (driveenergy1[i]>=minwork && driveenergy1[i]<=maxwork) { + datapoints['peakforceangle'].push(peakforceangle[i]) + datapoints['peakforce'].push(peakforce[i]) catchav += c[i] finishav += finish[i] slipav += slip[i] @@ -677,6 +748,7 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False): // source.trigger('change'); source.change.emit(); + sourcepoints.change.emit(); """) annotation = TextInput(title="Type your plot notes here", value="",