#
# The high-throughput toolkit (httk)
# Copyright (C) 2012-2015 Rickard Armiento
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Uses parts from 'dave', http://stackoverflow.com/questions/701429/library-tool-for-drawing-ternary-triangle-plots
from math import atan, pi
[docs]
class PolygonPlot(object):
def __init__(self, start_angle=90, rotate_labels=False, labels=('one', 'two', 'three', 'four', 'five'), sides=3,
label_offset=0.10, edge_args={'color': 'black', 'linewidth': 2}, fig_args = {'figsize': (8, 8), 'facecolor': 'white', 'edgecolor': 'white'},
text_args = {'fontsize': 24, 'color': 'black'}):
"""
start_angle (90): Direction of first vertex.
rotate_labels (False): Orient labels perpendicular to vertices.
labels ('one','two','three'): Labels for vertices.
sides (3): Number of dimensions to accommodate
label_offset (0.10): Offset for label from vertex (percent of distance from origin).
edge_args ({'color':'black','linewidth':2}): matplotlib keyword args for plots.
fig_args ({'figsize':(8,8),'facecolor':'white','edgecolor':'white'}): matplotlib keyword args for figures.
text_args: matplotlib keyword args for axis labels.
"""
from httk.external.matplotlib_ext import pylab
self.basis = pylab.array(
[
[
pylab.cos(2*_*pylab.pi/sides + start_angle*pylab.pi/180),
pylab.sin(2*_*pylab.pi/sides + start_angle*pylab.pi/180)
]
for _ in range(sides)
]
)
fig = pylab.figure(**fig_args)
ax = fig.add_subplot(111)
for i, l in enumerate(labels):
if i >= sides:
break
x = self.basis[i, 0]
y = self.basis[i, 1]
if rotate_labels:
angle = 180*atan(y/x)/pi + 90
if angle > 90 and angle <= 270:
angle = (angle + 180) % 360
else:
angle = 0
ax.text(
x*(1 + label_offset),
y*(1 + label_offset),
l,
horizontalalignment='center',
verticalalignment='center',
rotation=angle,
**text_args
)
# Clear normal matplotlib axes graphics.
ax.set_xticks(())
ax.set_yticks(())
ax.set_frame_on(False)
# Plot border
ax.plot(
[self.basis[_, 0] for _ in list(range(sides)) + [0, ]],
[self.basis[_, 1] for _ in list(range(sides)) + [0, ]],
**edge_args
)
self.ax = ax
[docs]
def translate_coords(self, data, scaling=True):
from httk.external.matplotlib_ext import pylab
if len(data) == 0:
return data
data = pylab.array(data)
# If data is Nxsides, newdata is Nx2.
if scaling:
# Scales data for you.
newdata = pylab.dot((data.T / data.sum(-1)).T, self.basis)
else:
# Assumes data already sums to 1.
newdata = pylab.dot(data, self.basis)
return newdata
__all__ = ['PolygonPlot']
if __name__ == '__main__':
from httk.external.matplotlib_ext import pylab
k = 0.5
s = 1000
data = pylab.vstack((
pylab.array([k, 0, 0]) + pylab.rand(s, 3),
pylab.array([0, k, 0]) + pylab.rand(s, 3),
pylab.array([0, 0, k]) + pylab.rand(s, 3)
))
color = pylab.array([[1, 0, 0]]*s + [[0, 1, 0]]*s + [[0, 0, 1]]*s)
pp = PolygonPlot()
newdata = pp.translate_coords(data)
pp.ax.scatter(
newdata[:, 0],
newdata[:, 1],
s=2,
alpha=0.5,
color=color
)
pylab.show(block=False)
pylab.pause(3)
pylab.close()
data = pylab.vstack((
pylab.array([k, 0, 0, 0, 0]) + pylab.rand(s, 5),
pylab.array([0, k, 0, 0, 0]) + pylab.rand(s, 5),
pylab.array([0, 0, k, 0, 0]) + pylab.rand(s, 5),
pylab.array([0, 0, 0, k, 0]) + pylab.rand(s, 5),
pylab.array([0, 0, 0, 0, k]) + pylab.rand(s, 5),
))
color = pylab.array([[1, 0, 0]]*s + [[0, 1, 0]]*s + [[0, 0, 1]]*s + [[1, 1, 0]]*s + [[0, 1, 1]]*s)
pp = PolygonPlot(sides=5)
newdata = pp.translate_coords(data)
pp.ax.scatter(
newdata[:, 0],
newdata[:, 1],
s=2,
alpha=0.5,
color=color
)
pylab.show(block=False)
pylab.pause(3)
pylab.close()