From 62d9cbcb1b444d32ad0ddb1d8346f886762f8480 Mon Sep 17 00:00:00 2001 From: tgwoodcock Date: Tue, 26 Apr 2022 14:29:46 +0200 Subject: [PATCH 01/20] Fixed pass through of permutation to line Ensured that TernaryAxesSubplot.line passes the self._permutation through to ternary.lines.line --- ternary/ternary_axes_subplot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ternary/ternary_axes_subplot.py b/ternary/ternary_axes_subplot.py index dd56f9b..24ecb0f 100644 --- a/ternary/ternary_axes_subplot.py +++ b/ternary/ternary_axes_subplot.py @@ -295,7 +295,8 @@ def gridlines(self, multiple=None, horizontal_kwargs=None, left_kwargs=None, def line(self, p1, p2, **kwargs): ax = self.get_axes() - lines.line(ax, p1, p2, **kwargs) + permutation = self._permutation + lines.line(ax, p1, p2, permutation=permutation, **kwargs) def horizontal_line(self, i, **kwargs): ax = self.get_axes() From d0d4da0ca3bd8acbd0c9f714822a204689b76c00 Mon Sep 17 00:00:00 2001 From: tgwoodcock Date: Tue, 26 Apr 2022 15:36:28 +0200 Subject: [PATCH 02/20] Added functions for truncating the simplex Some plots are mostly in one region of the simplex and using truncation, we can cut off one or more corners of the simplex to save whitespace. --- ternary/helpers.py | 21 ++++ ternary/lines.py | 167 +++++++++++++++++++++++++++----- ternary/ternary_axes_subplot.py | 89 ++++++++++++++--- 3 files changed, 242 insertions(+), 35 deletions(-) diff --git a/ternary/helpers.py b/ternary/helpers.py index 660c046..efad7bf 100644 --- a/ternary/helpers.py +++ b/ternary/helpers.py @@ -218,3 +218,24 @@ def convert_coordinates_sequence(qs, scale, limits, axisorder): conversion = get_conversion(scale, limits) return [convert_coordinates(q, conversion, axisorder) for q in qs] + + +def get_axis_min_max(truncation, scale): + """ + Get the min and max values in scale coords given various + truncation points. + + !! Assumes the truncation lines do NOT cross each other!! + + truncation: dict (see main module) + """ + axis_min_max = {'b' : [0,scale], + 'r' : [0,scale], + 'l' : [0,scale] + } + + for k in truncation.keys(): + axis_min_max[k[0]][1] = truncation[k] + axis_min_max[k[1]][0] = scale-truncation[k] + + return axis_min_max diff --git a/ternary/lines.py b/ternary/lines.py index 436798c..b2c0fcf 100644 --- a/ternary/lines.py +++ b/ternary/lines.py @@ -31,7 +31,7 @@ def line(ax, p1, p2, permutation=None, **kwargs): ax.add_line(Line2D((pp1[0], pp2[0]), (pp1[1], pp2[1]), **kwargs)) -def horizontal_line(ax, scale, i, **kwargs): +def horizontal_line(ax, scale, i, axis_min_max=None, **kwargs): """ Draws the i-th horizontal line parallel to the lower axis. @@ -43,16 +43,36 @@ def horizontal_line(ax, scale, i, **kwargs): Simplex scale size. i: float The index of the line to draw + axis_min_max: dict + giving the min max values of the axes in the case of truncation kwargs: Dictionary Any kwargs to pass through to Matplotlib. """ + if not axis_min_max: + p1 = (0, i, scale - i) + p2 = (scale - i, i, 0) + line(ax, p1, p2, **kwargs) - p1 = (0, i, scale - i) - p2 = (scale - i, i, 0) - line(ax, p1, p2, **kwargs) + else: + if i <= axis_min_max['r'][1]: + if i < scale-axis_min_max['l'][1]: + p1 = (axis_min_max['b'][0] - i, + i, + axis_min_max['l'][1]) + else: + p1 = (0, i, scale-i) + + if i < axis_min_max['r'][0]: + p2 = (axis_min_max['b'][1], + i, + scale - axis_min_max['b'][1] - i) + else: + p2 = (scale - i, i, 0) + line(ax, p1, p2, **kwargs) + -def left_parallel_line(ax, scale, i, **kwargs): +def left_parallel_line(ax, scale, i, axis_min_max=None, **kwargs): """ Draws the i-th line parallel to the left axis. @@ -64,16 +84,36 @@ def left_parallel_line(ax, scale, i, **kwargs): Simplex scale size. i: float The index of the line to draw + axis_min_max: dict + giving the min max values of the axes in the case of truncation kwargs: Dictionary Any kwargs to pass through to Matplotlib. """ + if not axis_min_max: + p1 = (i, scale - i, 0) + p2 = (i, 0, scale - i) + line(ax, p1, p2, **kwargs) + + else: + if i <= axis_min_max['b'][1]: + if i < scale-axis_min_max['r'][1]: + p1 = (i, + axis_min_max['r'][1], + axis_min_max['l'][0] - i) + else: + p1 = (i, scale - i, 0) + + if i < axis_min_max['b'][0]: + p2 = (i, + scale - axis_min_max['l'][1] - i, + axis_min_max['l'][1]) + else: + p2 = (i, 0, scale - i) - p1 = (i, scale - i, 0) - p2 = (i, 0, scale - i) - line(ax, p1, p2, **kwargs) + line(ax, p1, p2, **kwargs) -def right_parallel_line(ax, scale, i, **kwargs): +def right_parallel_line(ax, scale, i, axis_min_max=None, **kwargs): """ Draws the i-th line parallel to the right axis. @@ -85,18 +125,39 @@ def right_parallel_line(ax, scale, i, **kwargs): Simplex scale size. i: float The index of the line to draw + axis_min_max: dict + giving the min max values of the axes in the case of truncation kwargs: Dictionary Any kwargs to pass through to Matplotlib. """ + if not axis_min_max: + p1 = (0, scale - i, i) + p2 = (scale - i, 0, i) + line(ax, p1, p2, **kwargs) - p1 = (0, scale - i, i) - p2 = (scale - i, 0, i) - line(ax, p1, p2, **kwargs) + + else: + if i <= axis_min_max['l'][1]: + if i < axis_min_max['l'][0]: + p1 = (scale - axis_min_max['r'][1] - i, + axis_min_max['r'][1], + i) + else: + p1 = (0, scale - i, i) + + if i < scale - axis_min_max['b'][1]: + p2 = (axis_min_max['b'][1], + scale - axis_min_max['b'][1] - i, + i) + else: + p2 = (scale - i, 0, i) + + line(ax, p1, p2, **kwargs) ## Boundary, Gridlines ## -def boundary(ax, scale, axes_colors=None, **kwargs): +def boundary(ax, scale, axis_min_max=None, axes_colors=None, **kwargs): """ Plots the boundary of the simplex. Creates and returns matplotlib axis if none given. @@ -107,23 +168,44 @@ def boundary(ax, scale, axes_colors=None, **kwargs): The subplot to draw on. scale: float Simplex scale size. - kwargs: - Any kwargs to pass through to matplotlib. + axis_min_max: dict + giving the min max values of the axes in the case of truncation axes_colors: dict Option for coloring boundaries different colors. e.g. {'l': 'g'} for coloring the left axis boundary green + kwargs: + Any kwargs to pass through to matplotlib. """ - # Set default color as black. + # set default color as black if axes_colors is None: axes_colors = dict() for _axis in ['l', 'r', 'b']: if _axis not in axes_colors.keys(): axes_colors[_axis] = 'black' - horizontal_line(ax, scale, 0, color=axes_colors['b'], **kwargs) - left_parallel_line(ax, scale, 0, color=axes_colors['l'], **kwargs) - right_parallel_line(ax, scale, 0, color=axes_colors['r'], **kwargs) + horizontal_line(ax, scale, 0, axis_min_max=axis_min_max, + color=axes_colors['b'], **kwargs) + left_parallel_line(ax, scale, 0, axis_min_max=axis_min_max, + color=axes_colors['l'], **kwargs) + right_parallel_line(ax, scale, 0, axis_min_max=axis_min_max, + color=axes_colors['r'], **kwargs) + + if axis_min_max: + if axis_min_max['r'][1] < scale: + horizontal_line(ax, scale, axis_min_max['r'][1], + axis_min_max=axis_min_max, + color=axes_colors['r'], **kwargs) + if axis_min_max['b'][1] < scale: + left_parallel_line(ax, scale, axis_min_max['b'][1], + axis_min_max=axis_min_max, + color=axes_colors['b'], **kwargs) + if axis_min_max['l'][1] < scale: + right_parallel_line(ax, scale, axis_min_max['l'][1], + axis_min_max=axis_min_max, + color=axes_colors['l'], **kwargs) + + return ax @@ -147,8 +229,9 @@ def merge_dicts(base, updates): return z -def gridlines(ax, scale, multiple=None, horizontal_kwargs=None, - left_kwargs=None, right_kwargs=None, **kwargs): +def gridlines(ax, scale, axis_min_max=None, multiple=None, + horizontal_kwargs=None, left_kwargs=None, right_kwargs=None, + **kwargs): """ Plots grid lines excluding boundary. @@ -158,6 +241,8 @@ def gridlines(ax, scale, multiple=None, horizontal_kwargs=None, The subplot to draw on. scale: float Simplex scale size. + axis_min_max: dict + giving the min max values of the axes in the case of truncation multiple: float, None Specifies which inner gridelines to draw. For example, if scale=30 and multiple=6, only 5 inner gridlines will be drawn. @@ -184,11 +269,14 @@ def gridlines(ax, scale, multiple=None, horizontal_kwargs=None, ## Draw grid-lines # Parallel to horizontal axis for i in arange(0, scale, multiple): - horizontal_line(ax, scale, i, **horizontal_kwargs) + horizontal_line(ax, scale, i, axis_min_max=axis_min_max, + **horizontal_kwargs) # Parallel to left and right axes for i in arange(0, scale + multiple, multiple): - left_parallel_line(ax, scale, i, **left_kwargs) - right_parallel_line(ax, scale, i, **right_kwargs) + left_parallel_line(ax, scale, i, axis_min_max=axis_min_max, + **left_kwargs) + right_parallel_line(ax, scale, i, axis_min_max=axis_min_max, + **right_kwargs) return ax @@ -337,3 +425,34 @@ def ticks(ax, scale, ticks=None, locations=None, multiple=1, axis='b', s = tick_formats['b'] % tick ax.text(x, y, s, horizontalalignment="center", color=axes_colors['b'], fontsize=fontsize) + + +def add_extra_tick(ax, axis, loc1, offset, scale, tick ,fontsize, **kwargs): + """ + Add an extra tick on an axis but not necessarily on + the simplex. This may be useful if a truncation is applied. + """ + toff = offset * scale + + if axis == 'r': + loc2 = (loc1[0] + toff, loc1[1], loc1[2]-toff) + text_location = (loc1[0] + 2.6 * toff, + loc1[1] - 0.5 * toff, + loc1[2] - 2.6 * toff) + + elif axis == 'l': + loc2 = (loc1[0] - toff, loc1[1] + toff, loc1[2]) + text_location = (loc1[0] - 2 * toff, + loc1[1] + 1.5 * toff, + loc1[2] + 2 * toff) + + elif axis == 'b': + loc2 = (loc1[0], loc1[1] - toff, loc1[2] + toff) + text_location = (loc1[0] - 0.5 * toff, + loc1[1] - 3.5 * toff, + loc1[2] + 2 * toff) + + + line(ax, loc1, loc2, **kwargs) + x, y = project_point(text_location) + ax.text(x,y,tick,horizontalalignment='center',fontsize=fontsize) diff --git a/ternary/ternary_axes_subplot.py b/ternary/ternary_axes_subplot.py index 24ecb0f..298a6f3 100644 --- a/ternary/ternary_axes_subplot.py +++ b/ternary/ternary_axes_subplot.py @@ -121,6 +121,33 @@ def set_axis_limits(self, axis_limits=None): def get_axis_limits(self): return self._axis_limits + + def set_truncation(self, truncation): + """ + Set one or more truncation points which will be used to truncate + the simplex i.e. cut the corners off to zoom in on a particular + region. + + truncation = dict + keys are 'br', 'rl' and/or 'lb' + vals are a value in data coords giving the maximum of the + first axis mentioned in the key. + """ + self._truncation = {} + if len(self._axis_limits.keys()) > 0: + for k in truncation.keys(): + step = (self._axis_limits[k[0]][1]- + self._axis_limits[k[0]][0])/float(self._boundary_scale) + self._truncation[k] = int((truncation[k]- + self._axis_limits[k[0]][0])/step) + + else: + self._truncation = truncation + + self._axis_min_max = get_axis_min_max(self._truncation, + self._boundary_scale) + + # Title and Axis Labels def set_title(self, title, **kwargs): @@ -354,17 +381,51 @@ def get_ticks_from_axis_limits(self, multiple=1): int(self._boundary_scale / float(multiple) + 1) ).tolist() - def set_custom_ticks(self, locations=None, clockwise=False, multiple=1, - axes_colors=None, tick_formats=None, **kwargs): + + def get_ticks_from_truncation(self): + """ + Taking self._truncation and self._boundary_scale get the scaled ticks + for all three axes and store them in self._ticks under the keys + 'b' for bottom, 'l' for left and 'r' for right axes. + """ + self._ticklocs = {} + for k in ['b','l','r']: + + gg = self._axis_min_max[k][1] - self._axis_min_max[k][0] + 1 + self._ticklocs[k] = numpy.linspace(self._axis_min_max[k][0], + self._axis_min_max[k][1], + gg).astype("int").tolist() + + q = numpy.linspace(self._axis_limits[k][0], + self._axis_limits[k][1], + self._boundary_scale+1).astype("int").tolist() + + self._ticks[k] = q[self._axis_min_max[k][0]: + self._axis_min_max[k][1]+1] + + self._ticklocs['l'] = [self._boundary_scale - i for i in self._ticklocs['l']] + self._ticks['l'].reverse() + + + def set_custom_ticks(self, locations=None, truncation=False, + clockwise=False, multiple=1, axes_colors=None, + tick_formats=None, **kwargs): """ Having called get_ticks_from_axis_limits, set the custom ticks on the plot. """ - for k in ['b', 'l', 'r']: - self.ticks(ticks=self._ticks[k], locations=locations, - axis=k, clockwise=clockwise, multiple=multiple, - axes_colors=axes_colors, tick_formats=tick_formats, - **kwargs) + if not truncation: + for k in ['b','l','r']: + self.ticks(ticks=self._ticks[k], locations=locations, + axis=k, clockwise=clockwise, multiple=multiple, + axes_colors=axes_colors, tick_formats=tick_formats, + **kwargs) + else: + for k in ['b','l','r']: + self.ticks(ticks=self._ticks[k], locations=self._ticklocs[k], + axis=k, clockwise=clockwise, multiple=multiple, + axes_colors=axes_colors, tick_formats=tick_formats, + **kwargs) def ticks(self, ticks=None, locations=None, multiple=1, axis='blr', clockwise=False, axes_colors=None, tick_formats=None, **kwargs): @@ -384,7 +445,10 @@ def resize_drawing_canvas(self, scale=None): plotting.resize_drawing_canvas(ax, scale=scale) def _redraw_labels(self): - """Redraw axis labels, typically after draw or resize events.""" + """ + Redraw axis labels, typically after draw or resize events. + """ + ax = self.get_axes() # Remove any previous labels for mpl_object in self._to_remove: @@ -394,12 +458,15 @@ def _redraw_labels(self): label_data = list(self._labels.values()) label_data.extend(self._corner_labels.values()) for (label, position, rotation, kwargs) in label_data: - transform = ax.transAxes + if not hasattr(self,"_truncation"): + transform = ax.transAxes + else: + transform = ax.transData x, y = project_point(position) # Calculate the new angle. - position = np.array([x, y]) + position = numpy.array([x, y]) new_rotation = ax.transData.transform_angles( - np.array((rotation,)), position.reshape((1, 2)))[0] + numpy.array((rotation,)), position.reshape((1, 2)))[0] text = ax.text(x, y, label, rotation=new_rotation, transform=transform, horizontalalignment="center", **kwargs) From 3b5c2e0309861ca70f120b54586999d75a1d56f5 Mon Sep 17 00:00:00 2001 From: tgwoodcock Date: Tue, 26 Apr 2022 15:45:21 +0200 Subject: [PATCH 03/20] Updated .gitignore --- .gitignore | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.gitignore b/.gitignore index 0d20b64..2c08ef6 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,10 @@ *.pyc + +# general things to ignore +build/ +dist/ +*.egg-info/ +*.egg +*.py[cod] +__pycache__/ +*.ipynb_checkpoints/ From 855359d6baf66484a2cebfc6dfd876f52cdef89d Mon Sep 17 00:00:00 2001 From: tgwoodcock Date: Tue, 26 Apr 2022 15:52:40 +0200 Subject: [PATCH 04/20] Modified TernaryAxesSubplot.boundary & .gridlines These functions now handle truncation too. --- ternary/ternary_axes_subplot.py | 35 +++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/ternary/ternary_axes_subplot.py b/ternary/ternary_axes_subplot.py index 298a6f3..5cf34b1 100644 --- a/ternary/ternary_axes_subplot.py +++ b/ternary/ternary_axes_subplot.py @@ -301,22 +301,41 @@ def annotate(self, text, position, **kwargs): # Boundary and Gridlines - def boundary(self, scale=None, axes_colors=None, **kwargs): + def boundary(self, scale=None, truncation=None, + axes_colors=None, **kwargs): # Sometimes you want to draw a bigger boundary if not scale: - scale = self._boundary_scale # defaults to self._scale + scale = self._boundary_scale # defaults to self._scale ax = self.get_axes() self.resize_drawing_canvas(scale) - lines.boundary(scale=scale, ax=ax, axes_colors=axes_colors, **kwargs) + if not truncation: + lines.boundary(scale=scale, ax=ax, axis_min_max=None, + axes_colors=axes_colors, **kwargs) + + else: + lines.boundary(scale=scale, ax=ax, axis_min_max=self._axis_min_max, + axes_colors=axes_colors, **kwargs) - def gridlines(self, multiple=None, horizontal_kwargs=None, left_kwargs=None, + def gridlines(self, multiple=None, truncation=None, + horizontal_kwargs=None, left_kwargs=None, right_kwargs=None, **kwargs): ax = self.get_axes() scale = self.get_scale() - lines.gridlines(scale=scale, multiple=multiple, - ax=ax, horizontal_kwargs=horizontal_kwargs, - left_kwargs=left_kwargs, right_kwargs=right_kwargs, - **kwargs) + if not truncation: + lines.gridlines(scale=scale, axis_min_max=None, + multiple=multiple, ax=ax, + horizontal_kwargs=horizontal_kwargs, + left_kwargs=left_kwargs, + right_kwargs=right_kwargs, + **kwargs) + + else: + lines.gridlines(scale=scale, axis_min_max=self._axis_min_max, + multiple=multiple, ax=ax, + horizontal_kwargs=horizontal_kwargs, + left_kwargs=left_kwargs, + right_kwargs=right_kwargs, + **kwargs) # Various Lines From 7fdc61666eb39fd5ab628d36317a8395f2176ab2 Mon Sep 17 00:00:00 2001 From: tgwoodcock Date: Tue, 26 Apr 2022 15:56:18 +0200 Subject: [PATCH 05/20] Updated reference to numpy as np --- ternary/ternary_axes_subplot.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ternary/ternary_axes_subplot.py b/ternary/ternary_axes_subplot.py index 5cf34b1..9d3d6b3 100644 --- a/ternary/ternary_axes_subplot.py +++ b/ternary/ternary_axes_subplot.py @@ -411,11 +411,11 @@ def get_ticks_from_truncation(self): for k in ['b','l','r']: gg = self._axis_min_max[k][1] - self._axis_min_max[k][0] + 1 - self._ticklocs[k] = numpy.linspace(self._axis_min_max[k][0], + self._ticklocs[k] = np.linspace(self._axis_min_max[k][0], self._axis_min_max[k][1], gg).astype("int").tolist() - q = numpy.linspace(self._axis_limits[k][0], + q = np.linspace(self._axis_limits[k][0], self._axis_limits[k][1], self._boundary_scale+1).astype("int").tolist() @@ -483,9 +483,9 @@ def _redraw_labels(self): transform = ax.transData x, y = project_point(position) # Calculate the new angle. - position = numpy.array([x, y]) + position = np.array([x, y]) new_rotation = ax.transData.transform_angles( - numpy.array((rotation,)), position.reshape((1, 2)))[0] + np.array((rotation,)), position.reshape((1, 2)))[0] text = ax.text(x, y, label, rotation=new_rotation, transform=transform, horizontalalignment="center", **kwargs) From 5d59d3cb965134fae8b396c2e2b0f52ff7457009 Mon Sep 17 00:00:00 2001 From: tgwoodcock Date: Wed, 27 Apr 2022 17:31:40 +0200 Subject: [PATCH 06/20] Further code changes to enable truncation --- ternary/heatmapping.py | 17 +++- ternary/helpers.py | 18 ++--- ternary/ternary_axes_subplot.py | 138 ++++++++++++++++++++------------ 3 files changed, 110 insertions(+), 63 deletions(-) diff --git a/ternary/heatmapping.py b/ternary/heatmapping.py index 7487f69..63d051d 100644 --- a/ternary/heatmapping.py +++ b/ternary/heatmapping.py @@ -318,7 +318,7 @@ def heatmapf(func, scale=10, boundary=True, cmap=None, ax=None, # Pass everything to the heatmapper ax = heatmap(data, scale, cmap=cmap, ax=ax, style=style, scientific=scientific, colorbar=colorbar, - permutation=permutation, vmin=vmin, vmax=vmax, + permutation=permutation, vmin=vmin, vmax=vmax, cbarlabel=cbarlabel, cb_kwargs=cb_kwargs) return ax @@ -411,9 +411,20 @@ def svg_heatmap(data, scale, filename, vmax=None, vmin=None, style='h', output_file.write('\n') -def background_color(ax, color, scale, zorder=-1000, alpha=None): +def background_color(ax, color, scale, axis_min_max, zorder=-1000, alpha=None): """Draws a triangle behind the plot to serve as the background color.""" - vertices = [(scale, 0, 0), (0, scale, 0), (0, 0, scale)] + vertices = [(axis_min_max["b"][1], 0, scale - axis_min_max["b"][1]), + (axis_min_max["b"][1], axis_min_max["r"][0], 0), + (scale - axis_min_max["r"][1], axis_min_max["r"][1], 0), + (0, axis_min_max["r"][1], axis_min_max["l"][0]), + (0, scale - axis_min_max["l"][1], axis_min_max["l"][1]), + (axis_min_max["b"][0], 0, axis_min_max["l"][1]) + ] + + _, idx = np.unique(vertices, return_index=True, axis=0) + idx.sort() + vertices = [vertices[i] for i in idx] + vertices = map(project_point, vertices) xs, ys = unzip(vertices) poly = ax.fill(xs, ys, facecolor=color, edgecolor=color, zorder=zorder, alpha=alpha) diff --git a/ternary/helpers.py b/ternary/helpers.py index efad7bf..bc2f0fb 100644 --- a/ternary/helpers.py +++ b/ternary/helpers.py @@ -112,7 +112,7 @@ def planar_to_coordinates(p, scale): ---------- p: 2-tuple The planar simplex (x, y) point to be transformed to maps (x,y,z) coordinates - + scale: Int The normalized scale of the simplex, i.e. N such that points (x,y,z) satisfy x + y + z == N @@ -122,8 +122,8 @@ def planar_to_coordinates(p, scale): x = p[0] - y / 2. z = scale - x - y return np.array([x, y, z]) - - + + def project_sequence(s, permutation=None): """ Projects a point or sequence of points using `project_point` to lists xs, ys @@ -136,7 +136,7 @@ def project_sequence(s, permutation=None): Returns ------- - xs, ys: The sequence of projected points in coordinates as two lists + xs, ys: The sequence of projected points in coordinates as two lists """ xs, ys = unzip([project_point(p, permutation=permutation) for p in s]) @@ -188,7 +188,7 @@ def get_conversion(scale, limits): "r": lambda x: (x - limits['r'][0]) * fr} return conversion - + def convert_coordinates_sequence(qs, scale, limits, axisorder): """ @@ -216,7 +216,7 @@ def convert_coordinates_sequence(qs, scale, limits, axisorder): the points converted to simplex coordinates """ conversion = get_conversion(scale, limits) - + return [convert_coordinates(q, conversion, axisorder) for q in qs] @@ -230,9 +230,9 @@ def get_axis_min_max(truncation, scale): truncation: dict (see main module) """ axis_min_max = {'b' : [0,scale], - 'r' : [0,scale], - 'l' : [0,scale] - } + 'r' : [0,scale], + 'l' : [0,scale] + } for k in truncation.keys(): axis_min_max[k[0]][1] = truncation[k] diff --git a/ternary/ternary_axes_subplot.py b/ternary/ternary_axes_subplot.py index 9d3d6b3..bc93742 100644 --- a/ternary/ternary_axes_subplot.py +++ b/ternary/ternary_axes_subplot.py @@ -11,7 +11,7 @@ from . import heatmapping from . import lines from . import plotting -from .helpers import project_point, convert_coordinates_sequence +from .helpers import project_point, convert_coordinates_sequence, get_axis_min_max BackgroundParameters = namedtuple('BackgroundParameters', ['color', 'alpha', 'zorder']) @@ -71,6 +71,17 @@ def __init__(self, ax=None, scale=None, permutation=None): self._labels = dict() self._corner_labels = dict() self._ticks = dict() + # Container for data limits for the axes. Custom limits can + # be set by the user + self.set_axis_limits({"b" : [0, self._scale], + "r" : [0, self._scale], + "l" : [0, self._scale]}) + + # Container for parameters describing a possible truncation + self._truncation = dict() + self._axis_min_max = {"b" : [0, self._scale], + "r" : [0, self._scale], + "l" : [0, self._scale]} # Container for the redrawing of labels self._to_remove = [] self._connect_callbacks() @@ -111,6 +122,8 @@ def set_axis_limits(self, axis_limits=None): """ Set min and max data limits for each of the three axes. + max-min for each axis must be the same as the self._scale + axis_limits = dict keys are 'b','l' and 'r' for the three axes vals are lists of the min and max values for the axis in @@ -119,35 +132,38 @@ def set_axis_limits(self, axis_limits=None): self._axis_limits = axis_limits def get_axis_limits(self): + """Get the data limits for each axis""" return self._axis_limits + def get_axis_min_max(self): + """Get the simplex limits for each axis""" + return self._axis_min_max + def set_truncation(self, truncation): """ - Set one or more truncation points which will be used to truncate + Set one or more truncation lines which will be used to truncate the simplex i.e. cut the corners off to zoom in on a particular region. truncation = dict keys are 'br', 'rl' and/or 'lb' - vals are a value in data coords giving the maximum of the + values are a value in data coords giving the maximum of the first axis mentioned in the key. """ - self._truncation = {} - if len(self._axis_limits.keys()) > 0: - for k in truncation.keys(): - step = (self._axis_limits[k[0]][1]- - self._axis_limits[k[0]][0])/float(self._boundary_scale) - self._truncation[k] = int((truncation[k]- - self._axis_limits[k[0]][0])/step) - - else: - self._truncation = truncation + for k in truncation.keys(): + step = (self._axis_limits[k[0]][1]- + self._axis_limits[k[0]][0])/float(self._boundary_scale) + self._truncation[k] = int((truncation[k]- + self._axis_limits[k[0]][0])/step) self._axis_min_max = get_axis_min_max(self._truncation, self._boundary_scale) + self._draw_background() + + def get_truncation(self): + return self._truncation - # Title and Axis Labels def set_title(self, title, **kwargs): @@ -155,8 +171,8 @@ def set_title(self, title, **kwargs): ax = self.get_axes() ax.set_title(title, **kwargs) - def left_axis_label(self, label, position=None, rotation=60, offset=0.08, - **kwargs): + def left_axis_label(self, label, position=None, rotation=60, offset=0.08, + transform_type="transData", **kwargs): """ Sets the label on the left axis. @@ -176,10 +192,12 @@ def left_axis_label(self, label, position=None, rotation=60, offset=0.08, if not position: position = (-offset, 3./5, 2./5) - self._labels["left"] = (label, position, rotation, kwargs) + transform_type = "transAxes" + self._labels["left"] = (label, position, rotation, + transform_type, kwargs) def right_axis_label(self, label, position=None, rotation=-60, offset=0.08, - **kwargs): + transform_type="transData", **kwargs): """ Sets the label on the right axis. @@ -200,10 +218,12 @@ def right_axis_label(self, label, position=None, rotation=-60, offset=0.08, if not position: position = (2. / 5 + offset, 3. / 5, 0) - self._labels["right"] = (label, position, rotation, kwargs) + transform_type = "transAxes" + self._labels["right"] = (label, position, rotation, + transform_type, kwargs) def bottom_axis_label(self, label, position=None, rotation=0, offset=0.02, - **kwargs): + transform_type="transData", **kwargs): """ Sets the label on the bottom axis. @@ -223,10 +243,12 @@ def bottom_axis_label(self, label, position=None, rotation=0, offset=0.02, if not position: position = (0.5, -offset / 2., 0.5) - self._labels["bottom"] = (label, position, rotation, kwargs) + transform_type = "transAxes" + self._labels["bottom"] = (label, position, rotation, + transform_type, kwargs) def right_corner_label(self, label, position=None, rotation=0, offset=0.08, - **kwargs): + transform_type="transData", **kwargs): """ Sets the label on the right corner (complements left axis). @@ -246,10 +268,12 @@ def right_corner_label(self, label, position=None, rotation=0, offset=0.08, if not position: position = (1, offset / 2, 0) - self._corner_labels["right"] = (label, position, rotation, kwargs) + transform_type = "transAxes" + self._corner_labels["right"] = (label, position, rotation, + transform_type, kwargs) def left_corner_label(self, label, position=None, rotation=0, offset=0.08, - **kwargs): + transform_type="transData", **kwargs): """ Sets the label on the left corner (complements right axis.) @@ -269,10 +293,12 @@ def left_corner_label(self, label, position=None, rotation=0, offset=0.08, if not position: position = (-offset / 2, offset / 2, 0) - self._corner_labels["left"] = (label, position, rotation, kwargs) + transform_type="transAxes" + self._corner_labels["left"] = (label, position, rotation, + transform_type, kwargs) def top_corner_label(self, label, position=None, rotation=0, offset=0.2, - **kwargs): + transform_type="transData", **kwargs): """ Sets the label on the bottom axis. @@ -292,7 +318,9 @@ def top_corner_label(self, label, position=None, rotation=0, offset=0.2, if not position: position = (-offset / 2, 1 + offset, 0) - self._corner_labels["top"] = (label, position, rotation, kwargs) + transform_type="transAxes" + self._corner_labels["top"] = (label, position, rotation, + transform_type, kwargs) def annotate(self, text, position, **kwargs): ax = self.get_axes() @@ -301,14 +329,13 @@ def annotate(self, text, position, **kwargs): # Boundary and Gridlines - def boundary(self, scale=None, truncation=None, - axes_colors=None, **kwargs): + def boundary(self, scale=None, axes_colors=None, **kwargs): # Sometimes you want to draw a bigger boundary if not scale: scale = self._boundary_scale # defaults to self._scale ax = self.get_axes() self.resize_drawing_canvas(scale) - if not truncation: + if not self._truncation: lines.boundary(scale=scale, ax=ax, axis_min_max=None, axes_colors=axes_colors, **kwargs) @@ -316,12 +343,11 @@ def boundary(self, scale=None, truncation=None, lines.boundary(scale=scale, ax=ax, axis_min_max=self._axis_min_max, axes_colors=axes_colors, **kwargs) - def gridlines(self, multiple=None, truncation=None, - horizontal_kwargs=None, left_kwargs=None, - right_kwargs=None, **kwargs): + def gridlines(self, multiple=None, horizontal_kwargs=None, + left_kwargs=None, right_kwargs=None, **kwargs): ax = self.get_axes() scale = self.get_scale() - if not truncation: + if not self._truncation: lines.gridlines(scale=scale, axis_min_max=None, multiple=multiple, ax=ax, horizontal_kwargs=horizontal_kwargs, @@ -403,8 +429,8 @@ def get_ticks_from_axis_limits(self, multiple=1): def get_ticks_from_truncation(self): """ - Taking self._truncation and self._boundary_scale get the scaled ticks - for all three axes and store them in self._ticks under the keys + Taking self._axis_min_max and self._boundary_scale get the scaled + ticks for all three axes and store them in self._ticks under the keys 'b' for bottom, 'l' for left and 'r' for right axes. """ self._ticklocs = {} @@ -412,12 +438,12 @@ def get_ticks_from_truncation(self): gg = self._axis_min_max[k][1] - self._axis_min_max[k][0] + 1 self._ticklocs[k] = np.linspace(self._axis_min_max[k][0], - self._axis_min_max[k][1], - gg).astype("int").tolist() + self._axis_min_max[k][1], + gg).astype("int").tolist() q = np.linspace(self._axis_limits[k][0], - self._axis_limits[k][1], - self._boundary_scale+1).astype("int").tolist() + self._axis_limits[k][1], + self._boundary_scale+1).astype("int").tolist() self._ticks[k] = q[self._axis_min_max[k][0]: self._axis_min_max[k][1]+1] @@ -425,15 +451,14 @@ def get_ticks_from_truncation(self): self._ticklocs['l'] = [self._boundary_scale - i for i in self._ticklocs['l']] self._ticks['l'].reverse() - - def set_custom_ticks(self, locations=None, truncation=False, - clockwise=False, multiple=1, axes_colors=None, - tick_formats=None, **kwargs): + + def set_custom_ticks(self, locations=None, clockwise=False, multiple=1, + axes_colors=None, tick_formats=None, **kwargs): """ Having called get_ticks_from_axis_limits, set the custom ticks on the plot. """ - if not truncation: + if not self._truncation: for k in ['b','l','r']: self.ticks(ticks=self._ticks[k], locations=locations, axis=k, clockwise=clockwise, multiple=multiple, @@ -455,6 +480,12 @@ def ticks(self, ticks=None, locations=None, multiple=1, axis='blr', axes_colors=axes_colors, tick_formats=tick_formats, **kwargs) + def add_extra_tick(self, axis, loc1, offset, scale, tick, fontsize, + **kwargs): + ax = self.get_axes() + lines.add_extra_tick(ax, axis, loc1, offset, scale, tick, fontsize, + **kwargs) + # Redrawing and resizing def resize_drawing_canvas(self, scale=None): @@ -476,16 +507,17 @@ def _redraw_labels(self): # Redraw the labels with the appropriate angles label_data = list(self._labels.values()) label_data.extend(self._corner_labels.values()) - for (label, position, rotation, kwargs) in label_data: - if not hasattr(self,"_truncation"): + for (label, position, rotation, transform_type, kwargs) in label_data: + if transform_type == "transAxes": transform = ax.transAxes - else: + elif transform_type == "transData": transform = ax.transData + x, y = project_point(position) # Calculate the new angle. position = np.array([x, y]) - new_rotation = ax.transData.transform_angles( - np.array((rotation,)), position.reshape((1, 2)))[0] + new_rotation = ax.transData.transform_angles(np.array((rotation,)), + position.reshape((1, 2)))[0] text = ax.text(x, y, label, rotation=new_rotation, transform=transform, horizontalalignment="center", **kwargs) @@ -559,10 +591,14 @@ def _draw_background(self): color, alpha, zorder = self._background_parameters scale = self.get_scale() ax = self.get_axes() + axis_min_max = self.get_axis_min_max() # Remove any existing background if self._background_triangle: self._background_triangle.remove() # Draw the background - self._background_triangle = heatmapping.background_color(ax, color, scale, alpha=alpha, zorder=zorder)[0] + self._background_triangle = heatmapping.background_color(ax, color, scale, + axis_min_max, + alpha=alpha, + zorder=zorder)[0] From 49eaae0c448fa5bde33b1e963c7044884bcd0547 Mon Sep 17 00:00:00 2001 From: tgwoodcock Date: Wed, 27 Apr 2022 17:44:07 +0200 Subject: [PATCH 07/20] Added truncated_simplex_example.py to examples --- examples/truncated_simplex_example.py | 111 ++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 examples/truncated_simplex_example.py diff --git a/examples/truncated_simplex_example.py b/examples/truncated_simplex_example.py new file mode 100644 index 0000000..a44e115 --- /dev/null +++ b/examples/truncated_simplex_example.py @@ -0,0 +1,111 @@ +import ternary + +## Simple example with one corner truncated +## Boundary and Gridlines +scale = 10 +figure, tax = ternary.figure(scale=scale) +#tax.set_truncation({'rl' : 8}) +tax.ax.axis("off") + + +# Draw Boundary and Gridlines +tax.boundary(linewidth=0.25) +tax.gridlines(color="black", multiple=1, linewidth=0.25, ls='-') + +# Set Axis labels and Title + +tax.left_axis_label(r"$\longleftarrow$ Hogs", fontsize=10) +tax.right_axis_label(r"$\longleftarrow$ Dogs", fontsize=10) +tax.bottom_axis_label(r"Logs $\longrightarrow$", fontsize=10, offset=0.08) + + +# Set custom ticks +#tax.get_ticks_from_truncation(multiple=3) +# tax.get_ticks_from_truncation() +# offset=0.013 +# tax.set_custom_ticks(fontsize=8, offset=offset, +# tick_formats="%.1f", linewidth=0.25) + + +tax.ax.axis("scaled") +tax._redraw_labels() + + + + + + +## Boundary and Gridlines +scale = 14 +figure, tax = ternary.figure(scale=scale) +figure.set_facecolor('w') +w_i,h_i = 3.15, 2.36 +figure.set_size_inches((w_i,h_i)) +figure.set_dpi(150) +tax.set_axis_limits({'b':[52,59],'l':[41,48],'r':[0,7]}) +tax.set_truncation({'br' : 57.5, 'rl' : 4, 'lb' : 46.5}) +tax.ax.axis("off") +left = 0 +right = 1 +bottom = 0 +top = 1 +figure.subplots_adjust(left=left,right=right,bottom=bottom,top=top) + +# Draw Boundary and Gridlines +tax.boundary(linewidth=0.25) +tax.gridlines(color="black", multiple=1, linewidth=0.25, ls='-') + +# Set Axis labels and Title +fontsize = 10 +qs = [(55.9, -0.62, 44.5),(56.1, 3.30, 40.5),(51.5, 3.35, 45.0)] + +pos = ternary.helpers.convert_coordinates_sequence(qs,tax._boundary_scale, + tax._axis_limits, + axisorder='brl') +tax.left_axis_label(r"$\longleftarrow$ Hogs", + position=pos[2],fontsize=fontsize) +tax.right_axis_label(r"$\longleftarrow$ Dogs", + position=pos[1],fontsize=fontsize) +tax.bottom_axis_label(r"Logs $\longrightarrow$", + position=pos[0],fontsize=fontsize) + + +# Set custom ticks +#tax.get_ticks_from_truncation() +#tax.get_ticks_from_axis_limits() +ticks = {'b' : [54,55,56,57], + 'r' : [2,3,4], + 'l' : [46,45,44]} +ticklocs = {'b' : [4,6,8,10], + 'r' : [4,6,8], + 'l' : [8,6,4]} +tax._ticks = ticks +tax._ticklocs = ticklocs + +offset=0.013 +tax.set_custom_ticks(fontsize=8, offset=offset, + tick_formats=None, linewidth=0.25) + + +tax.add_extra_tick('r',(11,2,1),offset,scale,'1',fontsize=8,color='k', + linewidth=0.25) +tax.add_extra_tick('r',(11,0,3),offset,scale,'0',fontsize=8,color='k', + linewidth=0.25) + +tax.add_extra_tick('l',(2,8,4),offset,scale,'43',fontsize=8,color='k', + linewidth=0.25) +tax.add_extra_tick('l',(4,8,2),offset,scale,'42',fontsize=8,color='k', + linewidth=0.25) +tax.add_extra_tick('l',(6,8,0),offset,scale,'41',fontsize=8,color='k', + linewidth=0.25) + +tax.add_extra_tick('b',(2,1,11),offset,scale,'53',fontsize=8,color='k', + linewidth=0.25) + + +tax.ax.axis("scaled") +tax.ax.axis([1.1, 12.8, -0.7, 7.4]) +tax.ax.set_position([0,0.02,1,1]) +tax._redraw_labels() + +ternary.plt.show() From 6deedfb88a08048e47bda371e341effb4f44ebb9 Mon Sep 17 00:00:00 2001 From: tgwoodcock Date: Fri, 12 Aug 2022 16:09:50 +0200 Subject: [PATCH 08/20] Changes to boundary and gridlines We now have axis_min_max as n attribute during init, therefore we don't need to check for it in the functions for droawing the boundary and the gridlines both in lines and in ternary_axes_subplot. Also updated or added some docstrings to some functions. --- examples/truncated_simplex_example.py | 12 +- ternary/helpers.py | 6 +- ternary/lines.py | 186 ++++++++++++-------------- ternary/ternary_axes_subplot.py | 77 ++++++++--- 4 files changed, 152 insertions(+), 129 deletions(-) diff --git a/examples/truncated_simplex_example.py b/examples/truncated_simplex_example.py index a44e115..b388bdf 100644 --- a/examples/truncated_simplex_example.py +++ b/examples/truncated_simplex_example.py @@ -4,7 +4,7 @@ ## Boundary and Gridlines scale = 10 figure, tax = ternary.figure(scale=scale) -#tax.set_truncation({'rl' : 8}) +tax.set_truncation({'rl' : 8}) tax.ax.axis("off") @@ -20,11 +20,11 @@ # Set custom ticks -#tax.get_ticks_from_truncation(multiple=3) -# tax.get_ticks_from_truncation() -# offset=0.013 -# tax.set_custom_ticks(fontsize=8, offset=offset, -# tick_formats="%.1f", linewidth=0.25) +tax.get_ticks_from_truncation(multiple=3) +tax.get_ticks_from_truncation() +offset=0.013 +tax.set_custom_ticks(fontsize=8, offset=offset, + tick_formats="%.1f", linewidth=0.25) tax.ax.axis("scaled") diff --git a/ternary/helpers.py b/ternary/helpers.py index bc2f0fb..2fe1734 100644 --- a/ternary/helpers.py +++ b/ternary/helpers.py @@ -229,9 +229,9 @@ def get_axis_min_max(truncation, scale): truncation: dict (see main module) """ - axis_min_max = {'b' : [0,scale], - 'r' : [0,scale], - 'l' : [0,scale] + axis_min_max = {'b' : [0, scale], + 'r' : [0, scale], + 'l' : [0, scale] } for k in truncation.keys(): diff --git a/ternary/lines.py b/ternary/lines.py index b2c0fcf..80c02c5 100644 --- a/ternary/lines.py +++ b/ternary/lines.py @@ -31,7 +31,7 @@ def line(ax, p1, p2, permutation=None, **kwargs): ax.add_line(Line2D((pp1[0], pp2[0]), (pp1[1], pp2[1]), **kwargs)) -def horizontal_line(ax, scale, i, axis_min_max=None, **kwargs): +def horizontal_line(ax, scale, i, axis_min_max, **kwargs): """ Draws the i-th horizontal line parallel to the lower axis. @@ -44,35 +44,31 @@ def horizontal_line(ax, scale, i, axis_min_max=None, **kwargs): i: float The index of the line to draw axis_min_max: dict - giving the min max values of the axes in the case of truncation + The min and max values of the axes in simplex coordinates. + These may not be equal to (0, scale) if a truncation has been + applied. kwargs: Dictionary Any kwargs to pass through to Matplotlib. """ - if not axis_min_max: - p1 = (0, i, scale - i) - p2 = (scale - i, i, 0) - line(ax, p1, p2, **kwargs) - - else: - if i <= axis_min_max['r'][1]: - if i < scale-axis_min_max['l'][1]: - p1 = (axis_min_max['b'][0] - i, - i, - axis_min_max['l'][1]) - else: - p1 = (0, i, scale-i) + if i <= axis_min_max['r'][1]: + if i < scale-axis_min_max['l'][1]: + p1 = (axis_min_max['b'][0] - i, + i, + axis_min_max['l'][1]) + else: + p1 = (0, i, scale-i) + + if i < axis_min_max['r'][0]: + p2 = (axis_min_max['b'][1], + i, + scale - axis_min_max['b'][1] - i) + else: + p2 = (scale - i, i, 0) - if i < axis_min_max['r'][0]: - p2 = (axis_min_max['b'][1], - i, - scale - axis_min_max['b'][1] - i) - else: - p2 = (scale - i, i, 0) + line(ax, p1, p2, **kwargs) - line(ax, p1, p2, **kwargs) - -def left_parallel_line(ax, scale, i, axis_min_max=None, **kwargs): +def left_parallel_line(ax, scale, i, axis_min_max, **kwargs): """ Draws the i-th line parallel to the left axis. @@ -85,32 +81,28 @@ def left_parallel_line(ax, scale, i, axis_min_max=None, **kwargs): i: float The index of the line to draw axis_min_max: dict - giving the min max values of the axes in the case of truncation + The min and max values of the axes in simplex coordinates. + These may not be equal to (0, scale) if a truncation has been + applied. kwargs: Dictionary Any kwargs to pass through to Matplotlib. """ - if not axis_min_max: - p1 = (i, scale - i, 0) - p2 = (i, 0, scale - i) - line(ax, p1, p2, **kwargs) + if i <= axis_min_max['b'][1]: + if i < scale-axis_min_max['r'][1]: + p1 = (i, + axis_min_max['r'][1], + axis_min_max['l'][0] - i) + else: + p1 = (i, scale - i, 0) + + if i < axis_min_max['b'][0]: + p2 = (i, + scale - axis_min_max['l'][1] - i, + axis_min_max['l'][1]) + else: + p2 = (i, 0, scale - i) - else: - if i <= axis_min_max['b'][1]: - if i < scale-axis_min_max['r'][1]: - p1 = (i, - axis_min_max['r'][1], - axis_min_max['l'][0] - i) - else: - p1 = (i, scale - i, 0) - - if i < axis_min_max['b'][0]: - p2 = (i, - scale - axis_min_max['l'][1] - i, - axis_min_max['l'][1]) - else: - p2 = (i, 0, scale - i) - - line(ax, p1, p2, **kwargs) + line(ax, p1, p2, **kwargs) def right_parallel_line(ax, scale, i, axis_min_max=None, **kwargs): @@ -126,38 +118,33 @@ def right_parallel_line(ax, scale, i, axis_min_max=None, **kwargs): i: float The index of the line to draw axis_min_max: dict - giving the min max values of the axes in the case of truncation + The min and max values of the axes in simplex coordinates. + These may not be equal to (0, scale) if a truncation has been + applied. kwargs: Dictionary Any kwargs to pass through to Matplotlib. """ - if not axis_min_max: - p1 = (0, scale - i, i) - p2 = (scale - i, 0, i) - line(ax, p1, p2, **kwargs) - - - else: - if i <= axis_min_max['l'][1]: - if i < axis_min_max['l'][0]: - p1 = (scale - axis_min_max['r'][1] - i, - axis_min_max['r'][1], - i) - else: - p1 = (0, scale - i, i) - - if i < scale - axis_min_max['b'][1]: - p2 = (axis_min_max['b'][1], - scale - axis_min_max['b'][1] - i, - i) - else: - p2 = (scale - i, 0, i) + if i <= axis_min_max['l'][1]: + if i < axis_min_max['l'][0]: + p1 = (scale - axis_min_max['r'][1] - i, + axis_min_max['r'][1], + i) + else: + p1 = (0, scale - i, i) + + if i < scale - axis_min_max['b'][1]: + p2 = (axis_min_max['b'][1], + scale - axis_min_max['b'][1] - i, + i) + else: + p2 = (scale - i, 0, i) - line(ax, p1, p2, **kwargs) + line(ax, p1, p2, **kwargs) ## Boundary, Gridlines ## -def boundary(ax, scale, axis_min_max=None, axes_colors=None, **kwargs): +def boundary(ax, scale, axis_min_max, axes_colors=None, **kwargs): """ Plots the boundary of the simplex. Creates and returns matplotlib axis if none given. @@ -169,7 +156,9 @@ def boundary(ax, scale, axis_min_max=None, axes_colors=None, **kwargs): scale: float Simplex scale size. axis_min_max: dict - giving the min max values of the axes in the case of truncation + The min and max values of the axes in simplex coordinates. + These may not be equal to (0, scale) if a truncation has been + applied. axes_colors: dict Option for coloring boundaries different colors. e.g. {'l': 'g'} for coloring the left axis boundary green @@ -184,28 +173,27 @@ def boundary(ax, scale, axis_min_max=None, axes_colors=None, **kwargs): if _axis not in axes_colors.keys(): axes_colors[_axis] = 'black' - horizontal_line(ax, scale, 0, axis_min_max=axis_min_max, + horizontal_line(ax, scale, 0, axis_min_max, color=axes_colors['b'], **kwargs) - left_parallel_line(ax, scale, 0, axis_min_max=axis_min_max, + left_parallel_line(ax, scale, 0, axis_min_max, color=axes_colors['l'], **kwargs) - right_parallel_line(ax, scale, 0, axis_min_max=axis_min_max, + right_parallel_line(ax, scale, 0, axis_min_max, color=axes_colors['r'], **kwargs) - if axis_min_max: - if axis_min_max['r'][1] < scale: - horizontal_line(ax, scale, axis_min_max['r'][1], + if axis_min_max['r'][1] < scale: + horizontal_line(ax, scale, axis_min_max['r'][1], + axis_min_max=axis_min_max, + color=axes_colors['r'], **kwargs) + if axis_min_max['b'][1] < scale: + left_parallel_line(ax, scale, axis_min_max['b'][1], + axis_min_max=axis_min_max, + color=axes_colors['b'], **kwargs) + if axis_min_max['l'][1] < scale: + right_parallel_line(ax, scale, axis_min_max['l'][1], axis_min_max=axis_min_max, - color=axes_colors['r'], **kwargs) - if axis_min_max['b'][1] < scale: - left_parallel_line(ax, scale, axis_min_max['b'][1], - axis_min_max=axis_min_max, - color=axes_colors['b'], **kwargs) - if axis_min_max['l'][1] < scale: - right_parallel_line(ax, scale, axis_min_max['l'][1], - axis_min_max=axis_min_max, - color=axes_colors['l'], **kwargs) - - + color=axes_colors['l'], **kwargs) + + return ax @@ -229,7 +217,7 @@ def merge_dicts(base, updates): return z -def gridlines(ax, scale, axis_min_max=None, multiple=None, +def gridlines(ax, scale, axis_min_max, multiple=None, horizontal_kwargs=None, left_kwargs=None, right_kwargs=None, **kwargs): """ @@ -242,7 +230,9 @@ def gridlines(ax, scale, axis_min_max=None, multiple=None, scale: float Simplex scale size. axis_min_max: dict - giving the min max values of the axes in the case of truncation + The min and max values of the axes in simplex coordinates. + These may not be equal to (0, scale) if a truncation has been + applied. multiple: float, None Specifies which inner gridelines to draw. For example, if scale=30 and multiple=6, only 5 inner gridlines will be drawn. @@ -269,14 +259,12 @@ def gridlines(ax, scale, axis_min_max=None, multiple=None, ## Draw grid-lines # Parallel to horizontal axis for i in arange(0, scale, multiple): - horizontal_line(ax, scale, i, axis_min_max=axis_min_max, - **horizontal_kwargs) + horizontal_line(ax, scale, i, axis_min_max, **horizontal_kwargs) # Parallel to left and right axes for i in arange(0, scale + multiple, multiple): - left_parallel_line(ax, scale, i, axis_min_max=axis_min_max, - **left_kwargs) - right_parallel_line(ax, scale, i, axis_min_max=axis_min_max, - **right_kwargs) + left_parallel_line(ax, scale, i, axis_min_max, **left_kwargs) + right_parallel_line(ax, scale, i, axis_min_max, **right_kwargs) + return ax @@ -433,9 +421,9 @@ def add_extra_tick(ax, axis, loc1, offset, scale, tick ,fontsize, **kwargs): the simplex. This may be useful if a truncation is applied. """ toff = offset * scale - - if axis == 'r': - loc2 = (loc1[0] + toff, loc1[1], loc1[2]-toff) + + if axis == 'r': + loc2 = (loc1[0] + toff, loc1[1], loc1[2]-toff) text_location = (loc1[0] + 2.6 * toff, loc1[1] - 0.5 * toff, loc1[2] - 2.6 * toff) diff --git a/ternary/ternary_axes_subplot.py b/ternary/ternary_axes_subplot.py index bc93742..9199779 100644 --- a/ternary/ternary_axes_subplot.py +++ b/ternary/ternary_axes_subplot.py @@ -330,38 +330,73 @@ def annotate(self, text, position, **kwargs): # Boundary and Gridlines def boundary(self, scale=None, axes_colors=None, **kwargs): + """ + Draw a boundary around the simplex. + + Parameters + ---------- + scale : INT, optional + An int describing the scale of the boundary to be drawn. + Sometimes you may want to draw a bigger boundary than + specified in the initialisation of the tax. The default is None. + axes_colors: dict + Option for coloring boundaries different colors. + e.g. {'l': 'g'} for coloring the left axis boundary green + **kwargs : dict + Any kwargs to pass through to matplotlib.. + + Returns + ------- + None. + + """ # Sometimes you want to draw a bigger boundary if not scale: scale = self._boundary_scale # defaults to self._scale ax = self.get_axes() self.resize_drawing_canvas(scale) - if not self._truncation: - lines.boundary(scale=scale, ax=ax, axis_min_max=None, - axes_colors=axes_colors, **kwargs) - else: - lines.boundary(scale=scale, ax=ax, axis_min_max=self._axis_min_max, - axes_colors=axes_colors, **kwargs) + lines.boundary(ax, scale, self._axis_min_max, + axes_colors=axes_colors, **kwargs) + def gridlines(self, multiple=None, horizontal_kwargs=None, left_kwargs=None, right_kwargs=None, **kwargs): + """ + Draw gridlines on the simplex. + + Parameters + ---------- + multiple: float, optional + Specifies which inner gridelines to draw. For example, + if scale=30 and multiple=6, only 5 inner gridlines will be drawn. + The default is None. + horizontal_kwargs: dict, optional + Any kwargs to pass through to matplotlib for horizontal gridlines + The default is None. + left_kwargs: dict, optional + Any kwargs to pass through to matplotlib for left parallel gridlines + right_kwargs: dict, optional + Any kwargs to pass through to matplotlib for right parallel gridlines + The default is None. + kwargs: + Any kwargs to pass through to matplotlib, if not using + horizontal_kwargs, left_kwargs, or right_kwargs + + Returns + ------- + None. + + """ ax = self.get_axes() scale = self.get_scale() - if not self._truncation: - lines.gridlines(scale=scale, axis_min_max=None, - multiple=multiple, ax=ax, - horizontal_kwargs=horizontal_kwargs, - left_kwargs=left_kwargs, - right_kwargs=right_kwargs, - **kwargs) - else: - lines.gridlines(scale=scale, axis_min_max=self._axis_min_max, - multiple=multiple, ax=ax, - horizontal_kwargs=horizontal_kwargs, - left_kwargs=left_kwargs, - right_kwargs=right_kwargs, - **kwargs) + lines.gridlines(ax, scale, self._axis_min_max, + multiple=multiple, + horizontal_kwargs=horizontal_kwargs, + left_kwargs=left_kwargs, + right_kwargs=right_kwargs, + **kwargs) # Various Lines @@ -529,7 +564,7 @@ def convert_coordinates(self, points, axisorder='blr'): Convert data coordinates to simplex coordinates for plotting in the case that axis limits have been applied. """ - return convert_coordinates_sequence(points,self._boundary_scale, + return convert_coordinates_sequence(points, self._boundary_scale, self._axis_limits, axisorder) # Various Plots From 6a491033277a8931e6d456eeff8605a33d0b5284 Mon Sep 17 00:00:00 2001 From: tgwoodcock Date: Mon, 15 Aug 2022 13:50:18 +0200 Subject: [PATCH 09/20] Moved function get_truncation The function get_truncation was previously in one of the minor modules but I have now moved it to ternary_axes_subplot as it makes much more sence. This function has been rewritten and is now called set_truncation, with get_truncation now simply returning self._truncation, in alignment with other functions. --- examples/truncated_simplex_example.py | 36 +++++----- ternary/helpers.py | 21 ------ ternary/ternary_axes_subplot.py | 97 ++++++++++++++++++++------- 3 files changed, 91 insertions(+), 63 deletions(-) diff --git a/examples/truncated_simplex_example.py b/examples/truncated_simplex_example.py index b388bdf..8f652ca 100644 --- a/examples/truncated_simplex_example.py +++ b/examples/truncated_simplex_example.py @@ -20,10 +20,10 @@ # Set custom ticks -tax.get_ticks_from_truncation(multiple=3) -tax.get_ticks_from_truncation() +tax.get_ticks_from_truncation(multiple=2) +#tax.get_ticks_from_truncation() offset=0.013 -tax.set_custom_ticks(fontsize=8, offset=offset, +tax.set_custom_ticks(fontsize=8, offset=offset, multiple=2, tick_formats="%.1f", linewidth=0.25) @@ -42,8 +42,12 @@ w_i,h_i = 3.15, 2.36 figure.set_size_inches((w_i,h_i)) figure.set_dpi(150) -tax.set_axis_limits({'b':[52,59],'l':[41,48],'r':[0,7]}) + +tax.set_axis_limits({'b':[52,59],'r':[0,7],'l':[41,48]}) + tax.set_truncation({'br' : 57.5, 'rl' : 4, 'lb' : 46.5}) + + tax.ax.axis("off") left = 0 right = 1 @@ -60,12 +64,12 @@ qs = [(55.9, -0.62, 44.5),(56.1, 3.30, 40.5),(51.5, 3.35, 45.0)] pos = ternary.helpers.convert_coordinates_sequence(qs,tax._boundary_scale, - tax._axis_limits, - axisorder='brl') + tax._axis_limits, + axisorder='brl') tax.left_axis_label(r"$\longleftarrow$ Hogs", position=pos[2],fontsize=fontsize) tax.right_axis_label(r"$\longleftarrow$ Dogs", - position=pos[1],fontsize=fontsize) + position=pos[1],fontsize=fontsize) tax.bottom_axis_label(r"Logs $\longrightarrow$", position=pos[0],fontsize=fontsize) @@ -74,8 +78,8 @@ #tax.get_ticks_from_truncation() #tax.get_ticks_from_axis_limits() ticks = {'b' : [54,55,56,57], - 'r' : [2,3,4], - 'l' : [46,45,44]} + 'r' : [2,3,4], + 'l' : [46,45,44]} ticklocs = {'b' : [4,6,8,10], 'r' : [4,6,8], 'l' : [8,6,4]} @@ -84,23 +88,23 @@ offset=0.013 tax.set_custom_ticks(fontsize=8, offset=offset, - tick_formats=None, linewidth=0.25) + tick_formats=None, linewidth=0.25) tax.add_extra_tick('r',(11,2,1),offset,scale,'1',fontsize=8,color='k', - linewidth=0.25) + linewidth=0.25) tax.add_extra_tick('r',(11,0,3),offset,scale,'0',fontsize=8,color='k', - linewidth=0.25) + linewidth=0.25) tax.add_extra_tick('l',(2,8,4),offset,scale,'43',fontsize=8,color='k', - linewidth=0.25) + linewidth=0.25) tax.add_extra_tick('l',(4,8,2),offset,scale,'42',fontsize=8,color='k', - linewidth=0.25) + linewidth=0.25) tax.add_extra_tick('l',(6,8,0),offset,scale,'41',fontsize=8,color='k', - linewidth=0.25) + linewidth=0.25) tax.add_extra_tick('b',(2,1,11),offset,scale,'53',fontsize=8,color='k', - linewidth=0.25) + linewidth=0.25) tax.ax.axis("scaled") diff --git a/ternary/helpers.py b/ternary/helpers.py index 2fe1734..c4e31be 100644 --- a/ternary/helpers.py +++ b/ternary/helpers.py @@ -218,24 +218,3 @@ def convert_coordinates_sequence(qs, scale, limits, axisorder): conversion = get_conversion(scale, limits) return [convert_coordinates(q, conversion, axisorder) for q in qs] - - -def get_axis_min_max(truncation, scale): - """ - Get the min and max values in scale coords given various - truncation points. - - !! Assumes the truncation lines do NOT cross each other!! - - truncation: dict (see main module) - """ - axis_min_max = {'b' : [0, scale], - 'r' : [0, scale], - 'l' : [0, scale] - } - - for k in truncation.keys(): - axis_min_max[k[0]][1] = truncation[k] - axis_min_max[k[1]][0] = scale-truncation[k] - - return axis_min_max diff --git a/ternary/ternary_axes_subplot.py b/ternary/ternary_axes_subplot.py index 9199779..e4331f0 100644 --- a/ternary/ternary_axes_subplot.py +++ b/ternary/ternary_axes_subplot.py @@ -11,7 +11,7 @@ from . import heatmapping from . import lines from . import plotting -from .helpers import project_point, convert_coordinates_sequence, get_axis_min_max +from .helpers import project_point, convert_coordinates_sequence BackgroundParameters = namedtuple('BackgroundParameters', ['color', 'alpha', 'zorder']) @@ -71,6 +71,7 @@ def __init__(self, ax=None, scale=None, permutation=None): self._labels = dict() self._corner_labels = dict() self._ticks = dict() + self._ticklocs = dict() # Container for data limits for the axes. Custom limits can # be set by the user self.set_axis_limits({"b" : [0, self._scale], @@ -135,33 +136,76 @@ def get_axis_limits(self): """Get the data limits for each axis""" return self._axis_limits + def set_axis_min_max(self, truncation): + """ + Set the min and max values of the axes in SIMPLEX coords + (rather than data coords) given various + truncation points. + + !! Assumes the truncation lines do NOT cross each other!! + + truncation: dict (see main module) + """ + for k in truncation.keys(): + self._axis_min_max[k[0]][1] = truncation[k] + self._axis_min_max[k[1]][0] = self._scale - truncation[k] + + def get_axis_min_max(self): """Get the simplex limits for each axis""" return self._axis_min_max - def set_truncation(self, truncation): + def set_truncation(self, truncation_data): """ Set one or more truncation lines which will be used to truncate - the simplex i.e. cut the corners off to zoom in on a particular - region. + the simplex i.e. cut one or more corners off to remove whitespace. - truncation = dict + The self.axis_limits (data limits) and self.axis_min_max (simplex + limits) are set by this function. + + Truncation lines may not cross each other! + + Parameters + ---------- + truncation_data : dict keys are 'br', 'rl' and/or 'lb' - values are a value in data coords giving the maximum of the - first axis mentioned in the key. + values are a value in DATA coords giving the maximum of the + first axis mentioned in the key. These are then transformed + into SIMPLEX coords and stored internally. + + Returns + ------- + None. + """ - for k in truncation.keys(): - step = (self._axis_limits[k[0]][1]- - self._axis_limits[k[0]][0])/float(self._boundary_scale) - self._truncation[k] = int((truncation[k]- - self._axis_limits[k[0]][0])/step) + steps = {'b' : (self._axis_limits['b'][1] - + self._axis_limits['b'][0]) / float(self._scale), + 'r' : (self._axis_limits['r'][1] - + self._axis_limits['r'][0]) / float(self._scale), + 'l' : (self._axis_limits['l'][1] - + self._axis_limits['l'][0]) / float(self._scale)} + + axlim = {i:j[:] for i,j in self._axis_limits.items()} + + for k in truncation_data: + + self._truncation[k] = int((truncation_data[k]- + axlim[k[0]][0])/steps[k[0]]) + + self._axis_limits[k[0]][1] = truncation_data[k] - self._axis_min_max = get_axis_min_max(self._truncation, - self._boundary_scale) + self._axis_limits[k[1]][0] = axlim[k[1]][0] + steps[k[1]] *\ + (self._scale - self._truncation[k]) + + self.set_axis_min_max(self._truncation) self._draw_background() + def get_truncation(self): + """ + This returns the truncation in SIMPLEX coords + """ return self._truncation # Title and Axis Labels @@ -363,7 +407,7 @@ def boundary(self, scale=None, axes_colors=None, **kwargs): def gridlines(self, multiple=None, horizontal_kwargs=None, left_kwargs=None, right_kwargs=None, **kwargs): """ - Draw gridlines on the simplex. + Draw gridlines on the simplex (excluding the boundary). Parameters ---------- @@ -448,6 +492,7 @@ def clear_matplotlib_ticks(self, axis="both"): ax = self.get_axes() plotting.clear_matplotlib_ticks(ax=ax, axis=axis) + def get_ticks_from_axis_limits(self, multiple=1): """ Taking self._axis_limits and self._boundary_scale get the scaled @@ -458,32 +503,30 @@ def get_ticks_from_axis_limits(self, multiple=1): self._ticks[k] = np.linspace( self._axis_limits[k][0], self._axis_limits[k][1], - int(self._boundary_scale / float(multiple) + 1) + int(self._boundary_scale / multiple + 1) ).tolist() - def get_ticks_from_truncation(self): + def get_ticks_from_truncation(self, multiple=1): """ - Taking self._axis_min_max and self._boundary_scale get the scaled + Taking self._axis_min_max and self._scale get the scaled ticks for all three axes and store them in self._ticks under the keys 'b' for bottom, 'l' for left and 'r' for right axes. """ - self._ticklocs = {} for k in ['b','l','r']: + gg = self._axis_min_max[k][1] - self._axis_min_max[k][0] + gg = int(gg / multiple + 1) - gg = self._axis_min_max[k][1] - self._axis_min_max[k][0] + 1 self._ticklocs[k] = np.linspace(self._axis_min_max[k][0], self._axis_min_max[k][1], gg).astype("int").tolist() - q = np.linspace(self._axis_limits[k][0], - self._axis_limits[k][1], - self._boundary_scale+1).astype("int").tolist() + self._ticks[k] = np.linspace(self._axis_limits[k][0], + self._axis_limits[k][1], + gg).tolist() - self._ticks[k] = q[self._axis_min_max[k][0]: - self._axis_min_max[k][1]+1] - self._ticklocs['l'] = [self._boundary_scale - i for i in self._ticklocs['l']] + self._ticklocs['l'] = [self._scale - i for i in self._ticklocs['l']] self._ticks['l'].reverse() @@ -506,6 +549,7 @@ def set_custom_ticks(self, locations=None, clockwise=False, multiple=1, axes_colors=axes_colors, tick_formats=tick_formats, **kwargs) + def ticks(self, ticks=None, locations=None, multiple=1, axis='blr', clockwise=False, axes_colors=None, tick_formats=None, **kwargs): ax = self.get_axes() @@ -515,6 +559,7 @@ def ticks(self, ticks=None, locations=None, multiple=1, axis='blr', axes_colors=axes_colors, tick_formats=tick_formats, **kwargs) + def add_extra_tick(self, axis, loc1, offset, scale, tick, fontsize, **kwargs): ax = self.get_axes() From 134d794334f5abb7eaa5cbee2bbcff7ebefd0484 Mon Sep 17 00:00:00 2001 From: tgwoodcock Date: Mon, 15 Aug 2022 14:39:33 +0200 Subject: [PATCH 10/20] Refactored set_truncation --- ternary/ternary_axes_subplot.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/ternary/ternary_axes_subplot.py b/ternary/ternary_axes_subplot.py index e4331f0..123a56a 100644 --- a/ternary/ternary_axes_subplot.py +++ b/ternary/ternary_axes_subplot.py @@ -179,14 +179,10 @@ def set_truncation(self, truncation_data): None. """ - steps = {'b' : (self._axis_limits['b'][1] - - self._axis_limits['b'][0]) / float(self._scale), - 'r' : (self._axis_limits['r'][1] - - self._axis_limits['r'][0]) / float(self._scale), - 'l' : (self._axis_limits['l'][1] - - self._axis_limits['l'][0]) / float(self._scale)} - - axlim = {i:j[:] for i,j in self._axis_limits.items()} + steps = {i : (j[1] - j[0]) / float(self._scale) for i, j in + self._axis_limits.items()} + + axlim = {i : j[:] for i, j in self._axis_limits.items()} for k in truncation_data: From a5b377a7bca377b4721a746c1a5b3b96d2853c81 Mon Sep 17 00:00:00 2001 From: tgwoodcock Date: Mon, 15 Aug 2022 16:56:32 +0200 Subject: [PATCH 11/20] Further refactoring to get_ticks_from_truncation --- examples/truncated_simplex_example.py | 20 ++++++++++---------- ternary/ternary_axes_subplot.py | 6 ++++-- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/examples/truncated_simplex_example.py b/examples/truncated_simplex_example.py index 8f652ca..ab8510a 100644 --- a/examples/truncated_simplex_example.py +++ b/examples/truncated_simplex_example.py @@ -75,20 +75,20 @@ # Set custom ticks -#tax.get_ticks_from_truncation() +tax.get_ticks_from_truncation(multiple=1) #tax.get_ticks_from_axis_limits() -ticks = {'b' : [54,55,56,57], - 'r' : [2,3,4], - 'l' : [46,45,44]} -ticklocs = {'b' : [4,6,8,10], - 'r' : [4,6,8], - 'l' : [8,6,4]} -tax._ticks = ticks -tax._ticklocs = ticklocs +# ticks = {'b' : [54,55,56,57], +# 'r' : [2,3,4], +# 'l' : [46,45,44]} +# ticklocs = {'b' : [4,6,8,10], +# 'r' : [4,6,8], +# 'l' : [8,6,4]} +# tax._ticks = ticks +# tax._ticklocs = ticklocs offset=0.013 tax.set_custom_ticks(fontsize=8, offset=offset, - tick_formats=None, linewidth=0.25) + tick_formats="%.1f", linewidth=0.25) tax.add_extra_tick('r',(11,2,1),offset,scale,'1',fontsize=8,color='k', diff --git a/ternary/ternary_axes_subplot.py b/ternary/ternary_axes_subplot.py index 123a56a..accec78 100644 --- a/ternary/ternary_axes_subplot.py +++ b/ternary/ternary_axes_subplot.py @@ -522,8 +522,10 @@ def get_ticks_from_truncation(self, multiple=1): gg).tolist() - self._ticklocs['l'] = [self._scale - i for i in self._ticklocs['l']] - self._ticks['l'].reverse() + self._ticklocs['l'] = [i - self._axis_min_max["l"][0] + + (self._scale - self._axis_min_max["l"][1]) + for i in self._ticklocs['l']] + def set_custom_ticks(self, locations=None, clockwise=False, multiple=1, From dc7e6f3f75be57f6fd738c580b41b79b478a9f8c Mon Sep 17 00:00:00 2001 From: tgwoodcock Date: Tue, 16 Aug 2022 10:58:05 +0200 Subject: [PATCH 12/20] Refactoring several functions Refactorted set_custom_ticks and added docstrings to several functions. --- examples/custom_axis_scaling.py | 4 +- examples/truncated_simplex_example.py | 50 +++++----- ternary/lines.py | 29 +++++- ternary/ternary_axes_subplot.py | 136 +++++++++++++++++--------- 4 files changed, 144 insertions(+), 75 deletions(-) diff --git a/examples/custom_axis_scaling.py b/examples/custom_axis_scaling.py index 9559c4f..a18d10d 100644 --- a/examples/custom_axis_scaling.py +++ b/examples/custom_axis_scaling.py @@ -114,8 +114,8 @@ tax2.set_axis_limits({'b': [60, 75], 'l': [15, 30], 'r': [10, 25]}) tax2.get_ticks_from_axis_limits(multiple=5) tick_formats = "%.1f" -tax2.set_custom_ticks(fontsize=10, offset=0.025, multiple=5, - axes_colors=axes_colors, tick_formats=tick_formats) +tax2.set_custom_ticks(fontsize=10, offset=0.025, axes_colors=axes_colors, + tick_formats=tick_formats) # plot some data points = [(62, 12, 26), (63.5, 13.5, 23), (65, 14, 21), (61, 15, 24), diff --git a/examples/truncated_simplex_example.py b/examples/truncated_simplex_example.py index ab8510a..fc2e324 100644 --- a/examples/truncated_simplex_example.py +++ b/examples/truncated_simplex_example.py @@ -20,10 +20,9 @@ # Set custom ticks -tax.get_ticks_from_truncation(multiple=2) -#tax.get_ticks_from_truncation() +tax.get_ticks_from_axis_limits(multiple=2) offset=0.013 -tax.set_custom_ticks(fontsize=8, offset=offset, multiple=2, +tax.set_custom_ticks(fontsize=8, offset=offset, tick_formats="%.1f", linewidth=0.25) @@ -75,36 +74,35 @@ # Set custom ticks -tax.get_ticks_from_truncation(multiple=1) -#tax.get_ticks_from_axis_limits() -# ticks = {'b' : [54,55,56,57], -# 'r' : [2,3,4], -# 'l' : [46,45,44]} -# ticklocs = {'b' : [4,6,8,10], -# 'r' : [4,6,8], -# 'l' : [8,6,4]} -# tax._ticks = ticks -# tax._ticklocs = ticklocs +# tax.get_ticks_from_axis_limits(multiple=1) +# define ticks in data coords: +tax._ticks = {'b' : [54,55,56,57], + 'r' : [2,3,4], + 'l' : [44,45,46]} +# define tick locations in simplex coords: +tax._ticklocs = {'b' : [4,6,8,10], + 'r' : [4,6,8], + 'l' : [4,6,8]} offset=0.013 tax.set_custom_ticks(fontsize=8, offset=offset, - tick_formats="%.1f", linewidth=0.25) + tick_formats="%i", linewidth=0.25) -tax.add_extra_tick('r',(11,2,1),offset,scale,'1',fontsize=8,color='k', - linewidth=0.25) -tax.add_extra_tick('r',(11,0,3),offset,scale,'0',fontsize=8,color='k', - linewidth=0.25) +tax.add_extra_tick('r', (11,2,1), offset, scale, '1', fontsize=8, color='k', + linewidth=0.25) +tax.add_extra_tick('r', (11,0,3), offset, scale, '0', fontsize=8, color='k', + linewidth=0.25) -tax.add_extra_tick('l',(2,8,4),offset,scale,'43',fontsize=8,color='k', - linewidth=0.25) -tax.add_extra_tick('l',(4,8,2),offset,scale,'42',fontsize=8,color='k', - linewidth=0.25) -tax.add_extra_tick('l',(6,8,0),offset,scale,'41',fontsize=8,color='k', - linewidth=0.25) +tax.add_extra_tick('l', (2,8,4), offset, scale, '43', fontsize=8, color='k', + linewidth=0.25) +tax.add_extra_tick('l', (4,8,2), offset, scale, '42', fontsize=8, color='k', + linewidth=0.25) +tax.add_extra_tick('l', (6,8,0), offset, scale, '41', fontsize=8, color='k', + linewidth=0.25) -tax.add_extra_tick('b',(2,1,11),offset,scale,'53',fontsize=8,color='k', - linewidth=0.25) +tax.add_extra_tick('b', (2,1,11), offset, scale, '53', fontsize=8, color='k', + linewidth=0.25) tax.ax.axis("scaled") diff --git a/ternary/lines.py b/ternary/lines.py index 80c02c5..e7215ac 100644 --- a/ternary/lines.py +++ b/ternary/lines.py @@ -415,10 +415,35 @@ def ticks(ax, scale, ticks=None, locations=None, multiple=1, axis='b', color=axes_colors['b'], fontsize=fontsize) -def add_extra_tick(ax, axis, loc1, offset, scale, tick ,fontsize, **kwargs): +def add_extra_tick(ax, axis, loc1, offset, scale, tick, fontsize, **kwargs): """ Add an extra tick on an axis but not necessarily on - the simplex. This may be useful if a truncation is applied. + the boundary of the simplex. This may be useful if a truncation is applied. + + Parameters + ---------- + ax : matplotlib.Axes + The matplotlib Axes object containing the plot. + axis : STR + A string giving the axis on which the extra tick should be drawn. + One of 'l', 'b' or 'r'. + loc1 : 3-tuple + A 3-tuple giving the location of the extra tick in simplex coords. + offset : FLOAT + Defines an offset of the tick label and the length of the tick + scale : INT + The self._scale attibute of the simplex + tick : STR + A string giving the text for the tick label + fontsize : INT + Describing the font size of the tick label + **kwargs : DICT + Kwargs to pass through to matplotlib Line2D. + + Returns + ------- + None. + """ toff = offset * scale diff --git a/ternary/ternary_axes_subplot.py b/ternary/ternary_axes_subplot.py index accec78..225c4c8 100644 --- a/ternary/ternary_axes_subplot.py +++ b/ternary/ternary_axes_subplot.py @@ -407,7 +407,7 @@ def gridlines(self, multiple=None, horizontal_kwargs=None, Parameters ---------- - multiple: float, optional + multiple: int, optional Specifies which inner gridelines to draw. For example, if scale=30 and multiple=6, only 5 inner gridlines will be drawn. The default is None. @@ -491,35 +491,28 @@ def clear_matplotlib_ticks(self, axis="both"): def get_ticks_from_axis_limits(self, multiple=1): """ - Taking self._axis_limits and self._boundary_scale get the scaled + Taking self._axis_limits, self.axis_min_max and self._scale get the ticks for all three axes and store them in self._ticks under the - keys 'b' for bottom, 'l' for left and 'r' for right axes. - """ - for k in ['b', 'l', 'r']: - self._ticks[k] = np.linspace( - self._axis_limits[k][0], - self._axis_limits[k][1], - int(self._boundary_scale / multiple + 1) - ).tolist() - + keys 'b' for bottom, 'l' for left and 'r' for right axes. Get the + locations of the tickes and store them under self._ticklocs with the + same keys. - def get_ticks_from_truncation(self, multiple=1): - """ - Taking self._axis_min_max and self._scale get the scaled - ticks for all three axes and store them in self._ticks under the keys - 'b' for bottom, 'l' for left and 'r' for right axes. + NB. the tick locations for the left axis have to be shifted if there + is a truncation of that axis, otherwise they are projected in the + wrong place by lines.line(), which calls helpers.project_point(). """ for k in ['b','l','r']: gg = self._axis_min_max[k][1] - self._axis_min_max[k][0] - gg = int(gg / multiple + 1) + ff = self._axis_limits[k][1] - self._axis_limits[k][0] + step = ff/gg - self._ticklocs[k] = np.linspace(self._axis_min_max[k][0], - self._axis_min_max[k][1], - gg).astype("int").tolist() + self._ticklocs[k] = np.arange(self._axis_min_max[k][0], + self._axis_min_max[k][1] + step, + multiple).astype("int").tolist() - self._ticks[k] = np.linspace(self._axis_limits[k][0], - self._axis_limits[k][1], - gg).tolist() + self._ticks[k] = np.arange(self._axis_limits[k][0], + self._axis_limits[k][1] + step, + step*multiple).tolist() self._ticklocs['l'] = [i - self._axis_min_max["l"][0] + @@ -528,24 +521,43 @@ def get_ticks_from_truncation(self, multiple=1): - def set_custom_ticks(self, locations=None, clockwise=False, multiple=1, - axes_colors=None, tick_formats=None, **kwargs): + def set_custom_ticks(self, clockwise=False, axes_colors=None, + tick_formats=None, **kwargs): """ - Having called get_ticks_from_axis_limits, set the custom ticks on the - plot. + Having called get_ticks_from_axis_limits(), draw the custom ticks on + the plot. We call self.ticks() for each axis in turn with the ticks + and ticklocs already defined using get_ticks_from_axis_limits(). + + Parameters + ---------- + clockwise : BOOL, optional + Whether the axes of the simplex run clockwise or not. + The default is False. + axes_colors: Dict, optional + Option to color ticks differently for each axis, 'l', 'r', 'b' + e.g. {'l': 'g', 'r':'b', 'b': 'y'} + The default is None. + tick_formats: None, Dict, Str, optional + If None, all axes will be labelled with ints. + If Dict, the keys are 'b', 'l' and 'r' and the values are + format strings e.g. "%.3f" for a float with 3 decimal places + or "%.3e" for scientific format with 3 decimal places or + "%d" for ints. + If tick_formats is a string, it is assumed that this is a + format string to be applied to all axes. + The default is None + kwargs: + Any kwargs to pass through to matplotlib. + + Returns + ------- + None. + """ - if not self._truncation: - for k in ['b','l','r']: - self.ticks(ticks=self._ticks[k], locations=locations, - axis=k, clockwise=clockwise, multiple=multiple, - axes_colors=axes_colors, tick_formats=tick_formats, - **kwargs) - else: - for k in ['b','l','r']: - self.ticks(ticks=self._ticks[k], locations=self._ticklocs[k], - axis=k, clockwise=clockwise, multiple=multiple, - axes_colors=axes_colors, tick_formats=tick_formats, - **kwargs) + for k in ['b','l','r']: + self.ticks(ticks=self._ticks[k], locations=self._ticklocs[k], + axis=k, clockwise=clockwise, axes_colors=axes_colors, + tick_formats=tick_formats, **kwargs) def ticks(self, ticks=None, locations=None, multiple=1, axis='blr', @@ -558,11 +570,38 @@ def ticks(self, ticks=None, locations=None, multiple=1, axis='blr', **kwargs) - def add_extra_tick(self, axis, loc1, offset, scale, tick, fontsize, - **kwargs): - ax = self.get_axes() - lines.add_extra_tick(ax, axis, loc1, offset, scale, tick, fontsize, - **kwargs) + def add_extra_tick(self, axis, loc1, offset, tick, fontsize, **kwargs): + """ + Convenience function passthrough to lines.add_extra_tick. + + Add an extra tick on an axis but not necessarily on + the boundary of the simplex. This may be useful if a + truncation is applied. + + Parameters + ---------- + axis : STR + A string giving the axis on which the extra tick should be drawn. + One of 'l', 'b' or 'r'. + loc1 : 3-tuple + A 3-tuple giving the location of the extra tick in simplex coords. + offset : FLOAT + Defines an offset of the tick label and the length of the tick + tick : STR + A string giving the text for the tick label + fontsize : INT + Describing the font size of the tick label + **kwargs : DICT + Kwargs to pass through to matplotlib Line2D. + + Returns + ------- + None. + + """ + lines.add_extra_tick(self.get_axes(), axis, loc1, offset, + self.get_scale(), tick, fontsize, **kwargs) + # Redrawing and resizing @@ -572,11 +611,11 @@ def resize_drawing_canvas(self, scale=None): scale = self.get_scale() plotting.resize_drawing_canvas(ax, scale=scale) + def _redraw_labels(self): """ Redraw axis labels, typically after draw or resize events. """ - ax = self.get_axes() # Remove any previous labels for mpl_object in self._to_remove: @@ -602,6 +641,7 @@ def _redraw_labels(self): text.set_rotation_mode("anchor") self._to_remove.append(text) + def convert_coordinates(self, points, axisorder='blr'): """ Convert data coordinates to simplex coordinates for plotting @@ -619,18 +659,21 @@ def scatter(self, points, **kwargs): **kwargs) return plot_ + def plot(self, points, **kwargs): ax = self.get_axes() permutation = self._permutation plotting.plot(points, ax=ax, permutation=permutation, **kwargs) + def plot_colored_trajectory(self, points, cmap=None, **kwargs): ax = self.get_axes() permutation = self._permutation plotting.plot_colored_trajectory(points, cmap=cmap, ax=ax, permutation=permutation, **kwargs) + def heatmap(self, data, scale=None, cmap=None, scientific=False, style='triangular', colorbar=True, use_rgba=False, vmin=None, vmax=None, cbarlabel=None, cb_kwargs=None): @@ -646,6 +689,7 @@ def heatmap(self, data, scale=None, cmap=None, scientific=False, vmin=vmin, vmax=vmax, cbarlabel=cbarlabel, cb_kwargs=cb_kwargs) + def heatmapf(self, func, scale=None, cmap=None, boundary=True, style='triangular', colorbar=True, scientific=False, vmin=None, vmax=None, cbarlabel=None, cb_kwargs=None): @@ -661,10 +705,12 @@ def heatmapf(self, func, scale=None, cmap=None, boundary=True, vmin=vmin, vmax=vmax, cbarlabel=cbarlabel, cb_kwargs=cb_kwargs) + def set_background_color(self, color="whitesmoke", zorder=-1000, alpha=0.75): self._background_parameters = BackgroundParameters(color=color, alpha=alpha, zorder=zorder) self._draw_background() + def _draw_background(self): color, alpha, zorder = self._background_parameters scale = self.get_scale() From af4e1e6ca3539c3600d610e4cc2381d6263aab1f Mon Sep 17 00:00:00 2001 From: tgwoodcock Date: Tue, 16 Aug 2022 11:23:19 +0200 Subject: [PATCH 13/20] Updated docstrings --- ternary/lines.py | 4 +-- ternary/ternary_axes_subplot.py | 48 +++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/ternary/lines.py b/ternary/lines.py index e7215ac..e495538 100644 --- a/ternary/lines.py +++ b/ternary/lines.py @@ -234,7 +234,7 @@ def gridlines(ax, scale, axis_min_max, multiple=None, These may not be equal to (0, scale) if a truncation has been applied. multiple: float, None - Specifies which inner gridelines to draw. For example, if scale=30 and + Specifies which inner gridlines to draw. For example, if scale=30 and multiple=6, only 5 inner gridlines will be drawn. horizontal_kwargs: dict, None Any kwargs to pass through to matplotlib for horizontal gridlines @@ -298,7 +298,7 @@ def ticks(ax, scale, ticks=None, locations=None, multiple=1, axis='b', locations: list of points, None The locations of the ticks multiple: float, None - Specifies which ticks gridelines to draw. For example, if scale=30 and + Specifies which ticks to draw. For example, if scale=30 and multiple=6, only 5 ticks will be drawn. axis: str, 'b' The axis or axes to draw the ticks for. `axis` must be a substring of diff --git a/ternary/ternary_axes_subplot.py b/ternary/ternary_axes_subplot.py index 225c4c8..2c88883 100644 --- a/ternary/ternary_axes_subplot.py +++ b/ternary/ternary_axes_subplot.py @@ -562,6 +562,54 @@ def set_custom_ticks(self, clockwise=False, axes_colors=None, def ticks(self, ticks=None, locations=None, multiple=1, axis='blr', clockwise=False, axes_colors=None, tick_formats=None, **kwargs): + """ + Convenience function passthrough to lines.ticks. + + Calling this function with all the default arguments results in + each axis of the simplex being labelled on every gridline with + an int. This is the simplest case where the axis_limits (data) + have not been set and are therefore the same as the axis_min_max + (simplex) limits. + + If set_axis_limits() or set_truncation() has been called, this + function should not be used. Instead call get_ticks_from_axis_limits() + and then set_custom_ticks(). + + + Parameters + ---------- + ticks: list of strings, None + The tick labels + locations: list of points, None + The locations of the ticks + multiple: float, None + Specifies which ticks to draw. For example, + if scale=30 and multiple=6, only 5 ticks will be drawn. + axis: str, 'b' + The axis or axes to draw the ticks for. `axis` must be a + substring of 'lrb' (as sets) + offset: float, 0.01 + controls the length of the ticks + clockwise: bool, False + Draw ticks marks clockwise or counterclockwise + axes_colors: Dict, None + Option to color ticks differently for each axis, 'l', 'r', 'b' + e.g. {'l': 'g', 'r':'b', 'b': 'y'} + tick_formats: None, Dict, Str + If None, all axes will be labelled with ints. If Dict, the keys + are 'b', 'l' and 'r' and the values are format strings + e.g. "%.3f" for a float with 3 decimal places or "%.3e" for + scientific format with 3 decimal places or "%d" for ints. + If tick_formats is a string, it + is assumed that this is a format string to be applied to all axes. + kwargs: + Any kwargs to pass through to matplotlib. + + Returns + ------- + None. + + """ ax = self.get_axes() scale = self.get_scale() lines.ticks(ax, scale, ticks=ticks, locations=locations, From 9e086defa02ede8099517e4ebdde01e21309af08 Mon Sep 17 00:00:00 2001 From: tgwoodcock Date: Tue, 16 Aug 2022 16:16:23 +0200 Subject: [PATCH 14/20] Updating truncation example --- examples/truncated_simplex_example.py | 139 +++++++++++++++----------- ternary/helpers.py | 2 +- ternary/ternary_axes_subplot.py | 1 + 3 files changed, 85 insertions(+), 57 deletions(-) diff --git a/examples/truncated_simplex_example.py b/examples/truncated_simplex_example.py index fc2e324..ddff6c9 100644 --- a/examples/truncated_simplex_example.py +++ b/examples/truncated_simplex_example.py @@ -1,9 +1,20 @@ +""" +This script gives two examples of truncation i.e. cutting one +or more corners off the simplex to save whitespace in a figure. + +The first example is simple: only the top corner is truncated and the data +coordainates are the same as the simplex coordinates. + +The second example is more complex: all three corners have been truncated +and we set axis data limits which are not the same as the simplex coords. +""" + import ternary -## Simple example with one corner truncated -## Boundary and Gridlines +## Simple example with the top corner truncated scale = 10 figure, tax = ternary.figure(scale=scale) +figure.subplots_adjust(left=-0.1, right=1.1, bottom=0.1, top=1.1) tax.set_truncation({'rl' : 8}) tax.ax.axis("off") @@ -12,102 +23,118 @@ tax.boundary(linewidth=0.25) tax.gridlines(color="black", multiple=1, linewidth=0.25, ls='-') -# Set Axis labels and Title +# Set Axis labels tax.left_axis_label(r"$\longleftarrow$ Hogs", fontsize=10) tax.right_axis_label(r"$\longleftarrow$ Dogs", fontsize=10) tax.bottom_axis_label(r"Logs $\longrightarrow$", fontsize=10, offset=0.08) -# Set custom ticks +# As we have set a truncation, we need to get and set custom ticks tax.get_ticks_from_axis_limits(multiple=2) offset=0.013 tax.set_custom_ticks(fontsize=8, offset=offset, - tick_formats="%.1f", linewidth=0.25) - + tick_formats="%.1f", linewidth=0.25) +# here we have used a tick formatting string to label all the axes with +# floats to one decimal place. tax.ax.axis("scaled") tax._redraw_labels() +figure.canvas.draw() -## Boundary and Gridlines + +## More complex example with truncations on all 3 corners of the simplex +## and axes with data coordinates which are different to simplex coordinates. scale = 14 figure, tax = ternary.figure(scale=scale) figure.set_facecolor('w') w_i,h_i = 3.15, 2.36 figure.set_size_inches((w_i,h_i)) figure.set_dpi(150) +tax.ax.axis("off") +figure.subplots_adjust(left=0, right=1, bottom=0, top=1) -tax.set_axis_limits({'b':[52,59],'r':[0,7],'l':[41,48]}) +# here we set the data limits of the axes (of the complete simplex) +tax.set_axis_limits({'b' : [52, 59], 'r' : [0, 7], 'l' : [41, 48]}) +# now we set the truncation of all 3 corners in data coords. The truncation +# point refers to the first axis in the key e.g. "b" will be cut off at 57.5 +# parallel to the left axis until we reach the "r" axis for "br" : 57.5 tax.set_truncation({'br' : 57.5, 'rl' : 4, 'lb' : 46.5}) - -tax.ax.axis("off") -left = 0 -right = 1 -bottom = 0 -top = 1 -figure.subplots_adjust(left=left,right=right,bottom=bottom,top=top) - # Draw Boundary and Gridlines tax.boundary(linewidth=0.25) tax.gridlines(color="black", multiple=1, linewidth=0.25, ls='-') -# Set Axis labels and Title +# As the truncated figure is no longer a triangle, we need to set custom +# positions for the axis labels. One way is to enter positions as 2-tuples +# in xy data coords (e.g. obtained interactively by the hovering the mouse +# over the matplotlib figure axes) and convert them to simplex coords +# (3-tuples) for plotting: +qs = {"l" : (1.94, 5.80), + "b" : (6.67, -1.5), + "r" : (12.34, 5.58) + } + +pos = {i : ternary.helpers.planar_to_coordinates(j, tax._scale).tolist() + for i, j in qs.items() + } + fontsize = 10 -qs = [(55.9, -0.62, 44.5),(56.1, 3.30, 40.5),(51.5, 3.35, 45.0)] - -pos = ternary.helpers.convert_coordinates_sequence(qs,tax._boundary_scale, - tax._axis_limits, - axisorder='brl') -tax.left_axis_label(r"$\longleftarrow$ Hogs", - position=pos[2],fontsize=fontsize) -tax.right_axis_label(r"$\longleftarrow$ Dogs", - position=pos[1],fontsize=fontsize) -tax.bottom_axis_label(r"Logs $\longrightarrow$", - position=pos[0],fontsize=fontsize) - - -# Set custom ticks -# tax.get_ticks_from_axis_limits(multiple=1) -# define ticks in data coords: +tax.left_axis_label(r"$\longleftarrow$ Hogs", position=pos["l"], + fontsize=fontsize) +tax.right_axis_label(r"$\longleftarrow$ Dogs", position=pos["r"], + fontsize=fontsize) +tax.bottom_axis_label(r"Logs $\longrightarrow$", position=pos["b"], + fontsize=fontsize) + + +# We also need to set custom ticks for this example. +# We could use tax.get_ticks_from_axis_limits(multiple=1) and this gives +# us all the ticks on the remaining visible parts of the simplex boundary. +# Instead we show here how to plot specific ticks. +# define ticks in data coords along each axis: tax._ticks = {'b' : [54,55,56,57], 'r' : [2,3,4], 'l' : [44,45,46]} -# define tick locations in simplex coords: +# define tick locations along each axis in simplex coords: tax._ticklocs = {'b' : [4,6,8,10], - 'r' : [4,6,8], - 'l' : [4,6,8]} + 'r' : [4,6,8], + 'l' : [4,6,8]} offset=0.013 -tax.set_custom_ticks(fontsize=8, offset=offset, - tick_formats="%i", linewidth=0.25) - - -tax.add_extra_tick('r', (11,2,1), offset, scale, '1', fontsize=8, color='k', - linewidth=0.25) -tax.add_extra_tick('r', (11,0,3), offset, scale, '0', fontsize=8, color='k', - linewidth=0.25) - -tax.add_extra_tick('l', (2,8,4), offset, scale, '43', fontsize=8, color='k', - linewidth=0.25) -tax.add_extra_tick('l', (4,8,2), offset, scale, '42', fontsize=8, color='k', - linewidth=0.25) -tax.add_extra_tick('l', (6,8,0), offset, scale, '41', fontsize=8, color='k', - linewidth=0.25) - -tax.add_extra_tick('b', (2,1,11), offset, scale, '53', fontsize=8, color='k', - linewidth=0.25) +tax.set_custom_ticks(fontsize=8, offset=offset, tick_formats="%i", + linewidth=0.25) + +# As we have applied a truncation, it can be helpful to plot extra +# ticks which are not located on the simplex boundary. We specify +# which axis the tick belongs to so that the tick can be drawn with +# the correct orientation: horizontal, right parallel or left parallel. +# Then give the position of the tick in simplex coordinates and specify +# the tick label as a string in data coordinates: +tax.add_extra_tick('r', (11,2,1), offset, '1', fontsize=8, color='k', + linewidth=0.25) +tax.add_extra_tick('r', (11,0,3), offset, '0', fontsize=8, color='k', + linewidth=0.25) + +tax.add_extra_tick('l', (2,8,4), offset, '43', fontsize=8, color='k', + linewidth=0.25) +tax.add_extra_tick('l', (4,8,2), offset, '42', fontsize=8, color='k', + linewidth=0.25) +tax.add_extra_tick('l', (6,8,0), offset, '41', fontsize=8, color='k', + linewidth=0.25) + +tax.add_extra_tick('b', (2,1,11), offset, '53', fontsize=8, color='k', + linewidth=0.25) tax.ax.axis("scaled") -tax.ax.axis([1.1, 12.8, -0.7, 7.4]) -tax.ax.set_position([0,0.02,1,1]) +tax.ax.axis([0, 14, -2, 9]) tax._redraw_labels() ternary.plt.show() diff --git a/ternary/helpers.py b/ternary/helpers.py index c4e31be..f6afd54 100644 --- a/ternary/helpers.py +++ b/ternary/helpers.py @@ -147,7 +147,7 @@ def project_sequence(s, permutation=None): def convert_coordinates(q, conversion, axisorder): """ - Convert a 3-tuple in data coordinates into to simplex data + Convert a 3-tuple in data coordinates to simplex coordinates for plotting. Parameters diff --git a/ternary/ternary_axes_subplot.py b/ternary/ternary_axes_subplot.py index 2c88883..f1d604f 100644 --- a/ternary/ternary_axes_subplot.py +++ b/ternary/ternary_axes_subplot.py @@ -500,6 +500,7 @@ def get_ticks_from_axis_limits(self, multiple=1): NB. the tick locations for the left axis have to be shifted if there is a truncation of that axis, otherwise they are projected in the wrong place by lines.line(), which calls helpers.project_point(). + This is handled in the last 3 lines of this function. """ for k in ['b','l','r']: gg = self._axis_min_max[k][1] - self._axis_min_max[k][0] From 5dd1816cd70579390ad7251e57676eba74088636 Mon Sep 17 00:00:00 2001 From: tgwoodcock Date: Tue, 16 Aug 2022 16:44:53 +0200 Subject: [PATCH 15/20] Minor docstring update --- ternary/ternary_axes_subplot.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ternary/ternary_axes_subplot.py b/ternary/ternary_axes_subplot.py index f1d604f..645646f 100644 --- a/ternary/ternary_axes_subplot.py +++ b/ternary/ternary_axes_subplot.py @@ -123,8 +123,6 @@ def set_axis_limits(self, axis_limits=None): """ Set min and max data limits for each of the three axes. - max-min for each axis must be the same as the self._scale - axis_limits = dict keys are 'b','l' and 'r' for the three axes vals are lists of the min and max values for the axis in From 66ccb609ac327e131753b447a85b66a99a0402fb Mon Sep 17 00:00:00 2001 From: tgwoodcock Date: Thu, 18 Aug 2022 11:40:53 +0200 Subject: [PATCH 16/20] Minor docstring update --- ternary/ternary_axes_subplot.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ternary/ternary_axes_subplot.py b/ternary/ternary_axes_subplot.py index 645646f..488ea30 100644 --- a/ternary/ternary_axes_subplot.py +++ b/ternary/ternary_axes_subplot.py @@ -142,7 +142,10 @@ def set_axis_min_max(self, truncation): !! Assumes the truncation lines do NOT cross each other!! - truncation: dict (see main module) + truncation: dict + keys are 'br', 'rl' and/or 'lb' + values are a value in SIMPLEX coords giving the maximum of the + first axis mentioned in the key """ for k in truncation.keys(): self._axis_min_max[k[0]][1] = truncation[k] From 33cb96848d75214af70d1067f9d16e9e696d6a39 Mon Sep 17 00:00:00 2001 From: tgwoodcock Date: Thu, 18 Aug 2022 14:52:45 +0200 Subject: [PATCH 17/20] Updated examples and added readme images Updated the custom_axis_scaling and truncated_simplex_example scripts and added 3 images from these scripts to the readme_images folder. --- examples/custom_axis_scaling.py | 59 ++++++++++++++--------- examples/truncated_simplex_example.py | 1 - readme_images/truncation_all_corners.png | Bin 0 -> 27172 bytes readme_images/truncation_top_corner.png | Bin 0 -> 54351 bytes readme_images/zoom_example.png | Bin 0 -> 106879 bytes 5 files changed, 35 insertions(+), 25 deletions(-) create mode 100644 readme_images/truncation_all_corners.png create mode 100644 readme_images/truncation_top_corner.png create mode 100644 readme_images/zoom_example.png diff --git a/examples/custom_axis_scaling.py b/examples/custom_axis_scaling.py index a18d10d..6199b7c 100644 --- a/examples/custom_axis_scaling.py +++ b/examples/custom_axis_scaling.py @@ -1,12 +1,24 @@ -import ternary +""" +This script gives two examples of setting custom axis data limits instead of +having the axis limits being from 0 to scale as is the default case. + +In the first example we simply set some axis data limits, get and set the +ticks for the axes and then scatter some data. This example is then repeated +but with the additional feature of showing custom axis tick formatting. + +The second example shows how to use custom axis scaling to achieve a zoom +effect. We draw the full plot on the left and then zoom into a specific +region and plot that on the right. The basic principle is the same as the +first example. +""" -# Simple example: -## Boundary and Gridlines -scale = 9 -figure, tax = ternary.figure(scale=scale) +import ternary +## Simple example: +figure, tax = ternary.figure(scale=9) +figure.set_size_inches((4.8,4.8)) +figure.subplots_adjust(left=0.05, right=0.95, bottom=0.05, top=0.95) tax.ax.axis("off") -figure.set_facecolor('w') # Draw Boundary and Gridlines tax.boundary(linewidth=1.0) @@ -19,10 +31,10 @@ tax.bottom_axis_label("Hogs", fontsize=fontsize, offset=0.06) -# Set custom axis limits by passing a dict into set_limits. -# The keys are b, l and r for the three axes and the vals are a list +# Set custom axis DATA limits by passing a dict into set_axis_limits. +# The keys are b, l and r for the three axes and the values are a list # of the min and max in data coords for that axis. max-min for each -# axis must be the same as the scale i.e. 9 in this case. +# axis is the same as the scale i.e. 9 in this case. tax.set_axis_limits({'b': [67, 76], 'l': [24, 33], 'r': [0, 9]}) # get and set the custom ticks: tax.get_ticks_from_axis_limits() @@ -35,15 +47,14 @@ tax.ax.set_aspect('equal', adjustable='box') tax._redraw_labels() +figure.canvas.draw() ## Simple example with axis tick formatting: -## Boundary and Gridlines -scale = 9 -figure, tax = ternary.figure(scale=scale) - +figure, tax = ternary.figure(scale=9) +figure.set_size_inches((4.8,4.8)) +figure.subplots_adjust(left=0.05, right=0.95, bottom=0.05, top=0.95) tax.ax.axis("off") -figure.set_facecolor('w') # Draw Boundary and Gridlines tax.boundary(linewidth=1.0) @@ -55,33 +66,34 @@ tax.right_axis_label("Dogs", fontsize=fontsize, offset=0.12) tax.bottom_axis_label("Hogs", fontsize=fontsize, offset=0.06) - -# Set custom axis limits by passing a dict into set_limits. -# The keys are b, l and r for the three axes and the vals are a list +# Set custom axis DATA limits by passing a dict into set_axis_limits. +# The keys are b, l and r for the three axes and the values are a list # of the min and max in data coords for that axis. max-min for each -# axis must be the same as the scale i.e. 9 in this case. +# axis is the same as the scale i.e. 9 in this case. tax.set_axis_limits({'b': [67, 76], 'l': [24, 33], 'r': [0, 9]}) + # get and set the custom ticks: # custom tick formats: # tick_formats can either be a dict, like below or a single format string # e.g. "%.3e" (valid for all 3 axes) or None, in which case, ints are # plotted for all 3 axes. tick_formats = {'b': "%.2f", 'r': "%d", 'l': "%.1f"} - tax.get_ticks_from_axis_limits() tax.set_custom_ticks(fontsize=10, offset=0.02, tick_formats=tick_formats) # data can be plotted by entering data coords (rather than simplex coords): points = [(70, 3, 27), (73, 2, 25), (68, 6, 26)] -points_c = tax.convert_coordinates(points,axisorder='brl') +points_c = tax.convert_coordinates(points, axisorder='brl') tax.scatter(points_c, marker='o', s=25, c='r') tax.ax.set_aspect('equal', adjustable='box') tax._redraw_labels() +figure.canvas.draw() + ## Zoom example: -## Draw a plot with the full range on the left and a second plot which -## shows a zoomed region of the left plot. +# Draw a plot with the full range on the left and a second plot which +# shows a zoomed region of the left plot. fig = ternary.plt.figure(figsize=(11, 6)) ax1 = fig.add_subplot(2, 1, 1) ax2 = fig.add_subplot(2, 1, 2) @@ -132,11 +144,10 @@ tax1.line((60, 10, 30), (60, 25, 15), color='r', lw=2.0) tax1.line((75, 10, 15), (60, 25, 15), color='r', lw=2.0) -fig.set_facecolor("w") - tax1.ax.set_position([0.01, 0.05, 0.46, 0.8]) tax2.ax.set_position([0.50, 0.05, 0.46, 0.8]) tax1.resize_drawing_canvas() tax2.resize_drawing_canvas() +figure.canvas.draw() ternary.plt.show() diff --git a/examples/truncated_simplex_example.py b/examples/truncated_simplex_example.py index ddff6c9..e008deb 100644 --- a/examples/truncated_simplex_example.py +++ b/examples/truncated_simplex_example.py @@ -52,7 +52,6 @@ ## and axes with data coordinates which are different to simplex coordinates. scale = 14 figure, tax = ternary.figure(scale=scale) -figure.set_facecolor('w') w_i,h_i = 3.15, 2.36 figure.set_size_inches((w_i,h_i)) figure.set_dpi(150) diff --git a/readme_images/truncation_all_corners.png b/readme_images/truncation_all_corners.png new file mode 100644 index 0000000000000000000000000000000000000000..1d509b8cf13d6b57b3d2d3363b6b90c282edd9c7 GIT binary patch literal 27172 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 literal 0 HcmV?d00001 diff --git a/readme_images/truncation_top_corner.png b/readme_images/truncation_top_corner.png new file mode 100644 index 0000000000000000000000000000000000000000..841e215f4f5164e368d3346a7f9acba782858721 GIT binary patch literal 54351 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
^PU_etXjJS*5)B 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 *Ugu1C0RRg+*{<$h`bzvkHoEQ8fyT#K%|;X;4z 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%0Ae>FSxs>0345AP zd+nu2P*J0sagN?8&Sy7Zr4oxVrWU|gYI$|B_hfIOlgjSZ;w8wVf$w@jS3@ zK}3|{L{K`=100Xo)j456k{9y01B=W(!(yJZ9PRSApon2a4!u`ZMO_J4C6*})aI?7Q z{a+rQ^@#mFlK`aF6uJtoDsGp4B+46>7HSY_|0K_>Kl3JZAJgC(?x>u9?_UKIl=%*3sJ)pczp;v;Wt~~P*}TP8x6P^+SaNs4yT_oGpZtRJ z+GN(nF@`^3)8Mz;2Lwet9bkkFpsm%|@Q39w8s?7r_m~_qO)-l8-j~+j`v`6UVXOUe zk(n=awz3=d{zAKJ%?^zfrA*M=dP8tDgbc3_Fs@-sU!n6oXrTZ`G*=t*O{Ak94War% z;O=wuq)HK19!hpnRopd) zzB+}l`yjK$}*8hwr-JyWu08FBpo7`Ae(5^THbu8AzeD;u27}`w|b-2d``_i z;RzNSN>|Xa10f)-y6k|i3~aY$K@CwEDSjkcWuXC|Lu7riPn8VzY!ukLYvFf13gw!% zH;+_FgPuP0jLir|hhKa+*|MX{`t|Ahh8Cc1`kTEpQWY>cB-_qEw3|_1%TolGWw0tx zF~$`B5@ok=!|RR^Yf}r)D{;=vp6_4w(A_SNZR+K=aPbNk{0vjc zi!-ionq|ask`&^h9BFXqo*H&vEFEvw@D=Ea_tD5|#*!|!X$V=rseS#sC;XvNYyG)| zFE}#!a=LJpRCoY*fe106bnNyiu(|Mj+7dZg=#18O`S7bXtXHdNkUi(+JIA?e2ZxgY zFdP!|b#;8iMKe(c>`V1dO3L%A*v0fmuIq=B1@1>F90cQ6&;{*l2TRz#J!Onq>n4h6 z36g?_bRt>2%?YX&LL<_uAPg-fJY}g!cysk;zUPHNDRk~duWtV7O&i9*R&wE`fScyB zz+v#XTXZ$l)%4`Z0R8lkPxJ}ABe%b1Ho!mxpEr_c3PSQsa7Wy>G%gus1iK!2M#wOW zHJly8M<4?TMFH$i*1iImjj7=?5E!xl_#c|&2}D_P#B~+{($>3vuf&W$bhPg$6Zrd~ z<^Qw_UY`w~HOKdGE`QR{Hy%w2{-gq^TTvx6n>@6zL`zt66arehWIwl3JpVL)5`8@% zL&dn-xD?|Cg^2%xznugldMtMxbn{Qbgr1P?R@t%ZvD}Q3OO9;mx2O}aDt|7@G# zCuodgou1?a_1lIX?&Eg$tgvF&Er0yn%#>a6bKlh2lSh8qA&gOJ2~%wXkX=~lkFEQ2 z>LQj=MR-9jz)A>n(g$=ZrPniC5hpAG-p0jox_T_d7=-Wi_P8^%_q@m7u(lM|EOE}H zwm|=EA8U`t^D+{|~n0N$`aKesbH z=y|ZqUopV5bs3hdy3rz~CVn4Jh;ge|8}LIrhuWr?uy2G3?;kPG?otXO#72%MuSX#I z2#Im}Q-ZbvHQAO-m`m)sx0Pb@DG&M89_EubpRez?+P-}n58>}n$2=|E+TkhLaaoP} z@=B?0n?{H8dt@7Fu%PgJ#S?tC~EGdHI)S1|Tlqgq>nX;ahxnbIPHJuN-OBQ--kYZ)Av|6YS0C4 z=?y(Br_zl+prX++Yt>GCm83q9DMBMf{?@O7Nl4XOO8f?_ z7I&pluvppggAwQt=oJjm2TOCxR^}EyUh!{W0S#K13WTCHG2+kycE>Rj6K#ZP5Q_aS zU-p=B!>?ZV;d@CoHk2_XEipRIQcnGj+7)@}drKC9@KJ!2VQnjdA5uV_UXhGQ_BIVzCETE2tSBR!7Gk zJZ1B|G3g#h%$`w?i-Ym?Rk7}U9WL7M|H-I{u7eec(z)oTtN;A8k03HfP=0QL0&5HG za>!0Yr+|nr0my_pc7}U_TR(*rE*%t@#4=(Y2}9GPYKf2NLXM0YjG_=eE6$eHeje4< za9baT>i5+ZIuh5B794u%QudC0djlAn&6+L+O*JM4NAK9zChD#x)eAT2^1Wf1t1T8y z`tJKkMmjzDl%78Rk;q3((h~@=KvugYY)Zstn~b1`g?7LL^H_Q`vET?joQhI;h-m>Y zLT(Y>ia)^>@%SG!RJUYaZl?l?42;{pKJ3D(?A?CN?=D-32eNC)_!rVL40e>WoS-0B zx_emL9Cs;YIn&hth;I^gup2T^>-w6Q5L>!;gxG+G8V^*m@zppf=lxIX>t1k_qS+l}srezG|DPvJ_LEe2%( zIs^8}iH+|+gk6>(SLx(N6e>o4N4QT&WDCQg*J{lMq~n0r2_W81mmR}7!T<~&A%^@5 ztIwYif!=T=4pkw(3M|7_^szKlX}CZ-pd#18Awysr73o!EptKhIc2M+^fLs-?FNr{D z(5+hb4&5c=ZXT0fb&A3CIDhM%7jkun=v1Bl*x9SCP->dyQ?8~x$W5TMaVof%`2@y`5^(BW44wX zVw_V_;N`oU^Y5o1m;9D;n=_ne^CMOh#Vfz|*4fXGNq*c$)AmWwde48&hF2EY}aEr z^!@a(Z=O1*T5m;+SQIeq{9bnH_u>t1N_Z7UQdTXxOBOQ3aV3`nkcx5 z>KINqkT~ujC;xIo2(KQKl$6xb%NcJoSx@!AH$Fgz;srPo9#3ZwxS zb`9(tGAKSeJj_ zXgWogkK_JKT+kKayOrb^Vvw75v}tkw{KHZ$Qu*p)lk4pCayb9(!qdZ0RX!DKwygBb zj;ekDx%=D7C(ga&gIWDoYZyOvdPpynf6_wJ|JkvYGy2HxcY41P;dQcSRfmUNVNxgW zqsKB|-=C7Y9Qz`!xQp$_eg^U`0I?(%D2NRuKquq|h}oZb9-Vwqv+5D!hUbc~{Pt&&j6uC~ zq9j@zufQ@mN+g8w8UW-eun`DCFJYU2ih!v50c{~A;}9`41V1?dT9ijHWzx(&5ljN3 z|GJ8>qS@7@ro7KvlAXVh!cK6>bLsN0(HvWAI^e!OAk#zvlbA&gZXd#DUrT0)P4J)iV9 zj+4;g;mnDGl84>D6Az0srhnRSD^`0v6_P)UA2xjFoJQCse*xp1yC;-(f(AsI&7`j8bX3Q$cQijXhU#s> zZWIMkad|uNM%i#~1^nA*$FUf?dU8$Gxlx0`A7rji`3LG+7&=}NQga6i;7%~7iER(W znBs=Cw~Oe-y%@>^XW5+7PIy8wox1C=lC9*%vH2f&m*KHI?`H4RlD;I>G#n$%`diK{ zJ~(o8A_UVNcn5tvAnf*9Z;;}EA&)MDYB)DM2tJ>CGsH{2b7wbbbqEWPtl0k=>-&UgXOFt9KWg!~O2ACeEUuiV0uDzb|uV7Cgz6B)H>eqWYH) zI+}BiK_R+uZ4_foSZn5=T<1Xb*Y<((J6$O#3W9MJ50My5s)kXBttLXlp(?_2otRzc#{ri)`oG?L5^wwi5fGQ z>^KV*K$C|Lv|jB26qupZL->B-8*ZJP!zMczo)w>~S{iHHh+GRE+k`MeY5#QF<0Cas z+n_=@WZjX=t!=F33HCl6?fgsJxb|kWvs{K zf{WSM&z)hysayiXhDk4ru3-kpDCrRe@B(QRzrR~_xQ>ZSEDZ|)b?@!7BhNvBPh@XE zniW%&>Kq|@=+M`Ia%$w|9+y~KwoR~sV7r;eR?e9{R&w3w4D?x>`4jn%TlFiK(EK}Q z=Yq;HteJ)Q?)jb&B_e|XVKwp@Mgub7)XET_~GLIp?KQex4}c@1uYx{rOJ7C?~52{K&2IrT{?GhFqyJ|8}6c@ zyb(RQ@N2m?gfPGz09Kf22UMkiRZI4d)+Yq%_suqfPu)iNoYiNc(X7T9DnX)`hhc0~ z!l6Uo7iUXgE>_{2@jy6f$3Bwpvtx6LB+2`xLOH;E8?TuN`66_cq>oB+OY%?7vc}nw z%?f^tYc>63@Gz_2=0}%W0zR4l2nP1{5_&jAlfYa1I;q6MEL) zsQ(l-MggqaU_jR4x#li>m}d0lR;mZBnQ3fs@46I}3yfV2vPGueYByc}bQeAJd|JqH zTjOEizpFhowJeu8xLD6K6c{e;$#by$2~;ZkGlCH=;>g{W zAApN4H*fD<4vrzg0K?cW4jHrMEoB%Tkxxh+*-lP=P>f7^5@^toOqz{u)aKkM@F6-j z8~=hnN(u@Ehf!drKT>o>jjJNQ0NB)xlK*^9#D0fmpZtxHmNOU#>4gaMEUvAZaMk|3 z7|u;}+j-gowakrVm5>?*rbkIe1 z>@bA-o?HIONlomU>ko>|C^&`l*R3=|9WnM5d*+G)i<`sRvq~I*DNOm5ko|TgqKc|I ziX5x!6RuMIpKr$79fM*D9AW=_kMWehzVrcMMVAj}X($L+C*iLp0rHhpKN4V|Ix#cm6GcjAWjQtUJ2LPv(10*&lP+(aO+q4VEk;jrwU+OoOvV zW(6w`h@GxFW@OeVOZ9YFU=y6=!?^J`ZJ7Wm9)0GO0O2+duFNct=-af%-@}o^DxcOU zeGgQ1zvATrEbhhr3l{UaPmCG#pQJRRPX z(K;eFRV6TxO_&X~cQkhke=rdiQRr~lY14c6h@Ai0*7SFYaV&!+PD9^y-^K|nZ;p0Rih7t;&5eghJ42ksd_>9Xfky>$M1y|JJBUUt$GO| zFo7iuJ6vZ5)pb6ao`I#>H{^j~1a#Bo(`~%)wzI?9z^nQn*HH6XjX~)_0XWyTeD#kl zegbQs9Ns?N45jjD-trZn9>s_rkFz%H!+<;7R%RNxD=md0pTg=JvZSuKAPJjtiIr#V zl$B4ujI@@%JGC^spYu?|n;Fwc*b?yU6Ll;t2nu0`nl~b~{Mo)YY8U5E_+EmqpNQ*| zZ_ZQ8Iq6&cd*#pj{{w5Fciw#O^aAwaAFg?FZ2lI@l4o;W-s0Ei8Qx7VUX+S4dMpzj zosmK^&RG*WRX>v>ex;O*ey!b|x-m;ptgAA9JJn|(V!QQ-WxzGtH$^>I&%vj90zJpc zj(*Ac-ahH#9rfda9M=$2|zEZ@w@SC{P_|1UZaxej6HW7l4487k0$tAA1^Y~-a=V+dNK5xQ( z+sz}W0ewDc8H|6ULD9;Y`W*aC*<_fK*x&-fy3`0nEW&5I##WXC7Rq1W8x(D)q{N^2 z2?h;1x^n?%Pq|DV#axZhsew*+9}kLv`=T8otH!>_Sn(ZxV~1E-pPsOIB2dSq0(BqZ zhJ#C`n@=zoh*UR_Cq^gTC977D^!nMYb2VxSA3}7AqOb7>+rK}KtoxmUCJXOWZNkG0 zB>eEPRvxhxK}NCdIS%`6pL{eksuKQl-6d6T_kQ1>6qXd$A1DqyKyUcn9^WZu$uO%J z{rH5Ye{IF1O@!5Jf|BD({)hutcj^9+o39VOf#|R@ybct=UxCc$a$NTHKG>*W2|0~6 z#K+$*Ok*9oa4=jU#0?{E?kk^9zcZCKz%&HT@9IqpSC0-uf{QeZ1NiI1me&A1BIMbi zSP$#r13C38_}PCu@WNqR(d9Tmupib*Lz(l&W05Ct5HKW*n&3t52{h2&oVP76evqndc>ATB`$WRJ*_;lTW6?1Q`P z1{YG?@r3p_XE zxwM)N&pIH_U9cHu68UTuD{*$;F|T9lJ3@A*mb@Oz7toNo)ol_>G(5!P(kvwn`b@Wg z;>i+g+CO^W;Uf=)NIOR#TKBFD0&Nz=KCtui!-VCbzyCEPb4`!5HjK6Q8dfnC|OX>axyxj{)5HL7&F2iGm zS1Hoif3IB9w+mHR)u#$gd>S`^v3}G20@SCR`2jwpCB$bu*!lTw(cv*X6?8@v_wz6c zYOsaAtQQnVzlq#x<8Hj?%B{t?CXcZ!Phf0UUsZm6QwS@fu87JJXz(j1eNcw@pldvW zFLGai-Q)2rrh{2WGFegR+Csq>g#p|{!Y%q$dKu61Tx{J7e9)zIw&CW@2ZRcfQyFS8 zA81J47I2^l)vZ6Xb{2o1#OiXrmdN~zJDXpery6J=Kc@M>XS-HG$BU&o^=pT95)=oN zBm+k8pbPm6A^B{rq$y+pUzz@v9*!z{ki`~3m5wQ{yz#TI&TJnG)?`F&x32Vt>xNdH zzrLXk=Kf3@DY;7T+M4kjD(Ll=xN@*J&B*Mf-e7fPoaD@2owC5hkH9T9h1*B$MG_rK znhzW;m*Dkg{lTJ<`;7$s4rG<66Z;Wi6_T-l0YsgBG!e-4c?^At4VFi^ya$!{JUzn) zV~H0u!te_@}x?h&q&o3gm_nwe#av=__Hb_(l(B4>_x{`Kg)W zPMb)1Hd-#NLUiBs{V(~qiuy{B%Xp_pYmzA*;{Y);kDdN;DjrKds zVcI%dS8#&jYf*suEx4r4E;Oi8mehgjl4Ixl4bYWX1GcNnPA8x2?QpmT?`(`U4r+4K z#ZMX#^5C~-Z9>7Mz@&__eh8k%Osu}IT4qHt#gC2;lmeP@Zw30sRn_RGVL|GfqX~M$CA9Nf{%$$cmlQwv+ z88C=5xZd0P8?Cx<5+XIu`57*DdH0O`17+}^k|t3{2TNWSBe&kPyiSAb;U zee$Ql2gfnJif|s5pSS8Vt@VjNogg2AkHFu$xNU$KUSb1=Ll8j<{c--ljwRqisxQxy zBaXEkQ(8r2hJi5QaJOt2_CX}|?P+#u)Ohr~s*U{y|FJ2(vw7Qh`)EwQvL&>rH~P&D z8Tt@vLn)4ZGtKN`(w>((G+_0bTP|d}yRSUti@E*cCv@k)!;Iu>A~m*bH3YSyve>t# z;Foy`khY9Fn{{I=y^}!cG?tbtML&F##Ez-={(sw$;lvaAaB-N%w?34!QQs~N#<`ae zTn7eb=_Q_b-tLG5gP~ys3^AEj)C6Df!EGKVkydeFBh@3p)ImW$#JKJoEE|W)q!{p&72c_S0>kgJecm(av zjqXzLbsgqU^aV-ET(5rE@gaWc?!ud?e)55qeQ;b14BCrvEQE(5wy#wy?|%$2 zxi6SC&42$rgu#V;JVD)?F8p^eTM?{)Z%+l-k)$-I69^^>VGvKKh`jZudc*aJ5-RmX z#+eN}s0}V0+|)0i%5dtBcKoh-GFbPS?HqSQ3Po?lmtfY5Rw;H&Iv-E>R{6I35p_|f z=^c1e{GPn-tskN>I;Z4QKP3+rL$ji-(~cdt^pEO4;bC$4WF8TPwF@*xU}}QR+N5=Z zy2jQl9PowrMQa$c^^l7U%e)9&E#`Nq3IpDenauHfpyK1!4%lyE-LC_git4c36%1;isiXw$N5STDd9=-Hlq*oQB#Alvz!AqCpn(E_ zV1movZ+s@CxK+v8%Zd+nJ>p|+8Yz8T486iMQkX!}wlCc!$dh>@xAfXqp=;YgT4Zc> z8`4nAcT4mC#q z!xH2B|D2#2ioa*eA)6Np3zFlyB7}dSh|6Bx76#wRf&DK-Ut*ahS_^BS&R<3dy&ZJ+ zFWCyGGH}jykhP;ge%zCRZR!PDdA+foO{|XSu?TNCTPsiTg6Q&g6insOq<~}zHHbjg z0jxDyHoXJC-bG4BiywM+F~lEKzZdFI`K=0hU8Fx=bJ^5vO_I*rseXUK? z4OV||zA;uX_VlVm6;8ZwqC21_>#S+PS|IJ7J1(ovRG=~c{b4ZngDib>qanbv(sxHC zAah~E01wDH%Sc1AW-YLDO;~`>j&<^6iJx5u$lf-@5K$=-VXm-ef*A>`xuJd6 zbM{y-33vas(fV!FrWOMS58j+&(_k?N>TF-Uoy)+$H?V>aI~nvf*DCwcu1IiR7TVD2 zdVFm?djAO5E(@)QGQ$OaILOE^%}F0>Gz4d-{?6tBfa%035rhT7pM}pfej|t1jd43y>YwTEoRSTATcxWRZH=%b+y@F@oIy53?mJ~ zi4!p}X20I<$Bpd5>=9yn{Rfoy#hiaP@nMSbq9&$9@cNF~zUnV9T-Q^7PU2GXFd~0d zWVJt4w+KYeSs_cY$V%_#){K&m=KHSX(2h;L{$x?dseYG~Of0JEJe+ag^O90$(Qfm) zZ?JQxoIvGvc7pIktm4-$4p%=jc*akLZq~^;x9Bin9V}E^ZzDATC|i#M;Z)`fkM#YdDU4C%!ld#~WyEp?52FRB)S+t2hJ?+@$h;%sZklj8WS^RS_^~A((Ki=t24x|)$BS~ zDrU>pR>0DKsxpEP{!Lrcn|GGGg>LvdQMG*HYXevC7Z+&t&Jy877W)f+6B)mLowsnX zJSImQN~PzQhjZP)Hg6r$;u>JKvY6kQT4e)}{xV?z3m9j=&<(AIZtTy$amMUiT>d!9 zwgYvK1tGaa+{yu?UQ#H8L>+GU&;7AjJ~fAy{BWlC#vKg1G)g$);5&KPlG!g1mVk^h~kQ>2uYl9+)ah>eZxdYa@UEx{a8`ys^sU0o3<~xKI z6*W&BipZvrh;KFf2#ZEME@q7>OoXv5+FimsL*hhggtR=H)AwH>P@KF;ay?TzSz`bR zTXS7Rr5td;s3mQ4O{_38eBn~;inot#-SCL18(Wifq^z%r9pbOjqh1l? zh|Z1GVS(vJ{cS9(yC(i?aY9js&pcE4q|_vyvpt{UdL;uBI>^jl0+#>Xogl!TYsRS&@Zc8JX zxiEL=c-XT$`KRp7n>QP5f&i}?0Z)hQj^{t-#T%CjVFA9-zVN|$iWuo+G7()0-o;7m z#XkUuB$l;)G%EtjfG|yI4IB{~%iSy&E)~EqP#tMB*b=tX>ESmyf$L%h>KN=bTi)LC%15wI4@@7={YuT}_n4p|&vB20 zyTgrWQKOFReV4^TDEuNoS~Ac{BmHpgOzLI2cBPb(j>9uQF5;D<9k1Ywpk%xa6qeD{ zqWBS?(GMD-vQgesw@4rEySI7U^D9~BTl3~$PK9>FW9mIW z&$bD3;ZYc;pNW-lrw6M#I5-&FS`g-1&H|5%Fkh&}1GEWVz#n=X1U3pT6p?_F0Wm_& zbsqI^-}hO9vi7+2S7=J5>m6t3%iB(SynXXXdTui24{ooR7(zFlv9w)cxpa9`nF z6?W){N+Kx6Z)EcN`_^txn8}}Xblc`+??qmS2az7gq%iCDgI>Fn6m3N0ReFz~y2>TM z*!hrL3SYcQab85;Fo<_y!6Z?Gq9$4ZM9_he%mB-R`9q@;HPQE$7Xlh(4p3AU-g$4I z%t!Tbr?b6RIiW$s-Q7fw2wY@IWD9qn90swL&{q>oZ7i`9kZimBhx@ou{d2mEVxIyV zL>^IMH0}y2wPozQypM35KEO6FkrL8$;sFB@MoH0b3{S-TxO8_xQBfC6NF&Qw+!o@~ zxNM(r$rK4)7P4law3Iy)&i&qms^P7f{MDn>j$Nd=3$N4EMw`-LW|K*?Kv*?&?tTrnwxJukXc639p(8d+j2&OUgA zeyvmN)sSp&b#g5!wcFvWSo;q4G>j$05VOvz&R zJ{MF_MYPbI#;$}7D(*(5@}$S14bwGs*aoX#z53h*{{%_Pk`Zx|cFfDVPRF1Xp1@M~ocK0T@I)^k3W^tYa6#kH9~PWAd1()d6| z)Vw>j;?y*r8Aq}z_zW!=MmR`cA)i#rT(8B-K2;60!p8TMjPQLD-8K6 zhL^^O3+B&c5vqCV(M0^|!-^ZnrZgCD+osDSY%Mg)w3MoFpxaqj(|rDB{A3TG$Qkor zYJ;_sMx$|;4#rDK8%YScXRDCyJhqSC)c@qI`XEugzr0UH9aBi*Eu;+(Zo0fZ2etou zGbm@1aAvA+Kg9`Co7@&pD{6UwvG&TC`sk^73cEk9KO*m5|>L#8VWfU zK1muQji)g6pV;~CLe#%c$NGoMKgOq`uS+CRhkKd*+N!$}i$wGPe!QEvF$5I+DG9^z z!o)*H`;M~1A+sC@aR!Zii*P)G+csVeLVpqB^7OE(tY-7A2PzN!sGh7Xj2BUn@7gup z(-lI9$To)B{~qmd6PON?%e!9NP2MKYLJ)(KYEdn@kw&w9E3yqaCO$@^A!r~P1|W9OYv-9M{KR1fLA zBZUn{ub6grwEDVr_Qy(BK6%7Jvphq$h0Iwrc#P;%Hrf@O1!bC3_jAy8CQ1!d<^Q*% zzBR|)mbKY9eZu0Y;Ob+v=-0{(-A-Aw{=#K0%CgPL-%Z97+gB)D2R2ITCd~9fMW0 zZo^c5#HkFd@xZ^}?;hwc{C1-6yPbCK=u{@Y{8*vm^D#4Hw z9(2}!8;Sh+K-G?Cuj|n57CKh8T4f5L9w#`x4|eT83d=siJ$>=3S-w^W3DH3V2iSW| z%?CqF1p4s2hbekuzx%;VP6?9~y*yb06+u@6B4iwT7GPFR#@*Fhl!xiPxE*bE(kysHIGDFJPzV-Iy^=RWEGGG;o_1P!LVcR(L!ih zn=-9gP_#mbuXhldbfAPwH~I$9dlV6acnm2F#YQW$i^2@j+}sx@2TzNr0KN7cX&dN<^P=ZUqS)VZzpWCk(!N zC6)n~PC<2)fZWi*`(ETIDA)Er$JKcg%5SrE%a%mA`=7P6ogPpoOGFpy!fBj~6T5W+ ztxIgJ8-53|xP3xE+6?aB8_Y^L;@xuIN=r+t<92ns*M!?rF$i8zPt_Cu(!UaKH9PoI z4xcEGt-8K<1$mQ+D8b5{h7rf02A1q4*##@-PUP@Btv3FIW{5sW6B`c&lSl)*EqlK$ zp|IX@3FKtomOjMLsy=(2L@+tIa)|fxtWMvLJ!~lbhFdd(h@?hwWB#W&VOqlar1wQz zAIczvI36r@{r2nxCz|Amtht+z=aHawqC^(X(tDkEd>_J(D&IxY$D+JLPJ0a=LKvY6 z;iAnfR1w*?0m2X)QM1#^yc@iBM?VPly>8d zyo%wIM5L(5d451^A*fuG;pdWQiFq{g#R(-Bb1fHfTOXjIrlse@pj+OF$Ed#RKp` zHv~gQc&Z7O1)_Gpn+Wj0N)M1`S;T>^gZkV@t*_G;My|9N91=^w&~+K>2w&gp@#_#t2|}$vi2vQ-oj{DHuwJ-`YTR92nbSv? zSb~4)UVc0>#2mPrgox0=gYp8E@a2w8eaR(Htp z%R({W9TEu;vB#}%1HzuD5~wY9dJBVcWfbf#?PBhWf|ASQo3Y?oH5lbz=^&hp|BVmD za)N+4PK-s1@QXKfa*r97JWN9e30)ciJ`zJ4jpQ)U*cRj6 zbn~CbJeDWUW1FJxUO>x2jVx2vB^bV!LxS2pGn|GG!gK5{WDCrISYPo#_%9}_B~fDq z@>#UjuVRYz@!1YTT}6=9+%j@E;Q^#Ic{)sR3USdkB66AWK00Gbc*#7~JDG3W zPsyfQ6-1KW3?bF+v2fwj0FjBU9dD=+a4T@^m(E$8=f^@G2DU{R{fVjWAjy|QBE(AM zWSn`*l~yOii}4in8%K2+ULHRZ_%DuB+xUhokZaG%MA@a!yswm~)ZY}2wp+RGWDeJQ zs{M}F^wEUYYx&)Kx4gTI8p(G|fNPiF@jm^Jy#p(HD*f868Mit;+p{l@34e+)xfrH7 z{J~{j*g)#*l6-Z)R@c{w@bj5grBidG``4mbw;?@E&}vtjNLBZR(NDGdhc%2fQ>B8K z$EWK{U3s*2yUcoRvlV7Lnl;nJa@al)R?BVuB94k~u}Le8wnsE;59Qow<`09f)F-S9 z_Q=}#2@j1hJWKX)qgvm^+q2^Vcs(cnBRSzbdCea5HG5U&oo0=ZP}e{UvHJUegsf;mpIpG zR_0)|a17{)Ikt=S4~63j@qy;Q3-2hm&YL@@>KEv_!*X>HTyElEG|```;~7xK&Lt7j zx-V7$JGVO44NXv}RUoU@M5m*PXMC>7^=3}8X4o;c3rQK4VJ)BJl4#gNTi%~ZDp>RQ zSpE5y?Dgt54TC>rR~D01C>V}n+^FKa;+b{hsmh%@vel6Z4P8aD_QQ#488>8ga|cEp zdps_NHO~ym{(KYPK&O8cX6UjbZAlHbwH)echJThQC~#C9@inoY(~f^1=X3^`oV1Dg zWEQ8^p3T>}_A}Xc{gDa`)tt5w@$5LdSwVGhe!^=m@>y*fNC6A^LRM-!LR)g1o$_r> z;$xhok`y9CTXHm$`eQW*@6%Ctn%JbcuO3e07qz>Pgx%kKM5jBq_2WfwGQ-T^N^{j2x{VN>?FGE$pz|cfXuLc_%=)huB3zjGSL@7gV zVC=4OI;sFhLPz8MkFa=Cv7}&Ciz7-L%tVurlc+TIK;u|P)$M06i)|3X^QDd=QXMq2 z&lM4Tapnz)!6KfkxpPvS@hzFwsfZqVg^f!ces5}6FJO}zI$4e9r`_OBau#T-Z?Fd> z5Z{1|3w5khKRfFfMDm|q=7`T0b_QugVzQaWhzK5a5G!>Hu zqF_6?y|$v16oVAk{*@)!r`59WboNb6z$;R}c3GD=@PGpW`fD^xpZEmT zlKl6FRfN~h-=Bzfq#nCE!5$c@4HrqCcUd+Zh+QxBxm_7f(_vyRQ5z**#(m{dg`x7` zR4q-XwYfyu%N$SrB*s&r4B{>GbBGYOhqxr7bstw^Lr?j{80)D<2~XObsz0dgWP7Wg z&U-|?D_cF5Kj|`YF3!!ShgZgig86O3NGtgm>$_+pj-DT8y~qRUUQfKbv!<> zBO<98JzuP!?w6;sc(`egW@kY}|5CbenzcY?x%?xuMC zg&fdGeK6D*upn4?l7)0Le2ODlk2s_p?r-U@H4N9j5zbTeEf|JJf0k}#FfS#~R}4iv z{!XvGI%Vv%=B}076ybgJpxFqc+(B&(En;tuNf^46L@lNz8#UPre_?}PT;mh*@A68Y z@~7)`0h07xF|Lt!E2w)Cmmt5tyVCN7)O$ByV(r?Jj|`E2fD{4xR8 zYIY%S_VjJrCB-+)OL4^&oY~&Ve~(Oe)PpfV`orK5QLxu0YgEsF*w?P_xxYYU^Ii@^ z!M2yj3#zIk-&@cf{&?2z>OX34>8DO)TP?Sqe-fq`{HkIXv;7lBQDeckwmmmjPwv@M z$`i6M8P5fWl2=uRpe`XTbABbE54Lxc&qN!KdVjbcEvaZOQpWrYd5Qf(GH1oq(q2xS zlU&RKo*=VLO{!m(ScH$|^Kb56`ITbX@adC92=kY{v9AWxBoQ292tTg*POlDE9g(st zUs^*F*9$X>mb}&oE}Lo7_&_EBwt%V#)4|IzTJjAuDD0C3bDXJ zE1&EaSmDun-Vn?xYd?5xxce(vP~M5mi*KrM`7WN{x%blG?=KN@@-mk`@1jfq zVa5Aj^b#;uGw-XKp9^_X!x!(b{&)wipvL}(?`%!FvsgR@pX^J~`8n@5`Zpm=Cu_2< zfGKNsA;+s?xRfFuJMbGjGb8P;fkwW_kvfOE%Bnh-;acirsRe5q$HdxBW-7)A);xWA zE`a<>oSa{OgdZ|Jh;=Ksdnh%`Wmdb}&%~GdmS*>N`}hY`n#YuwjUu0&2z!`!*6xK= z=?=y^g;*;=uFabRdi6aQN}|WUrc-mKhp-2PQTNWb+YQracC#LjHo5pyESwKZN07>d)Y*iI{0Z_y`0?Vf=(<%9R%Yp!VW-VgFSI8J2gdAg5s?jII%%A&*7_EQxrKsR;i!2=KBOqIdjz&Qn|+l>c~*=R&n004}wl zLic?-z2`{pCBPe^!z1FXU1x8gTwv)w(O(;z0Kp$2B||f+3gp91*969CZ}kge;0aWE zDiB5s6Qu`kU@Qp+?WH&w3g8T-pzWPRtOyO8{O@STj)MR>VqAv)y)Iq50XQBC&_*C5 zjG+$!CnBWwnuzKc%mRotIhr%}0|tn1)RLrt4ZMtj0N?CKBLh^BlXJ0MlNu-2adtP^x2c&o$$Tx&NYTul*A5U-!dlTr(r*ssF}))CJ%FN z8F3Gk`1r*Ez$*ZIDZ>W|l|B)SZTgJ1Ukj?ZEzfbqZi&c|5FUY;b3dNzyT9?weGRzg z<%{11Ku2t$Cxl!AI?BaF#hoOP)nE{al9)york;m3;wtLx8|a@0(_%dq$>aB;Yb2u3 zZs|>-0wbVN4~QSu;iOIJ%~jxNIt?Zz5a?fdI8UsAwer7@R<0Dl+;JnKtG&y_LPEsJ z_)~^J#(onKHY7w#Ic9;Da%&-Kxj1G~B+iiFnN}RvBB0WG1C_U0d=l6jw(g=QtoRAS zIQsgRz-owjB+x&&wnYrM?)~?^T0gpM3he6YVV2HXg8H3Ne@_`w6hUz1cZ%KWy&O=>$GvHbmrz!SNN*S1fdFfZ-SLR0eIVMK?Nlym@x-O!E8 z_8Q*-ml#444-}-q7G7P10n5?&Ka4||74(4OTVnpH)aeRG2>=}u!sMv3Ba(OlR1$SX zyM-dWR{v<<3EIXvUiXQUxTlm%{=uvooM$iJ3Bq8Ha!&}kagCQL=Xmh($O$dC%q5Aa z$agXn-40Pr$J9x+?e9RhFKNCDj_D#Ff>ZdUp$8U7>9L?@I(RV4j8HmP4-dr zeg2KrQKv!(Qwp@!!7yhX2fX_-lo0meGQ+iu0q594AM$w>DJ*DQyor?z1Wqi$pIzs; z(Z2NKtI)iKVUDascZWC^YL#~khd+?A3^hs)ek6I{{QJuex5I*a+`TE^TQL!*66--4 z@9%tpI8~F_`~27cnceFE?Xm(JL)JjK%4BNwOVvHh?tFkU zTUpS(|EwHRC*K0n6q_ifX@K4qL-;8;y9J?F{eY(*CMWbJJ?r(6$|*lG=C_6CNjb;Q z9p_-QB$F_2;FWna+`4VzZDCn3>n-cDi}JqI<5$Tfu8~O`^jfd0zCspH(GAJWZy49~ zLNsoOyGpnyL+=0HbIl{>(e-7#*ghZoap9|?%(xZ~wBIEweJ8MK({B8R9dr*CXU;Uf zkfZa-AY~D@q7N#;yDYhC%5{X%f=nWmpX~{mks^kFH#a@SV6W>v%>N09*}80f*Wqy< zIYBS9OD2FH1)~PV4q7kC)!Qpe0qT~ZEopI%^Vza3&qmgftGwEtJO8q7BF#SeRSE_^F2A3a%t1uyT31mCp!5hsHZ_J4`zzMlJ5%y*g)w++b99GCoM6sE#b+DL8%BD@}KLp>D9z`#o>s zmF9>8*L!7~K9md`-7iM(vBp#pHdT0lR_YbmGnI(|dzydu7@jN-riZjAtZK8I_>2>9 zN1S~8|7&{bbY2X5$6xA?W}6#RkXrjg`;gZ-!~Tu-8g)EQH|YYI zz#-}`nZc67ql>-wck2=F;l{Q(hBQ4b?f&^&MrSWx{bw(yu|L<}*2aOe7?Se*&kFI{ y|F@y)|NM~uLz%gL3yJ^t|E@ay|L@0o*S40$O5E!b+(Cl>yLe9VY>L#?yZ;OINy| Date: Thu, 18 Aug 2022 15:37:56 +0200 Subject: [PATCH 18/20] Update README.md --- README.md | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4db5e77..81ad998 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,8 @@ The library provides functions for plotting projected lines, curves (trajectorie -
+ +

@@ -394,6 +395,58 @@ do so automatically when you pass `clockwise=True` to `tax.ticks()`. There is a [more detailed discussion](https://github.com/marcharper/python-ternary/issues/18) on issue #18 (closed). +# Custom Axis Data Limits + +By default, the axes limits are [0, scale] i.e. simplex coordinates but it is possible to set +custom data limits to the axes instead. This is done by passing a dict into set_axis_limits. +The keys are b, l and r for the three axes and the values are a list of the min and max in +data coords for that axis. max-min for each axis is the same as the scale i.e. 9 in this case. + +```python +tax.set_axis_limits({'b': [60, 75], 'l': [15, 30], 'r': [10, 25]}) +``` + +This can be used to zoom in on a particular region, for example. Please see +the [custom axis scaling example](examples/custom_axis_scaling.py) for further +details. + +

+ +

+ +# Truncated Simplex + +One or more corners can be removed from the simplex by setting a truncation. +This may be useful for saving whitespace if the data are grouped in one area +of the plot. A truncation is specified in data coordinates by passing a dict +into tax.set_truncation. The keys are two letters specifying the start and end +axes of the truncation line and the values give the maximum of the first axis +specified in data coordinates. For the example on the left below, we write: + +```python +tax.set_truncation({'rl' : 8}) +``` + +The result is that the truncation line has been drawn from the right axis at +data coordinate 8 to the left axis, cutting off the top corner. As the figure is no +longer a triangle, we need to get and set custom axis ticks and tick labels. +There are convenience functions for this in the case of a truncation and/or +custom axis data limits. Again for the left image shown below: + +```python +tax.get_ticks_from_axis_limits(multiple=2) +offset=0.013 +tax.set_custom_ticks(fontsize=8, offset=offset, + tick_formats="%.1f", linewidth=0.25) +``` + +

+ + +

+ +Please see the [truncated simplex example](examples/truncated_simplex_example.py) +for further details. # RGBA colors @@ -463,6 +516,7 @@ contribute. - Bryan Weinstein [btweinstein](https://github.com/btweinstein): Hexagonal heatmaps, colored trajectory plots - [chebee7i](https://github.com/chebee7i): Docs and figures, triangular heatmapping - [Cory Simon](https://github.com/CorySimon): Axis Colors, colored heatmap example +- [tgwoodcock](https://github.com/tgwoodcock): Custom axis data limits, truncated simplex # Known-Issues From 5a0c6ad3b9764b81e07c5c81447eb7468984d5e6 Mon Sep 17 00:00:00 2001 From: tgwoodcock Date: Thu, 18 Aug 2022 15:41:46 +0200 Subject: [PATCH 19/20] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 81ad998..3de6c7d 100644 --- a/README.md +++ b/README.md @@ -441,7 +441,7 @@ tax.set_custom_ticks(fontsize=8, offset=offset, ```

- +

From cdc10e9e2f54493d9e87e635a8211af41e29fb5c Mon Sep 17 00:00:00 2001 From: tgwoodcock Date: Tue, 6 Sep 2022 09:10:27 +0200 Subject: [PATCH 20/20] Minor fix to colormapping.colorbar_hack Replaced sm._A = [] with sm.set_array(None) as the former was causing an error with plt.cm.ScalarMappable --- ternary/colormapping.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ternary/colormapping.py b/ternary/colormapping.py index c264463..6c1fe6f 100644 --- a/ternary/colormapping.py +++ b/ternary/colormapping.py @@ -91,7 +91,7 @@ def colorbar_hack(ax, vmin, vmax, cmap, scientific=False, cbarlabel=None, norm=N if norm is None: norm = plt.Normalize(vmin=vmin, vmax=vmax) sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm) - sm._A = [] + sm.set_array(None) cb = plt.colorbar(sm, ax=ax, **kwargs) if cbarlabel is not None: cb.set_label(cbarlabel)