Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Truncation #198

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
*.pyc

# general things to ignore
build/
dist/
*.egg-info/
*.egg
*.py[cod]
__pycache__/
*.ipynb_checkpoints/
56 changes: 55 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ The library provides functions for plotting projected lines, curves (trajectorie
<img src="/readme_images/rgba_example.png" width="150" height="150"/>
<img src="/readme_images/various_lines.png" width="150" height="150"/>
<img src="/readme_images/colored_trajectory.png" width="150" height="150"/>
<img src="/readme_images/scatter.png" width="150" height="150"/><br/>
<img src="/readme_images/scatter.png" width="150" height="150"/>
<img src="/readme_images/truncation_all_corners.png" width="200" height="150"/><br/>
<br/>
<img src="/readme_images/btweinstein_example2.png" width="300" height="150"/>
<img src="/readme_images/btweinstein_example.png" width="300" height="150"/>
Expand Down Expand Up @@ -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.

<p align="center">
<img src="/readme_images/zoom_example.png" width="732" height="400"/>
</p>

# 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)
```

<p align="center">
<img src="/readme_images/truncation_top_corner.png" width="400" height="300"/>
<img src="/readme_images/truncation_all_corners.png" width="314" height="236"/>
</p>

Please see the [truncated simplex example](examples/truncated_simplex_example.py)
for further details.

# RGBA colors

Expand Down Expand Up @@ -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

Expand Down
63 changes: 37 additions & 26 deletions examples/custom_axis_scaling.py
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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()
Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -114,8 +126,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),
Expand All @@ -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()
139 changes: 139 additions & 0 deletions examples/truncated_simplex_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
"""
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 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")


# Draw Boundary and Gridlines
tax.boundary(linewidth=0.25)
tax.gridlines(color="black", multiple=1, linewidth=0.25, ls='-')


# 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)


# 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)
# 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()







## 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)
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)

# 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})

# Draw Boundary and Gridlines
tax.boundary(linewidth=0.25)
tax.gridlines(color="black", multiple=1, linewidth=0.25, ls='-')

# 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
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 along each axis 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="%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([0, 14, -2, 9])
tax._redraw_labels()

ternary.plt.show()
Binary file added readme_images/truncation_all_corners.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readme_images/truncation_top_corner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readme_images/zoom_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion ternary/colormapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading