Skip to content
Snippets Groups Projects
Verified Commit b9ca6f63 authored by Owen Graham's avatar Owen Graham
Browse files

Improve and refactor finding extrema

parent 572cda74
Branches
No related tags found
No related merge requests found
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import math import math
from io import BytesIO from io import BytesIO
import operator
from types import SimpleNamespace from types import SimpleNamespace
from flask import Response from flask import Response
...@@ -84,8 +85,7 @@ class TimeSeries(Plotter): ...@@ -84,8 +85,7 @@ class TimeSeries(Plotter):
axes.hlines(y=avg, xmin=data[0, 0], xmax=data[-1, 0]) axes.hlines(y=avg, xmin=data[0, 0], xmax=data[-1, 0])
axes.set_ylabel(f'{meas.title.title()} ({meas.units})') axes.set_ylabel(f'{meas.title.title()} ({meas.units})')
axes.grid(True) axes.grid(True)
maximum = max(data, key=lambda row: row[meas.field]) maximum, minimum = data_max_min(data, field=meas.field)
minimum = min(data, key=lambda row: row[meas.field])
axes.set_title( axes.set_title(
(f'Max {meas.title}: {maximum[meas.field]} {meas.units} ' (f'Max {meas.title}: {maximum[meas.field]} {meas.units} '
f'on {maximum[0]:%F at %R} / ' f'on {maximum[0]:%F at %R} / '
...@@ -168,15 +168,11 @@ class Overlay(Plotter): ...@@ -168,15 +168,11 @@ class Overlay(Plotter):
alpha=0.875 if i else 1.0) alpha=0.875 if i else 1.0)
axes.set_ylabel(f'{meas.title.title()} ({meas.units})') axes.set_ylabel(f'{meas.title.title()} ({meas.units})')
row_meas = operator.itemgetter(meas.field)
for dset in datasets: for dset in datasets:
comparable_data = [row for row in dset.data dset.max, dset.min = data_max_min(dset.data, key=row_meas)
if not math.isnan(row[meas.field])] max_dset = max(datasets, key=lambda dset: row_meas(dset.max))
dset.max = max(comparable_data, key=lambda row: row[meas.field], min_dset = min(datasets, key=lambda dset: row_meas(dset.min))
default=dset.data[0])
dset.min = min(comparable_data, key=lambda row: row[meas.field],
default=dset.data[0])
max_dset = max(datasets, key=lambda dset: dset.max[meas.field])
min_dset = min(datasets, key=lambda dset: dset.min[meas.field])
axes.grid(True) axes.grid(True)
axes.set_title( axes.set_title(
...@@ -239,23 +235,17 @@ class Boxplot(Plotter): ...@@ -239,23 +235,17 @@ class Boxplot(Plotter):
start_year, end_year = sorted([year1, year2]) start_year, end_year = sorted([year1, year2])
years = range(start_year, end_year + 1) years = range(start_year, end_year + 1)
maximum = minimum = None row_meas = operator.itemgetter(meas.field)
years_maxima = []
def at_index(row): years_minima = []
"""Get the selected field of `row`."""
return row[meas.field]
for year in years: for year in years:
data = read_data(station, year) data = read_data(station, year)
plot_data.append(data[:, meas.field]) plot_data.append(data[:, meas.field])
year_max, year_min = data_max_min(data, key=row_meas)
year_max = max(data, key=at_index) years_maxima.append(year_max)
year_min = min(data, key=at_index) years_minima.append(year_min)
if maximum is None: maximum = max(years_maxima, key=row_meas)
maximum, minimum = year_max, year_min minimum = min(years_minima, key=row_meas)
else:
maximum = max(maximum, year_max, key=at_index)
minimum = min(minimum, year_min, key=at_index)
fig, axes = plt.subplots() fig, axes = plt.subplots()
fig.set_figheight(6) fig.set_figheight(6)
...@@ -289,3 +279,23 @@ def savefig_response(fig, filename=None): ...@@ -289,3 +279,23 @@ def savefig_response(fig, filename=None):
if filename is not None: if filename is not None:
res.headers['Content-Disposition'] = f'inline; filename="{filename}"' res.headers['Content-Disposition'] = f'inline; filename="{filename}"'
return res return res
def data_max_min(data, field=None, key=None):
"""Smarter, NaN-aware `max_min()`, for plot functions."""
if key is None:
key = operator.itemgetter(field)
comparable = non_nan(data, key=key)
return max_min(comparable, key=key, default=data[0])
def max_min(*args, **kwargs):
"""Call `max()` and `min()` with the same arguments."""
return max(*args, **kwargs), min(*args, **kwargs)
def non_nan(items, key=None):
"""Filter out items that are NaN."""
if key is None:
key = lambda x: x
return [i for i in items if not math.isnan(key(i))]
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment