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

Add EventCollection and eventplot #1669

Merged
merged 13 commits into from Feb 21, 2013
3 changes: 3 additions & 0 deletions CHANGELOG
@@ -1,6 +1,9 @@
2013-01-23 Add 'savefig.directory' to rcParams to remember and fill in the last
directory saved to for figure save dialogs - Martin Spacek

2013-01-13 Add eventplot method to axes and pyplot and EventCollection class
to collections.

2013-01-08 Added two extra titles to axes which are flush with the left and
right edges of the plot respectively.
Andrew Dawson
Expand Down
1 change: 1 addition & 0 deletions boilerplate.py
Expand Up @@ -108,6 +108,7 @@ def boilerplate_gen():
'contourf',
'csd',
'errorbar',
'eventplot',
'fill',
'fill_between',
'fill_betweenx',
Expand Down
11 changes: 11 additions & 0 deletions doc/users/whats_new.rst
Expand Up @@ -22,6 +22,17 @@ revision, see the :ref:`github-stats`.
new in matplotlib-1.3
=====================

New eventplot plot type
-------------------------------------
Todd Jennings added a :func:`~matplotlib.pyplot.eventplot` function to
create multiple rows or columns of identical line segments

New EventCollection collections class
-------------------------------------
Todd Jennings created the new :class:`~matplotlib.collections.EventCollection`
class that allows for plotting and manipulating rows or columns of identical
line segments

Baselines for stackplot
-----------------------
Till Stensitzki added non-zero baselines to :func:`~matplotlib.pyplot.stackplot`.
Expand Down
54 changes: 54 additions & 0 deletions examples/pylab_examples/eventcollection_demo.py
@@ -0,0 +1,54 @@
#!/usr/bin/env python
# -*- Coding:utf-8 -*-
'''Plot two curves, then use EventCollections to mark the locations of the x
and y data points on the respective axes for each curve'''

import matplotlib.pyplot as plt
from matplotlib.collections import EventCollection
import numpy as np

# create random data
np.random.seed(50)
xdata = np.random.random([2, 10])

# split the data into two parts
xdata1 = xdata[0, :]
xdata2 = xdata[1, :]

# sort the data so it makes clean curves
xdata1.sort()
xdata2.sort()

# create some y data points
ydata1 = xdata1 ** 2
ydata2 = 1 - xdata2 ** 3

# plot the data
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.plot(xdata1, ydata1, 'r', xdata2, ydata2, 'b')

# create the events marking the x data points
xevents1 = EventCollection(xdata1, color=[1, 0, 0], linelength=0.05)
xevents2 = EventCollection(xdata2, color=[0, 0, 1], linelength=0.05)

# create the events marking the y data points
yevents1 = EventCollection(ydata1, color=[1, 0, 0], linelength=0.05,
orientation='vertical')
yevents2 = EventCollection(ydata2, color=[0, 0, 1], linelength=0.05,
orientation='vertical')

# add the events to the axis
ax.add_collection(xevents1)
ax.add_collection(xevents2)
ax.add_collection(yevents1)
ax.add_collection(yevents2)

# set the limits
ax.set_xlim([0, 1])
ax.set_ylim([0, 1])

ax.set_title('line plot with data points')

# display the plot
plt.show()
67 changes: 67 additions & 0 deletions examples/pylab_examples/eventplot_demo.py
@@ -0,0 +1,67 @@
#!/usr/bin/env python
# -*- Coding:utf-8 -*-
'''an eventplot showing sequences of events with various line properties
the plot is shown in both horizontal and vertical orientations'''

import matplotlib.pyplot as plt
import numpy as np

# set the random seed
np.random.seed(0)

# create random data
data1 = np.random.random([6, 50])

# set different colors for each set of positions
colors1 = np.array([[1, 0, 0],
[0, 1, 0],
[0, 0, 1],
[1, 1, 0],
[1, 0, 1],
[0, 1, 1]])

# set different line properties for each set of positions
# note that some overlap
lineoffsets1 = np.array([-15, -3, 1, 1.5, 6, 10])
linelengths1 = [5, 2, 1, 1, 3, 1.5]

fig = plt.figure()

# create a horizontal plot
ax1 = fig.add_subplot(221)
ax1.eventplot(data1, colors=colors1, lineoffsets=lineoffsets1,
linelengths=linelengths1)
ax1.set_title('horizontal eventplot 1')


# create a vertical plot
ax2 = fig.add_subplot(223)
ax2.eventplot(data1, colors=colors1, lineoffsets=lineoffsets1,
linelengths=linelengths1, orientation='vertical')
ax2.set_title('vertical eventplot 1')

# create another set of random data.
# the gamma distribution is only used fo aesthetic purposes
data2 = np.random.gamma(4, size=[60, 50])

# use individual values for the parameters this time
# these values will be used for all data sets (except lineoffsets2, which
# sets the increment between each data set in this usage)
colors2 = [[0, 0, 0]]
lineoffsets2 = 1
linelengths2 = 1

# create a horizontal plot
ax1 = fig.add_subplot(222)
ax1.eventplot(data2, colors=colors2, lineoffsets=lineoffsets2,
linelengths=linelengths2)
ax1.set_title('horizontal eventplot 2')


# create a vertical plot
ax2 = fig.add_subplot(224)
ax2.eventplot(data2, colors=colors2, lineoffsets=lineoffsets2,
linelengths=linelengths2, orientation='vertical')
ax2.set_title('vertical eventplot 2')

plt.show()
1 change: 1 addition & 0 deletions lib/matplotlib/__init__.py
Expand Up @@ -1089,6 +1089,7 @@ def tk_window_focus():
'matplotlib.tests.test_basic',
'matplotlib.tests.test_bbox_tight',
'matplotlib.tests.test_cbook',
'matplotlib.tests.test_collections',
'matplotlib.tests.test_colorbar',
'matplotlib.tests.test_colors',
'matplotlib.tests.test_contour',
Expand Down
169 changes: 169 additions & 0 deletions lib/matplotlib/axes.py
Expand Up @@ -3812,6 +3812,175 @@ def vlines(self, x, ymin, ymax, colors='k', linestyles='solid',

return coll

@docstring.dedent_interpd
def eventplot(self, positions, orientation='horizontal', lineoffsets=1,
linelengths=1, linewidths=None, colors=None,
linestyles='solid', **kwargs):
"""
Plot identical parallel lines at specific positions.

Call signature::

eventplot(positions, orientation='horizontal', lineoffsets=0,
linelengths=1, linewidths=None, color =None,
linestyles='solid'

Plot parallel lines at the given positions. positions should be a 1D
or 2D array-like object, with each row corresponding to a row or column
of lines.

This type of plot is commonly used in neuroscience for representing
neural events, where it is commonly called a spike raster, dot raster,
or raster plot.

However, it is useful in any situation where you wish to show the
timing or position of multiple sets of discrete events, such as the
arrival times of people to a business on each day of the month or the
date of hurricanes each year of the last century.

*orientation* : [ 'horizonal' | 'vertical' ]
'horizonal' : the lines will be vertical and arranged in rows
"vertical' : lines will be horizontal and arranged in columns

*lineoffsets* :
A float or array-like containing floats.

*linelengths* :
A float or array-like containing floats.

*linewidths* :
A float or array-like containing floats.

*colors*
must be a sequence of RGBA tuples (eg arbitrary color
strings, etc, not allowed) or a list of such sequences

*linestyles* :
[ 'solid' | 'dashed' | 'dashdot' | 'dotted' ] or an array of these
values

For linelengths, linewidths, colors, and linestyles, if only a single
value is given, that value is applied to all lines. If an array-like
is given, it must have the same length as positions, and each value
will be applied to the corresponding row or column in positions.

Returns a list of :class:`matplotlib.collections.EventCollection`
objects that were added.

kwargs are :class:`~matplotlib.collections.LineCollection` properties:

%(LineCollection)s

**Example:**

.. plot:: mpl_examples/pylab_examples/eventplot_demo.py
"""
self._process_unit_info(xdata=positions,
ydata=[lineoffsets, linelengths],
kwargs=kwargs)

# We do the conversion first since not all unitized data is uniform
positions = self.convert_xunits(positions)
lineoffsets = self.convert_yunits(lineoffsets)
linelengths = self.convert_yunits(linelengths)

if not iterable(positions):
positions = [positions]
elif any(iterable(position) for position in positions):
positions = [np.asanyarray(position) for position in positions]
else:
positions = [np.asanyarray(positions)]

if len(positions) == 0:
return []

if not iterable(lineoffsets):
lineoffsets = [lineoffsets]
if not iterable(linelengths):
linelengths = [linelengths]
if not iterable(linewidths):
linewidths = [linewidths]
if not iterable(colors):
colors = [colors]
if hasattr(linestyles, 'lower') or not iterable(linestyles):
linestyles = [linestyles]

lineoffsets = np.asarray(lineoffsets)
linelengths = np.asarray(linelengths)
linewidths = np.asarray(linewidths)

if len(lineoffsets) == 0:
lineoffsets = [None]
if len(linelengths) == 0:
linelengths = [None]
if len(linewidths) == 0:
lineoffsets = [None]
if len(linewidths) == 0:
lineoffsets = [None]
if len(colors) == 0:
colors = [None]

if len(lineoffsets) == 1 and len(positions) != 1:
lineoffsets = np.tile(lineoffsets, len(positions))
lineoffsets[0] = 0
lineoffsets = np.cumsum(lineoffsets)
if len(linelengths) == 1:
linelengths = np.tile(linelengths, len(positions))
if len(linewidths) == 1:
linewidths = np.tile(linewidths, len(positions))
if len(colors) == 1:
colors = np.asanyarray(colors)
colors = np.tile(colors, [len(positions), 1])
if len(linestyles) == 1:
linestyles = [linestyles] * len(positions)

if len(lineoffsets) != len(positions):
raise ValueError('lineoffsets and positions are unequal sized '
'sequences')
if len(linelengths) != len(positions):
raise ValueError('linelengths and positions are unequal sized '
'sequences')
if len(linewidths) != len(positions):
raise ValueError('linewidths and positions are unequal sized '
'sequences')
if len(colors) != len(positions):
raise ValueError('colors and positions are unequal sized '
'sequences')
if len(linestyles) != len(positions):
raise ValueError('linestyles and positions are unequal sized '
'sequences')

colls = []
for position, lineoffset, linelength, linewidth, color, linestyle in \
itertools.izip(positions, lineoffsets, linelengths, linewidths,
colors, linestyles):
coll = mcoll.EventCollection(position,
orientation=orientation,
lineoffset=lineoffset,
linelength=linelength,
linewidth=linewidth,
color=color,
linestyle=linestyle)
self.add_collection(coll)
coll.update(kwargs)
colls.append(coll)

if len(positions) > 0:
minpos = min(position.min() for position in positions)
maxpos = max(position.max() for position in positions)

minline = (lineoffsets - linelengths).min()
maxline = (lineoffsets + linelengths).max()

if colls[0].is_horizontal():
corners = (minpos, minline), (maxpos, maxline)
else:
corners = (minline, minpos), (maxline, maxpos)
self.update_datalim(corners)
self.autoscale_view()

return colls

#### Basic plotting
@docstring.dedent_interpd
def plot(self, *args, **kwargs):
Expand Down