From fb82ca676bf7d6f604fd08edbc5b1b3864d2f1f1 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Tue, 25 Jun 2019 10:21:13 +0200 Subject: [PATCH] Use builtin round instead of np.round for scalars. Compared to np.round, builtin round is 20x faster for python floats and 2x faster for numpy floats (it's admittedly unlikely that any of the places patched is an actual bottleneck). On Py2 builtin round and np.round were not equivalent because builtin round rounded halves away from zero, but now both round-to-even. Small misc. changes; the rearrangement in backend_agg.draw_text is to make the implementation more similar to draw_mathtext (just above). --- examples/misc/custom_projection.py | 2 +- examples/statistics/barchart_demo.py | 30 +++++++++---------- examples/statistics/boxplot_demo.py | 2 +- .../ticks_and_spines/date_index_formatter2.py | 2 +- lib/matplotlib/backends/backend_agg.py | 17 +++++------ lib/matplotlib/backends/backend_pdf.py | 5 ++-- lib/matplotlib/colors.py | 6 ++-- lib/matplotlib/dates.py | 2 +- lib/matplotlib/image.py | 14 ++++----- lib/matplotlib/patches.py | 4 +-- lib/matplotlib/projections/geo.py | 3 +- lib/matplotlib/tests/test_ticker.py | 2 +- lib/matplotlib/ticker.py | 8 ++--- lib/matplotlib/widgets.py | 4 +-- lib/mpl_toolkits/mplot3d/axes3d.py | 14 ++++----- 15 files changed, 56 insertions(+), 59 deletions(-) diff --git a/examples/misc/custom_projection.py b/examples/misc/custom_projection.py index 50396e7692c9..5858e904a911 100644 --- a/examples/misc/custom_projection.py +++ b/examples/misc/custom_projection.py @@ -39,7 +39,7 @@ def __init__(self, round_to=1.0): self._round_to = round_to def __call__(self, x, pos=None): - degrees = np.round(np.rad2deg(x) / self._round_to) * self._round_to + degrees = round(np.rad2deg(x) / self._round_to) * self._round_to if rcParams['text.usetex'] and not rcParams['text.latex.unicode']: return r"$%0.0f^\circ$" % degrees else: diff --git a/examples/statistics/barchart_demo.py b/examples/statistics/barchart_demo.py index c167e08e2c68..e069fed82672 100644 --- a/examples/statistics/barchart_demo.py +++ b/examples/statistics/barchart_demo.py @@ -27,9 +27,9 @@ Score = namedtuple('Score', ['score', 'percentile']) # GLOBAL CONSTANTS -testNames = ['Pacer Test', 'Flexed Arm\n Hang', 'Mile Run', 'Agility', - 'Push Ups'] -testMeta = dict(zip(testNames, ['laps', 'sec', 'min:sec', 'sec', ''])) +test_names = ['Pacer Test', 'Flexed Arm\n Hang', 'Mile Run', 'Agility', + 'Push Ups'] +test_meta = dict(zip(test_names, ['laps', 'sec', 'min:sec', 'sec', ''])) def attach_ordinal(num): @@ -54,7 +54,7 @@ def format_score(scr, test): info (like for pushups) then don't add the carriage return to the string """ - md = testMeta[test] + md = test_meta[test] if md: return '{0}\n{1}'.format(scr, md) else: @@ -63,10 +63,10 @@ def format_score(scr, test): def format_ycursor(y): y = int(y) - if y < 0 or y >= len(testNames): + if y < 0 or y >= len(test_names): return '' else: - return testNames[y] + return test_names[y] def plot_student_results(student, scores, cohort_size): @@ -75,12 +75,12 @@ def plot_student_results(student, scores, cohort_size): fig.subplots_adjust(left=0.115, right=0.88) fig.canvas.set_window_title('Eldorado K-8 Fitness Chart') - pos = np.arange(len(testNames)) + pos = np.arange(len(test_names)) - rects = ax1.barh(pos, [scores[k].percentile for k in testNames], + rects = ax1.barh(pos, [scores[k].percentile for k in test_names], align='center', height=0.5, - tick_label=testNames) + tick_label=test_names) ax1.set_title(student.name) @@ -95,7 +95,7 @@ def plot_student_results(student, scores, cohort_size): # Set the right-hand Y-axis ticks and labels ax2 = ax1.twinx() - scoreLabels = [format_score(scores[k].score, k) for k in testNames] + scoreLabels = [format_score(scores[k].score, k) for k in test_names] # set the tick locations ax2.set_yticks(pos) @@ -156,11 +156,11 @@ def plot_student_results(student, scores, cohort_size): student = Student('Johnny Doe', 2, 'boy') -scores = dict(zip(testNames, - (Score(v, p) for v, p in - zip(['7', '48', '12:52', '17', '14'], - np.round(np.random.uniform(0, 1, - len(testNames)) * 100, 0))))) +scores = dict(zip( + test_names, + (Score(v, p) for v, p in + zip(['7', '48', '12:52', '17', '14'], + np.round(np.random.uniform(0, 100, len(test_names)), 0))))) cohort_size = 62 # The number of other 2nd grade boys arts = plot_student_results(student, scores, cohort_size) diff --git a/examples/statistics/boxplot_demo.py b/examples/statistics/boxplot_demo.py index 570e9a2428f7..c6fc79cfc008 100644 --- a/examples/statistics/boxplot_demo.py +++ b/examples/statistics/boxplot_demo.py @@ -164,7 +164,7 @@ # X-axis tick labels with the sample medians to aid in comparison # (just use two decimal places of precision) pos = np.arange(num_boxes) + 1 -upper_labels = [str(np.round(s, 2)) for s in medians] +upper_labels = [str(round(s, 2)) for s in medians] weights = ['bold', 'semibold'] for tick, label in zip(range(num_boxes), ax1.get_xticklabels()): k = tick % 2 diff --git a/examples/ticks_and_spines/date_index_formatter2.py b/examples/ticks_and_spines/date_index_formatter2.py index c59342e134a7..ce36589b9d91 100644 --- a/examples/ticks_and_spines/date_index_formatter2.py +++ b/examples/ticks_and_spines/date_index_formatter2.py @@ -32,7 +32,7 @@ def __init__(self, dates, fmt='%Y-%m-%d'): def __call__(self, x, pos=0): 'Return the label for time x at position pos' - ind = int(np.round(x)) + ind = int(round(x)) if ind >= len(self.dates) or ind < 0: return '' return dates.num2date(self.dates[ind]).strftime(self.fmt) diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py index b2ade30edd00..c5545ae80bc3 100644 --- a/lib/matplotlib/backends/backend_agg.py +++ b/lib/matplotlib/backends/backend_agg.py @@ -163,8 +163,8 @@ def draw_mathtext(self, gc, x, y, s, prop, angle): xd = descent * sin(radians(angle)) yd = descent * cos(radians(angle)) - x = np.round(x + ox + xd) - y = np.round(y - oy + yd) + x = round(x + ox + xd) + y = round(y - oy + yd) self._renderer.draw_text_image(font_image, x, y + 1, angle, gc) def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): @@ -190,11 +190,11 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): xo, yo = font.get_bitmap_offset() xo /= 64.0 yo /= 64.0 - xd = -d * sin(radians(angle)) + xd = d * sin(radians(angle)) yd = d * cos(radians(angle)) - - self._renderer.draw_text_image( - font, np.round(x - xd + xo), np.round(y + yd + yo) + 1, angle, gc) + x = round(x + xo + xd) + y = round(y + yo + yd) + self._renderer.draw_text_image(font, x, y + 1, angle, gc) def get_text_width_height_descent(self, s, prop, ismath): # docstring inherited @@ -235,9 +235,8 @@ def draw_tex(self, gc, x, y, s, prop, angle, ismath='TeX!', mtext=None): w, h, d = self.get_text_width_height_descent(s, prop, ismath) xd = d * sin(radians(angle)) yd = d * cos(radians(angle)) - x = np.round(x + xd) - y = np.round(y + yd) - + x = round(x + xd) + y = round(y + yd) self._renderer.draw_text_image(Z, x, y, angle, gc) def get_canvas_width_height(self): diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index 4e5042e96659..34d826c75816 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -903,9 +903,8 @@ def cvt(length, upe=font.units_per_EM, nearest=True): "Convert font coordinates to PDF glyph coordinates" value = length / upe * 1000 if nearest: - return np.round(value) - # Perhaps best to round away from zero for bounding - # boxes and the like + return round(value) + # Best(?) to round away from zero for bounding boxes and the like. if value < 0: return math.floor(value) else: diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index baec148e8f15..7e9311326b11 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -325,7 +325,8 @@ def to_rgb(c): def to_hex(c, keep_alpha=False): - """Convert *c* to a hex color. + """ + Convert *c* to a hex color. Uses the ``#rrggbb`` format if *keep_alpha* is False (the default), ``#rrggbbaa`` otherwise. @@ -333,8 +334,7 @@ def to_hex(c, keep_alpha=False): c = to_rgba(c) if not keep_alpha: c = c[:3] - return "#" + "".join(format(int(np.round(val * 255)), "02x") - for val in c) + return "#" + "".join(format(int(round(val * 255)), "02x") for val in c) ### Backwards-compatible color-conversion API diff --git a/lib/matplotlib/dates.py b/lib/matplotlib/dates.py index 3c8464e3da3f..e96af84f27b1 100644 --- a/lib/matplotlib/dates.py +++ b/lib/matplotlib/dates.py @@ -640,7 +640,7 @@ def __init__(self, t, fmt, tz=None): def __call__(self, x, pos=0): 'Return the label for time *x* at position *pos*' - ind = int(np.round(x)) + ind = int(round(x)) if ind >= len(self.t) or ind <= 0: return '' return num2date(self.t[ind], self.tz).strftime(self.fmt) diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index da5d7d4cad77..c2e4db87ca4b 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -147,7 +147,7 @@ def flush_images(): gc = renderer.new_gc() gc.set_clip_rectangle(parent.bbox) gc.set_clip_path(parent.get_clip_path()) - renderer.draw_image(gc, np.round(l), np.round(b), data) + renderer.draw_image(gc, round(l), round(b), data) gc.restore() del image_group[:] @@ -971,8 +971,8 @@ def make_image(self, renderer, magnification=1.0, unsampled=False): self.is_grayscale = False x0, y0, v_width, v_height = self.axes.viewLim.bounds l, b, r, t = self.axes.bbox.extents - width = (np.round(r) + 0.5) - (np.round(l) - 0.5) - height = (np.round(t) + 0.5) - (np.round(b) - 0.5) + width = (round(r) + 0.5) - (round(l) - 0.5) + height = (round(t) + 0.5) - (round(b) - 0.5) width *= magnification height *= magnification im = _image.pcolor(self._Ax, self._Ay, A, @@ -1086,11 +1086,11 @@ def make_image(self, renderer, magnification=1.0, unsampled=False): bg = mcolors.to_rgba(fc, 0) bg = (np.array(bg)*255).astype(np.uint8) l, b, r, t = self.axes.bbox.extents - width = (np.round(r) + 0.5) - (np.round(l) - 0.5) - height = (np.round(t) + 0.5) - (np.round(b) - 0.5) + width = (round(r) + 0.5) - (round(l) - 0.5) + height = (round(t) + 0.5) - (round(b) - 0.5) # The extra cast-to-int is only needed for python2 - width = int(np.round(width * magnification)) - height = int(np.round(height * magnification)) + width = int(round(width * magnification)) + height = int(round(height * magnification)) if self._rgbacache is None: A = self.to_rgba(self._A, bytes=True) self._rgbacache = A diff --git a/lib/matplotlib/patches.py b/lib/matplotlib/patches.py index 4b43dcb6f55d..e4e3650f6f67 100644 --- a/lib/matplotlib/patches.py +++ b/lib/matplotlib/patches.py @@ -2241,9 +2241,9 @@ def _get_sawtooth_vertices(self, x0, y0, width, height, mutation_size): # the sizes of the vertical and horizontal sawtooth are # separately adjusted to fit the given box size. - dsx_n = int(np.round((width - tooth_size) / (tooth_size * 2))) * 2 + dsx_n = int(round((width - tooth_size) / (tooth_size * 2))) * 2 dsx = (width - tooth_size) / dsx_n - dsy_n = int(np.round((height - tooth_size) / (tooth_size * 2))) * 2 + dsy_n = int(round((height - tooth_size) / (tooth_size * 2))) * 2 dsy = (height - tooth_size) / dsy_n x0, y0 = x0 - pad + tooth_size2, y0 - pad + tooth_size2 diff --git a/lib/matplotlib/projections/geo.py b/lib/matplotlib/projections/geo.py index 33a00387a427..a5d6efb5f055 100644 --- a/lib/matplotlib/projections/geo.py +++ b/lib/matplotlib/projections/geo.py @@ -22,8 +22,7 @@ def __init__(self, round_to=1.0): self._round_to = round_to def __call__(self, x, pos=None): - degrees = (x / np.pi) * 180.0 - degrees = np.round(degrees / self._round_to) * self._round_to + degrees = round(np.rad2deg(x) / self._round_to) * self._round_to if rcParams['text.usetex'] and not rcParams['text.latex.unicode']: return r"$%0.0f^\circ$" % degrees else: diff --git a/lib/matplotlib/tests/test_ticker.py b/lib/matplotlib/tests/test_ticker.py index 61140f954b9f..4859fff48b56 100644 --- a/lib/matplotlib/tests/test_ticker.py +++ b/lib/matplotlib/tests/test_ticker.py @@ -621,7 +621,7 @@ def _sub_labels(self, axis, subs=()): minor_tlocs = axis.get_minorticklocs() fmt.set_locs(minor_tlocs) coefs = minor_tlocs / 10**(np.floor(np.log10(minor_tlocs))) - label_expected = [np.round(c) in subs for c in coefs] + label_expected = [round(c) in subs for c in coefs] label_test = [fmt(x) != '' for x in minor_tlocs] assert label_test == label_expected diff --git a/lib/matplotlib/ticker.py b/lib/matplotlib/ticker.py index 5039b0230cf6..62e6eff5282b 100644 --- a/lib/matplotlib/ticker.py +++ b/lib/matplotlib/ticker.py @@ -1012,8 +1012,8 @@ def __call__(self, x, pos=None): # only label the decades fx = math.log(x) / math.log(b) is_x_decade = is_close_to_int(fx) - exponent = np.round(fx) if is_x_decade else np.floor(fx) - coeff = np.round(x / b ** exponent) + exponent = round(fx) if is_x_decade else np.floor(fx) + coeff = round(x / b ** exponent) if self.labelOnlyBase and not is_x_decade: return '' @@ -1116,8 +1116,8 @@ def __call__(self, x, pos=None): # only label the decades fx = math.log(x) / math.log(b) is_x_decade = is_close_to_int(fx) - exponent = np.round(fx) if is_x_decade else np.floor(fx) - coeff = np.round(x / b ** exponent) + exponent = round(fx) if is_x_decade else np.floor(fx) + coeff = round(x / b ** exponent) if is_x_decade: fx = round(fx) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index 0f0114d89a84..83c10c5f7ffe 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -393,8 +393,8 @@ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt='%1.2f', def _value_in_bounds(self, val): """Makes sure *val* is with given bounds.""" if self.valstep: - val = np.round((val - self.valmin)/self.valstep)*self.valstep - val += self.valmin + val = (self.valmin + + round((val - self.valmin) / self.valstep) * self.valstep) if val <= self.valmin: if not self.closedmin: diff --git a/lib/mpl_toolkits/mplot3d/axes3d.py b/lib/mpl_toolkits/mplot3d/axes3d.py index 624d46f003ae..0564281b625f 100644 --- a/lib/mpl_toolkits/mplot3d/axes3d.py +++ b/lib/mpl_toolkits/mplot3d/axes3d.py @@ -2042,7 +2042,7 @@ def _3d_extend_contour(self, cset, stride=5): polyverts = [] normals = [] - nsteps = np.round(len(topverts[0]) / stride) + nsteps = round(len(topverts[0]) / stride) if nsteps <= 1: if len(topverts[0]) > 1: nsteps = 2 @@ -2050,13 +2050,13 @@ def _3d_extend_contour(self, cset, stride=5): continue stepsize = (len(topverts[0]) - 1) / (nsteps - 1) - for i in range(int(np.round(nsteps)) - 1): - i1 = int(np.round(i * stepsize)) - i2 = int(np.round((i + 1) * stepsize)) + for i in range(int(round(nsteps)) - 1): + i1 = int(round(i * stepsize)) + i2 = int(round((i + 1) * stepsize)) polyverts.append([topverts[0][i1], - topverts[0][i2], - botverts[0][i2], - botverts[0][i1]]) + topverts[0][i2], + botverts[0][i2], + botverts[0][i1]]) # all polygons have 4 vertices, so vectorize polyverts = np.array(polyverts)