By Alex Hagen
pyg
started as a simple wrapper around matplotlib
to help me keep my style the same in plotting, but now it's expanded to a full graphics suite. If you get bored reading through the first two examples, skip to the bottom. Those examples are a bit cooler.
For pyg
, we need quite a few requirements. Installation right now is pretty manual, but this should do the trick on unix systems:
pip install numpy scipy matplotlib colours
mkdir ~/util
cd ~/util
git clone https://github.com/alexhagen/pyg -b master pyg
sudo echo "export PYTHONPATH=${PYTHONPATH}:~/util" >> ~/.bashrc
source ~/.bashrc
and then we can just import pyg whenever with
from pyg import twod as pyg2d
pyg
has one main class, a twod
plot type, and it has several other classes. The table
module has some table printing help for Jupyter notebooks and some LaTeX publication helper functions. The threed
module has some matplotlib
three dimensional plotting (this is good for surface plotting, if you're doing geometric visualization, use my pyb
class, which I'll include into pyg
soon), three2twod
is a class for annotating three dimensional plotting (if you have the transformation matrix from 3-d to 2-d). I've created some informative examples of these below. I've put interesting examples first, but they're a little complex. If you want to get started, skip to the "Boring Examples" section.
The following shows an example for 3d plotting using pyg
, which is generally simple except for the conversion from matrix data into a form that can be plotted. Below shows a simple example for a power fit, but an API is soon coming for converting data into the right formats.
from pyg import threed as pyg3d
import numpy as np
plot = pyg3d.pyg3d()
x = np.linspace(0.0, 5.0)
y = np.linspace(0.0, 5.0)
X, Y = np.meshgrid(x, y)
z = np.power(X, 2.0) - np.power(Y, 3.0)
plot.surf(x, y, z)
plot.export('_static/threed_surf')
plot.show(caption='An arbitrary three dimensional surface')
The description of 3 dimensional geometry by annotation is difficult in the best circumstances, but very few things are "best circumstances". Most of the field of visualization relies on software specifically designed for visualization, such as VTK
and its derivatives. This is very powerful, but for someone analyzing data or writing simulations, the last thing they want to do is write an interface to these programs. So, I've written a quick and easy API to keep all the data in Python and visualize the geometry in Blender. Then, I have an interface for which the code can extract the camera parameters of an exported render, and then the user can plot two dimensional annotations overtop of the three-dimensional geometry, in place. A more advanced example is coming, but a rudimentary example is shown below.
from pyg.pyb import pyb
from pyg import three2twod as pyg32d
scene = pyb.pyb()
scene.rpp(c=(0., 0., 0.), l=(100., 100., 100.), name='cube')
scene.flat(color='#fc8d82', name='newgold')
scene.set_matl(obj='cube', matl='newgold')
scene.rpp(c=(0., 0., -65.), l=(500., 500., 30.), name='floor')
scene.flat(color='#888888', name='gray')
scene.set_matl(obj='floor', matl='gray')
scene.run('_static/blenderrender.png')
plot = pyg32d.ann_im('_static/blenderrender.png')
plot.add_data_pointer(0., 0., 0., string=r'$\vec{c} = \left( 0, 0, 0 \right)$',
place=(-500., 200.))
plot.add_legend_entry(color='#fc8d82', name='Cube')\
.add_legend_entry(color='#888888', name='Floor')
plot.legend(loc=2)
plot.export('_static/ann_im', ratio='golden')
plot.show('Using two dimensional annotations on a three dimensional geometric plot')
One thing I always hated about most plotting programs is how hard it is to add "measurements". These are so useful in calling out visual information that they're near universal in CAD, but in most plotting and other visualization, they're nowhere to be found. So, for the most part, I've included measurements in pyg
. The following example shows some measurements of grade distributions at IU's School of Medicine versus their nursing department. It shows how the distributions are clearly not normally distributed, but it also shows the grade inflation for the nursing department, with the overwhelming majority of classes giving A's, whereas the medical school fails a large proportion of students.
from scipy.stats import gaussian_kde as gkde
grades = [4.0, 4.0, 3.7, 3.3, 3.0, 2.7, 2.3, 2.0, 1.7, 1.3, 1.0, 0.7, 0.0]
med = [51, 188, 84, 74, 141, 69, 54, 84, 45, 30, 51, 19, 53]
nur = [228, 160, 89, 58, 77, 38, 17, 10, 1, 0, 0, 0, 0]
_med = []
for m, g in zip(med, grades):
_med += [g] * m
_nur = []
for n, g in zip(nur, grades):
_nur += [g] * n
m_dist = gkde(_med)
n_dist = gkde(_nur)
sigma_m = np.std(_med)
mu_m = np.mean(_med)
sigma_n = np.std(_nur)
mu_n = np.mean(_nur)
_grades = np.linspace(0., 4.0)
plot = pyg2d.pyg2d()
plot.add_line(_grades, m_dist(_grades), linestyle='-', linecolor='#285668')
plot.add_line(_grades, n_dist(_grades), linestyle='-', linecolor='#fc8d82')
plot.add_hmeasure(mu_m + sigma_m, mu_m - sigma_m, 0.35, 'middle $2\sigma$')
plot.add_hmeasure(mu_n + sigma_n, mu_n - sigma_n, 1.5, 'middle $2\sigma$')
plot.xlabel(r'Grade ($g$) [$\text{GPA Points}$]')
plot.ylabel(r'Likelihood ($P$) [ ]')
plot.lines_on()
plot.markers_off()
plot.export('_static/measure', ratio='silver')
plot.show(caption='Depiction of useful measurements on a two-d plot')
x = np.linspace(0.0, 4.0 * np.pi, 1000)
y = np.sin(x)
u_y = 0.1
plot = pyg2d.pyg2d()
plot.add_line(x, y, linestyle='-', linecolor='#285668', yerr=u_y, error_fill=True,
name=r'$\sin \left( \theta \right)$')
plot.xlabel('x-coordinate ($x$) [$\unit{cm}$]')
plot.ylabel('y-coordinate ($y$) [$\unit{cm}$]')
plot.lines_on()
plot.markers_off()
plot.export('_static/line', ratio='silver')
plot.show(caption='A line drawing with uncertainty in y')
x = np.linspace(0.0, 4.0 * np.pi, 1000)
y = 5.0 * np.cos(x)
u_y = 1.0
x_sparse = np.linspace(0.0, 4.0 * np.pi, 10)
y_sparse = 5.0 * np.cos(x_sparse)
u_y_sparse = 1.0
plot = pyg2d.pyg2d()
plot.add_line(x, y, linestyle='-', linecolor='#fc8d82', yerr=u_y, error_fill=True,
name=r'$\sin \left( \theta \right)$')
plot.add_line(x_sparse, y_sparse, linecolor='#000000', yerr=u_y_sparse,
name=r'sparse')
plot.lines_on()
plot.markers_off()
plot.lines['sparse'].set_alpha(1.0)
plot.lines['sparse'].set_markersize(6)
plot.lines['sparse'].set_linewidth(0.0)
plot.export('_static/err', ratio='silver')
plot.show(caption='Sinusoid with uncertainty and a sparsely sampled sinusoid with uncertainty')
The following figure shows the API for plotting data on concurrent axes. There are two different APIs to this: the first requires you to plot your data, and then define a function that converts one axis to another. The other API requires you to plot two different data sets on axes with different limits.
x = np.linspace(0., 4.0 * np.pi, 1000)
y1 = 1.0 * np.sin(x)
y2 = 5.0 * np.cos(x)
plot = pyg2d.pyg2d()
plot.add_line(x, y1, linecolor='#fc8d82', name='$y_{1}$')
plot.add_line_yy(x, y2, linecolor='#285668', name='$y_{2}$')
plot.markers_off()
plot.xlabel('x coordinate ($x$)')
plot.ylabel('y coordinate ($y_{1}$)')
plot.ylabel('y coordinate ($y_{2}$)', axes=plot.ax2)
plot.legend(loc=3)
plot.export('_static/dual', ratio='silver')
plot.show('Sinusoids with the same $x$ axis, on different $y$ axes')
The next figure shows how you can compare a single function against different ordinate axes. This would be useful if you are comparing different units, but I particularly use it when there is some electrical measurement that is calibrated non-linearly (for example, in gamma spectroscopy).
x = np.linspace(0., 4.0 * np.pi, 1000)
y = 1.0 * np.sin(x)
plot = pyg2d.pyg2d()
plot.add_line(x, y, linecolor='#285668', name='$y$')
plot.markers_off()
def pi_div(x):
return x / np.pi
plot.add_xx(pi_div)
plot.xlabel('x coordinate ($x$) [$\unit{cm}$]')
plot.xlabel('x coordinate in terms of $\pi$ ($x$) [$\unit{\pi}$]', axes=plot.ax2)
plot.ylabel('y coordinate ($y$) [$\unit{cm}$]')
plot.export('_static/dualx', ratio='silver')
plot.show('Sinusoid in terms of radians and in terms of $\pi$')
twod.
pyg2d
(env=’plot’, polar=False, colors=’purdue’)[source]¶Bases: object
A pyg.pyg2d
object plots many two-dimensional data types.
The pyg2d
class provides an access to matplotlib
charting functions
and some hook ins to making these functions easier to use and more
repeatable. The constructor itself takes only one optional argument,
env
.
Todo
Add more color schemes and the ability to define and hook in color schemes manually.
Parameters: |
|
---|---|
Returns: | the |
Return type: |
|
add_hline
(y, xmin=None, xmax=None, ls=’solid’, lw=1.5, color=’black’, axes=None)[source]¶pyg2d.add_hline
draws a horizontal line.
pyg2d.add_hline
draws a horizontal line from either the left axis
to the right axis if xmin
and xmax
are not provided, otherwise
it is drawn from xmin
to xmax
at y
. Be careful not to
change from linear to log scale AFTER using this function, as
and this means the line will extend past the extents of the latex page.
Parameters: |
|
---|---|
Returns: | None |
add_subplot
(subp=121, polar=False)[source]¶pyg2d.add_subplot
adds a grid in which you can make subplots.
pyg2d.add_subplot
follows Matlab’s lead and allows you to plot
several axes on one plot. The newly created axes is saved as
pyg2d.ax2
- this should be expanded for more axes later.
Todo
Expand subplotting to be able to use more than two axes total.
Parameters: | subp (int) – If kwarg subp is not defined, the
default is to add a second plot in a 1x2 array. When subp is
defined, it will follow that system (i.e. subp=234 means you
have two rows and three columns and you are plotting in the 4th
postition (2,1) ). |
---|---|
Returns: | None |
add_vline
(x, ymin=None, ymax=None, ls=’solid’, lw=1.5, color=’black’, name=None, axes=None)[source]¶pyg2d.add_vline
draws a vertical line.
pyg2d.add_vline
draws a vertical line from either the bottom axis
to the top axis if ymin
and ymax
are not provided, otherwise
it is drawn from ymin
to ymax
at x
. Be careful not to
change from linear to log scale AFTER using this function, as
and this means the line will extend past the extents of the latex page.
Todo
Fix the latex page extents problem with \(-\infty\)
Parameters: |
|
---|---|
Returns: | None |
fit_lines_on
()[source]¶pyg2d.fit_lines_on
turns on the connector lines for anyReturns: | None |
---|
fit_markers_off
()[source]¶pyg2d.fit_markers_off
turns off the data markers for anyReturns: | None |
---|
legend
(loc=1, exclude=’saljfdaljdfaslkjfd’, axes=None)[source]¶pyg2d.legend
shows the legend on the plot.
pyg2d.legend
toggles the legend showing on. This is done by getting
the included objects and titles from the matplotlib
axis item, and
then checking to see if there is the word ‘connector’ in that title. If
there is that word, then the entry is discarded.
Parameters: | |
---|---|
Returns: | None |
title
(title, axes=None)[source]¶pyg2d.title
adds a title to the plot.
Parameters: | title (str) – the title to be added to the plot. The title can take LaTeX arguments. |
---|---|
Returns: | None |
xlabel
(label, axes=None)[source]¶pyg2d.xlabel
adds a label to the x-axis.
pyg2d.xlabel
adds a label to the x-axis of the current axes (or
other axis given by kwarg axes
).
Parameters: |
|
---|---|
Returns: | None |
xlim
(minx, maxx, axes=None)[source]¶pyg2d.xlim
limits the view of the x-axis to limits.
Parameters: | |
---|---|
Returns: | None |
xticks
(ticks, labels, axes=None)[source]¶pyg2d.xticks
changes the ticks and labels to provided values.
pyg2d.xticks
will move the ticks on the abscissa to the
locations given in ticks
and place the labels in list labels
at
those locations, repsectively.
Parameters: |
|
---|---|
Returns: | None |
ylabel
(label, axes=None)[source]¶pyg2d.ylabel
adds a label to the y-axis.
pyg2d.ylabel
adds a label to the y-axis of the current axes (or
other axis given by kwarg axes
). The label can take LaTeX
arguments and the ah style guide asks for labels given as ‘Label
($variable$) [$unit$]’.
Parameters: |
|
---|---|
Returns: | None |
ylim
(miny, maxy, axes=None)[source]¶pyg2d.ylim
limits the view of the y-axis to limits.
Parameters: | |
---|---|
Returns: | None |
yticks
(ticks, labels, axes=None)[source]¶pyg2d.yticks
changes the ticks and labels to provided values.
pyg2d.yticks
will move the ticks on the ordinate axis to the
locations given in ticks
and place the labels in list labels
at
those locations, repsectively.
Parameters: |
|
---|---|
Returns: | None |
threed.
pyg3d
(env=’plot’, colors=’purdue’)[source]¶Bases: pyg.twod.pyg2d
A pyg.pyg3d
object plots many three-dimensional data types.
The pyg3d
class provides an access to matplotlib
charting functions
and some hook ins to making these functions easier to use and more
repeatable. The constructor itself takes only one optional argument,
env
.
Parameters: | env (plot , gui , or None ) – The environement option defines where you are going to use
the generated plot, with the default option being plot (or printing).
If you are using this to generate plots for a gui, define this option
as gui and the class will choose a prettier parameter set for your
chart. Default: plot . |
---|---|
Returns: | the pyg3d object. |
Return type: | pyg3d |
clabel
(label, axes=None)[source]¶pyg2d.xlabel
adds a label to the x-axis.
pyg2d.xlabel
adds a label to the x-axis of the current axes (or
other axis given by kwarg axes
).
Parameters: |
|
---|---|
Returns: | None |
zlabel
(label, axes=None)[source]¶pyg2d.xlabel
adds a label to the x-axis.
pyg2d.xlabel
adds a label to the x-axis of the current axes (or
other axis given by kwarg axes
).
Parameters: |
|
---|---|
Returns: | None |
Todo
Make sure rotation works here
(The original entry is located in /home/ahagen/code/pyg/pyb/pyb.py:docstring of pyg.pyb.pyb.pyb.cone, line 9.)
Todo
Make sure rotation works here
(The original entry is located in /home/ahagen/code/pyg/pyb/pyb.py:docstring of pyg.pyb.pyb.pyb.rcc, line 3.)
Todo
Add more color schemes and the ability to define and hook in color schemes manually.
(The original entry is located in /home/ahagen/code/pyg/twod.py:docstring of pyg.twod.pyg2d, line 8.)
Todo
Expand subplotting to be able to use more than two axes total.
(The original entry is located in /home/ahagen/code/pyg/twod.py:docstring of pyg.twod.pyg2d.add_subplot, line 7.)
Todo
Fix the latex page extents problem with \(-\infty\)
(The original entry is located in /home/ahagen/code/pyg/twod.py:docstring of pyg.twod.pyg2d.add_vline, line 14.)
Todo
Add more color schemes and the ability to define and hook in color schemes manually.
(The original entry is located in /home/ahagen/code/pyg/twod.py:docstring of twod.pyg2d, line 8.)
Todo
Expand subplotting to be able to use more than two axes total.
(The original entry is located in /home/ahagen/code/pyg/twod.py:docstring of twod.pyg2d.add_subplot, line 7.)
Todo
Fix the latex page extents problem with \(-\infty\)
(The original entry is located in /home/ahagen/code/pyg/twod.py:docstring of twod.pyg2d.add_vline, line 14.)