Skip to content

Permutations #17

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

Merged
merged 10 commits into from
Jun 24, 2015
22 changes: 14 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ tax.scatter(points, marker='s', color='red', label="Red Squares")
tax.legend()
```

Most drawing functions can take standard matplotlib keyword arguments such as [linestyle](http://matplotlib.org/api/lines_api.html#matplotlib.lines.Line2D.set_linestyle) and linewidth. You can use LaTeX in titles and labels.
Most drawing functions can take standard matplotlib keyword arguments such as
[linestyle](http://matplotlib.org/api/lines_api.html#matplotlib.lines.Line2D.set_linestyle)
and linewidth. You can use LaTeX in titles and labels.

If you need to act directly on the underyling matplotlib axes, you can access them:

Expand All @@ -75,12 +77,17 @@ figure, tax = ternary.figure(ax=ax)
...
````

`TernaryAxesSubplot` objects keep track of the scale and supply this parameter to other functions as needed.
`TernaryAxesSubplot` objects keep track of the scale, axes, and other parameters,
supplying them as needed to other functions.

## Simplex Boundary and Gridlines

The following code draws a boundary for the simplex and gridlines.

<<<<<<< HEAD
![Ternary Plot -- Boundary and Gridlines](/readme_images/boundary_and_gridlines.png)
=======
>>>>>>> upstream

```
from matplotlib import pyplot
Expand All @@ -92,7 +99,7 @@ figure, tax = ternary.figure(scale=scale)

# Draw Boundary and Gridlines
tax.boundary(color="black", linewidth=2.0)
tax.gridlines(color="blue", multiple=5) # Every 5th gridline, can be fractional
tax.gridlines(color="blue", multiple=5) # Every 5th gridline, can be a float

# Set Axis labels and Title
fontsize = 20
Expand Down Expand Up @@ -154,7 +161,7 @@ Curves can be plotted by specifying the points of the curve, just like matplotli
ternary.plot(points)
```

Points is a list of tuples or numpy arrays, e.g. [(0.5, 0.25, 0.25), (1./3, 1./3, 1./3)], e.g. as in the [sample data](/curve.txt).
Points is a list of tuples or numpy arrays, such as [(0.5, 0.25, 0.25), (1./3, 1./3, 1./3)],

```
import ternary
Expand All @@ -166,7 +173,7 @@ tax.gridlines(multiple=0.2, color="black")
tax.set_title("Plotting of sample trajectory data", fontsize=20)
points = []
# Load some data, tuples (x,y,z)
with open("curve.txt") as handle:
with open("sample_data/curve.txt") as handle:
for line in handle:
points.append(map(float, line.split(' ')))
# Plot the data
Expand Down Expand Up @@ -258,8 +265,6 @@ import ternary
scale = 60

figure, tax = ternary.figure(scale=scale)
tax.set_title("Scatter Plot", fontsize=20)

tax.heatmapf(shannon_entropy, boundary=True, style="triangular")
tax.boundary(linewidth=2.0)
tax.set_title("Shannon Entropy Heatmap")
Expand All @@ -282,14 +287,15 @@ Make the heatmap as follows:
ternary.heatmap(data, scale, ax=None, cmap=None)
```

or
or on a `TernaryAxesSubplot` object

```
tax.heatmap(data, cmap=None)
```

This can produces images such as:


![Ternary Heatmap Examples](/readme_images/heatmap-dual_vs_triangular.png)

![Ternary Heatmap Examples](/readme_images/heatmap_rsp.png)
Expand Down
15 changes: 15 additions & 0 deletions examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,21 @@ def random_heatmap(scale=4):
pyplot.show()

if __name__ == '__main__':
# Show Coordinates
scale = 3
figure, tax = ternary.figure(scale=scale, permutation="120")
points_lists = [[(0,0,3), (1,0,2), (2,0,1)],
[(3,0,0), (2,1,0), (1,2,0)],
[(0,3,0), (0,2,1), (0,1,2)],
[(1,1,1)]]
colors = ['b', 'r', 'g', 'black']
markers = ['o', 'v', '*', 'd']
for i, points in enumerate(points_lists):
for point in points:
tax.scatter([tuple(point)], color=colors[i], marker=markers[i])
tax.annotate("".join(map(str, point)), tuple(point), color=colors[i])
tax.gridlines(multiple=1.)

## Boundary and Gridlines
scale = 40
figure, ternary_ax = ternary.figure(scale=scale)
Expand Down
139 changes: 77 additions & 62 deletions ternary/heatmapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,29 @@
Various Heatmaps.
"""

import functools
import numpy
from matplotlib import pyplot

from helpers import SQRT3, SQRT3OVER2, unzip, normalize, simplex_iterator
from helpers import SQRT3, SQRT3OVER2, unzip, normalize, simplex_iterator, project_point
import plotting
from colormapping import get_cmap, colormapper, colorbar_hack


hexagon_deltas = generate_hexagon_deltas()

### Heatmap Triangulation Coordinates ###

## Triangular Heatmaps ##

def blend_value(data, i, j, k=None, keys=None):
def blend_value(data, i, j, k, keys=None):
"""Computes the average value of the three vertices of a triangule in the
simplex triangulation, where two of the vertices are on the lower
horizontal."""

key_size = len(data.keys()[0])
if not keys:
keys = [(i, j, k), (i, j + 1, k - 1), (i + 1, j, k - 1)]
keys = triangle_coordinates(i, j, k)
# Reduce key from (i, j, k) to (i, j) if necessary
keys = [tuple(key[:key_size]) for key in keys]

Expand All @@ -32,15 +36,15 @@ def blend_value(data, i, j, k=None, keys=None):
value = None
return value

def alt_blend_value(data, i, j, k=None):
def alt_blend_value(data, i, j, k):
"""Computes the average value of the three vertices of a triangule in the
simplex triangulation, where two of the vertices are on the upper
horizontal."""

keys = [(i, j, k), (i, j + 1, k - 1), (i + 1, j - 1, k)]
keys = alt_triangle_coordinates(i, j, k)
return blend_value(data, i, j, k, keys=keys)

def triangle_coordinates(i, j, k=None):
def triangle_coordinates(i, j, k):
"""
Computes coordinates of the constituent triangles of a triangulation for the
simplex. These triangules are parallel to the lower axis on the lower side.
Expand All @@ -51,13 +55,12 @@ def triangle_coordinates(i, j, k=None):

Returns
-------
A numpy array of coordinates of the hexagon
A numpy array of coordinates of the hexagon (unprojected)
"""

return [(i / 2. + j, i * SQRT3OVER2), (i / 2. + j + 1, i * SQRT3OVER2),
(i / 2. + j + 0.5, (i + 1) * SQRT3OVER2)]
return [(i, j, k), (i + 1, j, k - 1), (i, j + 1, k - 1)]

def alt_triangle_coordinates(i, j, k=None):
def alt_triangle_coordinates(i, j, k):
"""
Computes coordinates of the constituent triangles of a triangulation for the
simplex. These triangules are parallel to the lower axis on the upper side.
Expand All @@ -68,23 +71,40 @@ def alt_triangle_coordinates(i, j, k=None):

Returns
-------
A numpy array of coordinates of the hexagon
A numpy array of coordinates of the hexagon (unprojected)
"""

return [(i/2. + j + 1, i * SQRT3OVER2),
(i/2. + j + 1.5, (i + 1) * SQRT3OVER2),
(i/2. + j + 0.5, (i + 1) * SQRT3OVER2)]
return [(i, j + 1, k - 1), (i + 1, j + 1, k), (i + 1, j, k - 1)]

## Hexagonal Heatmaps ##
## Original Hexagonal heatmap code submitted by https://github.com/btweinstein
# Hexagonal heatmaps do no smooth the colors as in the triangular case.

_alpha = numpy.array([0, 1. / SQRT3])
_deltaup = numpy.array([1. / 2., 1. / (2. * SQRT3)])
_deltadown = numpy.array([1. / 2., - 1. / (2. * SQRT3)])
_i_vec = numpy.array([1. / 2., SQRT3 / 2.])
_i_vec_down = numpy.array([1. / 2., -SQRT3 / 2.])
_deltaX_vec = numpy.array([1. / 2, 0])
def generate_hexagon_deltas():
"""
Generates a dictionary of the necessary additive vectors to generate the
heaxagon points for the haxagonal heatmap.
"""

zero = numpy.array([0, 0, 0])
alpha = numpy.array([-1./3, 2./3, 0])
deltaup = numpy.array([1./3, 1./3, 0])
deltadown = numpy.array([2./3, -1./3, 0])
i_vec = numpy.array([0, 1./2, -1./2])
i_vec_down = numpy.array([1./2, -1./2, 0])
deltaX_vec = numpy.array([1./2, 0, -1./2])

d = dict()
# Corner Points
d["100"] = [zero, -deltaX_vec, -deltadown, -i_vec_down]
d["010"] = [zero, i_vec_down, -alpha, -i_vec]
d["001"] = [zero, i_vec, deltaup, deltaX_vec]
# On the Edges
d["011"] = [i_vec, deltaup, deltadown, -alpha, -i_vec]
d["101"] = [-deltaX_vec, -deltadown, alpha, deltaup, deltaX_vec]
d["110"] = [i_vec_down, -alpha, -deltaup, -deltadown, -i_vec_down]
# Interior point
d["111"] = [alpha, deltaup, deltadown, -alpha, -deltaup, -deltadown]

return d

def hexagon_coordinates(i, j, k):
"""
Expand All @@ -96,42 +116,29 @@ def hexagon_coordinates(i, j, k):

Returns
-------
A numpy array of coordinates of the hexagon
A numpy array of coordinates of the hexagon (unprojected)
"""

steps = i + j + k
ij = numpy.array([i / 2. + j, SQRT3 / 2 * i])

# Corner cases (literally)
if i == steps: # j == k == 0
coords = [ij, ij + _i_vec_down / 2., ij - _alpha, ij - _i_vec / 2.]
elif k == steps: # i == j == 0
coords = [ij, ij + _i_vec / 2., ij + _deltaup, ij + _deltaX_vec]
elif j == steps: # i == k == 0
coords = [ij, ij - _deltaX_vec, ij - _deltadown, ij - _i_vec_down / 2.]
# Now the edges
elif i == 0:
coords = [ij - _deltaX_vec, ij - _deltadown,
ij + _alpha, ij + _deltaup, ij + _deltaX_vec]
elif j == 0:
coords = [ij + _i_vec / 2., ij + _deltaup, ij + _deltadown,
ij - _alpha, ij - _i_vec / 2.]
elif k == 0:
coords = [ij + _i_vec_down / 2., ij - _alpha, ij - _deltaup,
ij - _deltadown, ij - _i_vec_down / 2.]
# Must be an interior point
else:
coords = [ij + _alpha, ij + _deltaup, ij + _deltadown,
ij - _alpha, ij - _deltaup, ij - _deltadown]

return numpy.array(coords)
signature = ""
for x in [i, j, k]:
if x == 0:
signature += "0"
else:
signature += "1"
deltas = hexagon_deltas[signature]
center = numpy.array([i, j, k])
return numpy.array([center + x for x in deltas])

## Heatmaps ##

def polygon_iterator(data, scale, style):
"""Iterator for the vertices of the polygon to be colored and its color,
def polygon_generator(data, scale, style, permutation=None):
"""Generator for the vertices of the polygon to be colored and its color,
depending on style. Called by heatmap."""

# We'll project the coordinates inside this function to prevent
# passing around permutation more than necessary
project = functools.partial(project_point, permutation=permutation)

for key, value in sorted(data.items()):
if value is None:
continue
Expand All @@ -140,29 +147,30 @@ def polygon_iterator(data, scale, style):
k = scale - i - j
if style == 'h':
vertices = hexagon_coordinates(i, j, k)
yield (vertices, value)
yield (map(project, vertices), value)
elif style == 'd':
# Upright triangles
vertices = triangle_coordinates(i, j, k)
yield (vertices, value)
yield (map(project, vertices), value)
# Upside-down triangles
vertices = alt_triangle_coordinates(i, j, k)
value = blend_value(data, i, j, k)
yield (vertices, value)
yield (map(project, vertices), value)
elif style == 't':
# Upright triangles
vertices = triangle_coordinates(i, j, k)
value = blend_value(data, i, j, k)
yield (vertices, value)
yield (map(project, vertices), value)
# If not on the boundary add the upside-down triangle
if (j == 0) or (j == scale):
if i == scale:
continue
vertices = alt_triangle_coordinates(i, j - 1, k + 1)
vertices = alt_triangle_coordinates(i, j, k)
value = alt_blend_value(data, i, j, k)
yield (vertices, value)
yield (map(project, vertices), value)

def heatmap(data, scale, vmin=None, vmax=None, cmap=None, ax=None,
scientific=False, style='triangular', colorbar=True):
scientific=False, style='triangular', colorbar=True,
permutation=None):
"""
Plots heatmap of given color values.

Expand All @@ -187,6 +195,8 @@ def heatmap(data, scale, vmin=None, vmax=None, cmap=None, ax=None,
The style of the heatmap, "triangular", "dual-triangular" or "hexagonal"
colorbar: bool, True
Show colorbar.
permutation: string, None
A permutation of the coordinates

Returns
-------
Expand All @@ -204,7 +214,8 @@ def heatmap(data, scale, vmin=None, vmax=None, cmap=None, ax=None,
if style not in ["t", "h", 'd']:
raise ValueError("Heatmap style must be 'triangular', 'dual-triangular', or 'hexagonal'")

vertices_values = polygon_iterator(data, scale, style=style)
vertices_values = polygon_generator(data, scale, style,
permutation=permutation)

# Draw the polygons and color them
for vertices, value in vertices_values:
Expand All @@ -222,7 +233,8 @@ def heatmap(data, scale, vmin=None, vmax=None, cmap=None, ax=None,
## User Convenience Functions ##

def heatmapf(func, scale=10, boundary=True, cmap=None, ax=None,
scientific=False, style='triangular', colorbar=True):
scientific=False, style='triangular', colorbar=True,
permutation=None):
"""
Computes func on heatmap partition coordinates and plots heatmap. In other
words, computes the function on lattice points of the simplex (normalized
Expand All @@ -246,6 +258,8 @@ def heatmapf(func, scale=10, boundary=True, cmap=None, ax=None,
Whether to use scientific notation for colorbar numbers.
colorbar: bool, True
Show colorbar.
permutation: string, None
A permutation of the coordinates

Returns
-------
Expand All @@ -258,5 +272,6 @@ def heatmapf(func, scale=10, boundary=True, cmap=None, ax=None,
data[(i, j)] = func(normalize([i, j, k]))
# Pass everything to the heatmapper
ax = heatmap(data, scale, cmap=cmap, ax=ax, style=style,
scientific=scientific, colorbar=colorbar)
scientific=scientific, colorbar=colorbar,
permutation=permutation)
return ax
Loading