diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index e2c01858..c04dd167 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -1177,440 +1177,6 @@ def interactive_agegroup_plot(df, distance=2000, duration=None, return script, div - -def interactive_cpchart(rower, thedistances, thesecs, theavpower, - theworkouts, promember=0, - wcpower=[], wcdurations=[]): - - message = 0 - # plot tools - if (promember == 1): - TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' - else: # pragma: no cover - TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' - - x_axis_type = 'log' - - thesecs = pd.Series(thesecs) - - velo = thedistances/thesecs - p = pd.Series(500./velo) - - p2 = p.fillna(method='ffill').apply(lambda x: timedeltaconv(x)) - - source = ColumnDataSource( - data=dict( - dist=thedistances, - duration=thesecs, - spm=0*theavpower, - tim=niceformat( - thesecs.fillna(method='ffill').apply( - lambda x: timedeltaconv(x)) - ), - - power=theavpower, - fpace=nicepaceformat(p2), - ) - ) - - # fitting the data to Paul - if len(thedistances) >= 2: - paulslope, paulintercept, r, p, stderr = linregress( - np.log10(thedistances), p) - else: # pragma: no cover - paulslope = 5.0/np.log10(2.0) - paulintercept = p[0]-paulslope*np.log10(thedistances[0]) - - fitx = pd.Series(np.arange(100)*2*max(np.log10(thedistances))/100.) - - fitp = paulslope*fitx+paulintercept - - fitvelo = 500./fitp - fitpower = 2.8*(fitvelo**3) - fitt = 10**fitx/fitvelo - fitp2 = fitp.fillna(method='ffill').apply(lambda x: timedeltaconv(x)) - - sourcepaul = ColumnDataSource( - data=dict( - dist=10**fitx, - duration=fitt, - power=fitpower, - spm=0*fitpower, - tim=niceformat( - fitt.fillna(method='ffill').apply(lambda x: timedeltaconv(x)) - ), - fpace=nicepaceformat(fitp2), - ) - ) - - def fitfunc(pars, x): - return pars[0] / (1+(x/pars[2])) + pars[1]/(1+(x/pars[3])) - - def errfunc(pars, x, y): - return fitfunc(pars, x)-y - - # p0 = [500,350,10,8000] - wcpower = pd.Series(wcpower, dtype='float') - wcdurations = pd.Series(wcdurations, dtype='float') - - # fitting WC data to three parameter CP model - if len(wcdurations) >= 4: - p1wc, success = optimize.leastsq(errfunc, p0[:], - args=(wcdurations, wcpower)) - else: # pragma: no cover - p1wc = None - - # fitting the data to three parameter CP model - - success = 0 - p1 = p0 - if len(thesecs) >= 4: - try: - p1, success = optimize.leastsq( - errfunc, p0[:], args=(thesecs, theavpower)) - except (RuntimeError, RuntimeWarning): # pragma: no cover - factor = fitfunc(p0, thesecs.mean())/theavpower.mean() - p1 = [p0[0]/factor, p0[1]/factor, p0[2], p0[3]] - success = 0 - else: # pragma: no cover - factor = fitfunc(p0, thesecs.mean())/theavpower.mean() - p1 = [p0[0]/factor, p0[1]/factor, p0[2], p0[3]] - success = 0 - - # Get stayer score - if success == 1: # pragma: no cover - power4min = fitfunc(p1, 240.) - power1h = fitfunc(p1, 3600.) - power10sec = fitfunc(p1, 10.) - r10sec4min = 100.*power10sec/power4min - r1h4min = 100.*power1h/power4min - - combined = r1h4min-0.2*(r10sec4min-100) - - dataset = pd.read_csv('static/stats/combined_set.csv') - - stayerscore = int(percentileofscore(dataset['combined'], combined)) - else: - stayerscore = None - - fitt = pd.Series(10**(4*np.arange(100)/100.)) - - fitpower = fitfunc(p1, fitt) - if p1wc is not None: - fitpowerwc = 0.95*fitfunc(p1wc, fitt) - fitpowerexcellent = 0.7*fitfunc(p1wc, fitt) - fitpowergood = 0.6*fitfunc(p1wc, fitt) - fitpowerfair = 0.5*fitfunc(p1wc, fitt) - fitpoweraverage = 0.4*fitfunc(p1wc, fitt) - - else: # pragma: no cover - fitpowerwc = 0*fitpower - fitpowerexcellent = 0*fitpower - fitpowergood = 0*fitpower - fitpowerfair = 0*fitpower - fitpoweraverage = 0*fitpower - - message = "" - if len(fitpower[fitpower < 0]) > 0: # pragma: no cover - message = "CP model fit didn't give correct results" - - fitvelo = (fitpower/2.8)**(1./3.) - fitdist = fitt*fitvelo - fitp = 500./fitvelo - fitp2 = fitp.fillna(method='ffill').apply(lambda x: timedeltaconv(x)) - - sourcecomplex = ColumnDataSource( - data=dict( - dist=fitdist, - duration=fitt, - tim=niceformat( - fitt.fillna(method='ffill').apply(lambda x: timedeltaconv(x)) - ), - spm=0*fitpower, - power=fitpower, - fitpowerwc=fitpowerwc, - fitpowerexcellent=fitpowerexcellent, - fitpowergood=fitpowergood, - fitpowerfair=fitpowerfair, - fitpoweraverage=fitpoweraverage, - fpace=nicepaceformat(fitp2), - ) - ) - - # making the plot - plot = figure(tools=TOOLS, x_axis_type=x_axis_type, - width=900, - toolbar_location="above", - toolbar_sticky=False) - - # add watermark - watermarkurl = "/static/img/logo7.png" - watermarkrange = Range1d(start=0, end=1) - watermarkalpha = 0.6 - watermarky = 0.01 - watermarkw = 184 - watermarkh = 35 - watermarkanchor = 'bottom_right' - plot.extra_y_ranges = {"watermark": watermarkrange} - #plot.sizing_mode = 'scale_both' - - plot.image_url([watermarkurl], 1.8*max(thesecs), watermarky, - watermarkw, watermarkh, - global_alpha=watermarkalpha, - w_units='screen', - h_units='screen', - anchor=watermarkanchor, - dilate=True, - y_range_name="watermark", - ) - - plot.circle('duration', 'power', source=source, fill_color='red', size=15, - legend_label='Power') - plot.xaxis.axis_label = "Duration (seconds)" - plot.yaxis.axis_label = "Power (W)" - - if stayerscore is not None: # pragma: no cover - plot.add_layout( - Label(x=100, y=100, x_units='screen', y_units='screen', - text='Stayer Score '+str(stayerscore)+'%', - background_fill_alpha=0.7, - background_fill_color='white', - text_color='black') - ) -# plot.add_layout( -# Label(x=100,y=120,x_units='screen',y_units='screen', -# text='Stayer Score (6min) '+str(stayerscore2)+'%', -# background_fill_alpha=0.7, -# background_fill_color='white', -# text_color='black') -# ) - - cpdata = dataprep.fetchcperg(rower, theworkouts) - - if cpdata.empty: # pragma: no cover - message = 'Calculations are running in the background. Please refresh this page to see updated results' - return ['', '', paulslope, paulintercept, p1, message, p1wc] - - velo = cpdata['distance']/cpdata['delta'] - - p = 500./velo - - p2 = p.fillna(method='ffill').apply(lambda x: timedeltaconv(x)) - - source2 = ColumnDataSource( - data=dict( - duration=cpdata['delta'], - power=cpdata['cp'], - tim=niceformat( - cpdata['delta'].fillna(method='ffill').apply( - lambda x: timedeltaconv(x)) - ), - dist=cpdata['distance'], - pace=nicepaceformat(p2), - ) - ) - - plot.circle('duration', 'power', source=source2, - fill_color='blue', size=3, - legend_label='Power from segments') - - hover = plot.select(dict(type=HoverTool)) - - hover.tooltips = OrderedDict([ - ('Duration ', '@tim'), - ('Power (W)', '@power{int}'), - ('Distance (m)', '@dist{int}'), - ('Pace (/500m)', '@fpace'), - ]) - - hover.mode = 'mouse' - - plot.y_range = Range1d(0, 1.5*max(theavpower)) - plot.x_range = Range1d(1, 2*max(thesecs)) - plot.legend.orientation = "vertical" - - plot.line('duration', 'power', source=sourcepaul, - legend_label="Paul's Law") - plot.line('duration', 'power', source=sourcecomplex, legend_label="CP Model", - color='green') - if p1wc is not None: - plot.line('duration', 'fitpowerwc', source=sourcecomplex, - legend_label="World Class", - color='Maroon', line_dash='dotted') - - plot.line('duration', 'fitpowerexcellent', source=sourcecomplex, - legend_label="90% percentile", - color='Purple', line_dash='dotted') - - plot.line('duration', 'fitpowergood', source=sourcecomplex, - legend_label="75% percentile", - color='Olive', line_dash='dotted') - - plot.line('duration', 'fitpowerfair', source=sourcecomplex, - legend_label="50% percentile", - color='Gray', line_dash='dotted') - - plot.line('duration', 'fitpoweraverage', source=sourcecomplex, - legend_label="25% percentile", - color='SkyBlue', line_dash='dotted') - - script, div = components(plot) - - return [script, div, paulslope, paulintercept, p1, message, p1wc] - - -def interactive_windchart(id=0, promember=0): - # check if valid ID exists (workout exists) - row = Workout.objects.get(id=id) - # g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime") - - f1 = row.csvfilename - - # create interactive plot - plot = figure(width=400, height=300) - - # get user - # u = User.objects.get(id=row.user.id) - r = row.user - - rr = rrower(hrmax=r.max, hrut2=r.ut2, - hrut1=r.ut1, hrat=r.at, - hrtr=r.tr, hran=r.an, ftp=r.ftp) - - rowdata = rdata(f1, rower=rr) - if rowdata == 0: # pragma: no cover - return 0 - - try: - dist = rowdata.df.loc[:, 'cum_dist'] - except KeyError: - return ['', 'No Data Found'] - - try: # pragma: no cover - vwind = rowdata.df.loc[:, 'vwind'] - winddirection = rowdata.df.loc[:, 'winddirection'] - bearing = rowdata.df.loc[:, 'bearing'] - except KeyError: - rowdata.add_wind(0, 0) - rowdata.add_bearing() - vwind = rowdata.df.loc[:, 'vwind'] - winddirection = rowdata.df.loc[:, 'winddirection'] - bearing = rowdata.df.loc[:, 'winddirection'] - rowdata.write_csv(f1, gzip=True) - dataprep.update_strokedata(id, rowdata.df) - - winddirection = winddirection % 360 - winddirection = (winddirection + 360) % 360 - - tw = tailwind(bearing, vwind, 1.0*winddirection) - - source = ColumnDataSource( - data=dict( - dist=dist, - vwind=vwind, - tw=tw, - winddirection=winddirection, - ) - ) - - # plot tools - if (promember == 1): - TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,crosshair' - else: # pragma: no cover - TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,crosshair' - - # making the plot - plot = figure(tools=TOOLS, width=400, height=500, - # toolbar_location="below", - toolbar_sticky=False, - ) - plot.line('dist', 'vwind', source=source, legend_label="Wind Speed (m/s)") - plot.line('dist', 'tw', source=source, - legend_label="Tail (+)/Head (-) Wind (m/s)", color='black') - try: - plot.title.text = row.name - except ValueError: # pragma: no cover - plot.title.text = "" - # plot.title.text_font_size="1.0em" - plot.title.text_font = "1.0em" - plot.xaxis.axis_label = "Distance (m)" - plot.yaxis.axis_label = "Wind Speed (m/s)" - plot.y_range = Range1d(-7, 7) - #plot.sizing_mode = 'stretch_both' - - plot.extra_y_ranges = {"winddirection": Range1d(start=0, end=360)} - plot.line('dist', 'winddirection', source=source, - legend_label='Wind Direction', color="red", - y_range_name="winddirection") - plot.add_layout(LinearAxis(y_range_name="winddirection", - axis_label="Wind Direction (degree)"), 'right') - - script, div = components(plot) - - return [script, div] - - -def interactive_streamchart(id=0, promember=0): - # check if valid ID exists (workout exists) - row = Workout.objects.get(id=id) - # g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime") - - f1 = row.csvfilename - - # create interactive plot - plot = figure(width=400, - ) - # get user - # u = User.objects.get(id=row.user.id) - r = row.user - - rr = rrower(hrmax=r.max, hrut2=r.ut2, - hrut1=r.ut1, hrat=r.at, - hrtr=r.tr, hran=r.an, ftp=r.ftp) - - rowdata = rdata(f1, rower=rr) - if rowdata == 0: # pragma: no cover - return "", "No Valid Data Available" - - try: - dist = rowdata.df.loc[:, 'cum_dist'] - except KeyError: - return ['', 'No Data found'] - - try: - vstream = rowdata.df.loc[:, 'vstream'] - except KeyError: - rowdata.add_stream(0) - vstream = rowdata.df.loc[:, 'vstream'] - rowdata.write_csv(f1, gzip=True) - dataprep.update_strokedata(id, rowdata.df) - - # plot tools - if (promember == 1): - TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,crosshair' - else: # pragma: no cover - TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,crosshair' - - # making the plot - plot = figure(tools=TOOLS, width=400, height=500, - # toolbar_location="below", - toolbar_sticky=False, - ) - plot.line(dist, vstream, legend_label="River Stream Velocity (m/s)") - try: - plot.title.text = row.name - except ValueError: # pragma: no cover - plot.title.text = "" - plot.title.text_font_size = "1.0em" - plot.xaxis.axis_label = "Distance (m)" - plot.yaxis.axis_label = "River Current (m/s)" - plot.y_range = Range1d(-2, 2) - #plot.sizing_mode = 'stretch_both' - - script, div = components(plot) - - return [script, div] - def forcecurve_multi_interactive_chart(selected): # pragma: no cover ids = [analysis.id for analysis in selected] workoutids = [analysis.workout.id for analysis in selected] @@ -1678,12 +1244,13 @@ def instroke_multi_interactive_chart(selected, *args, **kwargs): # pragma: no co maximum_values[analysis.metric] = mean_vals.max() xvals = np.arange(len(mean_vals)) - data2 = pd.DataFrame({ - 'x': pd.Series(xvals), - 'y': pd.Series(mean_vals), + data2 = pl.DataFrame({ + 'x': pl.Series(xvals), + 'y': pl.Series(mean_vals), }) - data2['id'] = cntr + data2 = data2.with_columns((pl.lit(cntr)).alias("id")) + df2.append(data2) legendlabel = '{name} - {metric} - {workout}'.format( @@ -1704,9 +1271,9 @@ def instroke_multi_interactive_chart(selected, *args, **kwargs): # pragma: no co ytitle = 'Scaled' cntr = cntr+1 - df2 = pd.concat(df2, axis=0) + df2 = pl.concat(df2) - data_dict = df2.to_dict("records") + data_dict = df2.to_dicts() chart_data = { 'title': '', @@ -1725,24 +1292,25 @@ def instroke_interactive_chart(df,metric, workout, spm_min, spm_max, individual_curves, name='',notes=''): # pragma: no cover - df_pos = (df+abs(df))/2. - df_min = -(-df+abs(-df))/2. - if df.empty: return "", "No data in selection" - mean_vals = df.median().replace(0, np.nan) - q75 = df_pos.quantile(q=0.75).replace(0,np.nan) - q25 = df_pos.quantile(q=0.25).replace(0,np.nan) - q75min = df_min.quantile(q=0.75).replace(0,np.nan) - q25min = df_min.quantile(q=0.25).replace(0,np.nan) + df_pos = (df+abs(df))/2. + df_min = -(-df+abs(-df))/2. + + + mean_vals = df.median().replace(0, np.nan) + q75 = df_pos.quantile(q = 0.75).replace(0, np.nan) + q25 = df_pos.quantile(q=0.25).replace(0, np.nan) + q75min = df_min.quantile(q=0.75).replace(0, np.nan) + q25min = df_min.quantile(q=0.25).replace(0, np.nan) mean_vals = mean_vals.interpolate() xvals = np.arange(len(mean_vals)) - df_plot = pd.DataFrame({ + df_plot = pl.DataFrame({ 'x':xvals, 'median':mean_vals, 'high':q75, @@ -1751,14 +1319,17 @@ def instroke_interactive_chart(df,metric, workout, spm_min, spm_max, 'low 2': q25, }) - df_plot['high'].update(df_plot.pop('high 2')) - df_plot['low'].update(df_plot.pop('low 2')) - try: - df_plot.interpolate(axis=1,inplace=True) - except TypeError: - pass + df_plot = df_plot.with_columns( + pl.coalesce(["high", "high 2"]).alias("high") + ) + + df_plot = df_plot.with_columns( + pl.coalesce("low", "low 2").alias("low") + ) + + df_plot = df_plot.drop(["high 2", "low 2"]) + df_plot = df_plot.drop_nulls() - if metric == 'boat accelerator curve': ytitle = "Boat acceleration (m/s^2)" elif metric == 'instroke boat speed': @@ -1771,7 +1342,7 @@ def instroke_interactive_chart(df,metric, workout, spm_min, spm_max, lines_dict = df.to_dict("records") - data_dict = df_plot.to_dict("records") + data_dict = df_plot.to_dicts() chart_data = { 'lines': lines_dict, @@ -1790,7 +1361,6 @@ def instroke_interactive_chart(df,metric, workout, spm_min, spm_max, 'analysis_name': name, } - script, div = get_chart("/instroke", chart_data, debug=False) return script, div diff --git a/rowers/views/analysisviews.py b/rowers/views/analysisviews.py index 5c88125c..58566c67 100644 --- a/rowers/views/analysisviews.py +++ b/rowers/views/analysisviews.py @@ -1523,19 +1523,6 @@ def otwcp_toadmin_view(request, theuser=0, return response -def agegroupcpview(request, age, normalize=0, userid=0): - script, div = interactive_agegroupcpchart(age, normalized=normalize) - - response = render(request, 'agegroupcp.html', - { - 'active': 'nav-analysis', - 'interactiveplot': script, - 'the_div': div, - } - ) - - return response - def agegrouprecordview(request, sex='male', weightcategory='hwt', distance=2000, duration=None):