@@ -186,6 +186,23 @@ def ts_data(ts):
186
186
}
187
187
188
188
189
+ def determine_aggregation_function (function_name ):
190
+ """
191
+ Return the aggregation function associated to the provided function name. This is used by
192
+ dropdown menus that allow selecting from multiple aggregation functions.
193
+ """
194
+ if function_name == 'min' :
195
+ return lnt .util .stats .safe_min
196
+ elif function_name == 'max' :
197
+ return lnt .util .stats .safe_max
198
+ elif function_name == 'mean' :
199
+ return lnt .util .stats .mean
200
+ elif function_name == 'median' :
201
+ return lnt .util .stats .median
202
+ else :
203
+ assert False , f'Invalid aggregation function name { function_name } '
204
+
205
+
189
206
@db_route ('/submitRun' , methods = ('GET' , 'POST' ))
190
207
def submit_run ():
191
208
"""Compatibility url that hardcodes testsuite to 'nts'"""
@@ -353,10 +370,7 @@ def __init__(self, run_id):
353
370
abort (404 , "Invalid run id {}" .format (run_id ))
354
371
355
372
# Get the aggregation function to use.
356
- aggregation_fn_name = request .args .get ('aggregation_fn' )
357
- self .aggregation_fn = {'min' : lnt .util .stats .safe_min ,
358
- 'median' : lnt .util .stats .median }.get (
359
- aggregation_fn_name , lnt .util .stats .safe_min )
373
+ aggregation_fn = determine_aggregation_function (request .args .get ('aggregation_function' , 'min' ))
360
374
361
375
# Get the MW confidence level.
362
376
try :
@@ -428,7 +442,7 @@ def __init__(self, run_id):
428
442
session , self .run , baseurl = db_url_for ('.index' , _external = False ),
429
443
result = None , compare_to = compare_to , baseline = baseline ,
430
444
num_comparison_runs = self .num_comparison_runs ,
431
- aggregation_fn = self . aggregation_fn , confidence_lv = confidence_lv ,
445
+ aggregation_fn = aggregation_fn , confidence_lv = confidence_lv ,
432
446
styles = styles , classes = classes )
433
447
self .sri = self .data ['sri' ]
434
448
note = self .data ['visible_note' ]
@@ -516,7 +530,7 @@ def v4_run(id):
516
530
else :
517
531
test_min_value_filter = 0.0
518
532
519
- options ['aggregation_fn ' ] = request .args .get ('aggregation_fn ' , 'min' )
533
+ options ['aggregation_function ' ] = request .args .get ('aggregation_function ' , 'min' )
520
534
521
535
# Get the test names.
522
536
test_info = session .query (ts .Test .name , ts .Test .id ).\
@@ -961,30 +975,13 @@ def v4_tableau():
961
975
962
976
@v4_route ("/graph" )
963
977
def v4_graph ():
964
-
965
978
session = request .session
966
979
ts = request .get_testsuite ()
967
- switch_min_mean_local = False
968
980
969
- if 'switch_min_mean_session' not in flask .session :
970
- flask .session ['switch_min_mean_session' ] = False
971
981
# Parse the view options.
972
- options = {'min_mean_checkbox' : 'min()' }
973
- if 'submit' in request .args : # user pressed a button
974
- if 'switch_min_mean' in request .args : # user checked mean() checkbox
975
- flask .session ['switch_min_mean_session' ] = \
976
- options ['switch_min_mean' ] = \
977
- bool (request .args .get ('switch_min_mean' ))
978
- switch_min_mean_local = flask .session ['switch_min_mean_session' ]
979
- else : # mean() check box is not checked
980
- flask .session ['switch_min_mean_session' ] = \
981
- options ['switch_min_mean' ] = \
982
- bool (request .args .get ('switch_min_mean' ))
983
- switch_min_mean_local = flask .session ['switch_min_mean_session' ]
984
- else : # new page was loaded by clicking link, not submit button
985
- options ['switch_min_mean' ] = switch_min_mean_local = \
986
- flask .session ['switch_min_mean_session' ]
987
-
982
+ options = {}
983
+ options ['aggregation_function' ] = \
984
+ request .args .get ('aggregation_function' ) # default determined later based on the field being graphed
988
985
options ['hide_lineplot' ] = bool (request .args .get ('hide_lineplot' ))
989
986
show_lineplot = not options ['hide_lineplot' ]
990
987
options ['show_mad' ] = show_mad = bool (request .args .get ('show_mad' ))
@@ -1198,15 +1195,16 @@ def trace_name(name, test_name, field_name):
1198
1195
1199
1196
is_multisample = (len (values ) > 1 )
1200
1197
1201
- aggregation_fn = min
1202
- if switch_min_mean_local :
1203
- aggregation_fn = lnt .util .stats .agg_mean
1204
- if field .bigger_is_better :
1205
- aggregation_fn = max
1198
+ fn_name = options .get ('aggregation_function' ) or ('max' if field .bigger_is_better else 'min' )
1199
+ aggregation_fn = determine_aggregation_function (fn_name )
1200
+ agg_value = aggregation_fn (values )
1201
+
1202
+ # When aggregating multiple samples, it becomes unclear which sample to use for
1203
+ # associated data like the run date, the order, etc. Use the index of the closest
1204
+ # value in all the samples.
1205
+ closest_value = sorted (values , key = lambda val : abs (val - agg_value ))[0 ]
1206
+ agg_index = values .index (closest_value )
1206
1207
1207
- agg_value , agg_index = \
1208
- aggregation_fn ((value , index )
1209
- for (index , value ) in enumerate (values ))
1210
1208
pts_y .append (agg_value )
1211
1209
1212
1210
# Plotly does not sort X axis in case of type: 'category'.
0 commit comments