-
Notifications
You must be signed in to change notification settings - Fork 132
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
annotate with two different colors based on threshold #104
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -136,8 +136,8 @@ def plot_traj(traj, colorby='particle', mpp=1, label=False, superimpose=None, | |
ptraj = plot_traj # convenience alias | ||
|
||
@make_axes | ||
def annotate(centroids, image, circle_size=170, color='g', | ||
invert=False, ax=None): | ||
def annotate(centroids, image, circle_size=170, color=None, | ||
invert=False, ax=None, split_category=None, split_thresh=None): | ||
"""Mark identified features with white circles. | ||
|
||
Parameters | ||
|
@@ -146,17 +146,36 @@ def annotate(centroids, image, circle_size=170, color='g', | |
image : image array (or string path to image file) | ||
circle_size : size of circle annotations in matplotlib's annoying | ||
arbitrary units, default 170 | ||
color : string | ||
default 'g' | ||
color : single matplotlib color or a list of multiple colors | ||
default None | ||
invert : If you give a filepath as the image, specify whether to invert | ||
black and white. Default True. | ||
ax : matplotlib axes object, defaults to current axes | ||
|
||
split_category : string, parameter to use to split the data into sections | ||
default None | ||
split_thresh : single value or list of ints or floats threshold to split | ||
particles into sections for plotting in multiple colors | ||
default None | ||
|
||
Returns | ||
------ | ||
axes | ||
""" | ||
import matplotlib.pyplot as plt | ||
from itertools import tee, izip | ||
from collections import Iterable | ||
|
||
# https://docs.python.org/2/library/itertools.html | ||
def pairwise(iterable): | ||
"s -> (s0,s1), (s1,s2), (s2, s3), ..." | ||
a, b = tee(iterable) | ||
next(b, None) | ||
return izip(a, b) | ||
|
||
if color is None: | ||
color = 'g' | ||
if not (split_thresh, Iterable): | ||
split_thresh = [split_thresh] | ||
|
||
# The parameter image can be an image object or a filename. | ||
if isinstance(image, basestring): | ||
|
@@ -167,10 +186,30 @@ def annotate(centroids, image, circle_size=170, color='g', | |
ax.imshow(image, origin='upper', shape=image.shape, cmap=plt.cm.gray) | ||
ax.set_xlim(0, image.shape[1]) | ||
ax.set_ylim(0, image.shape[0]) | ||
ax.scatter(centroids['x'], centroids['y'], | ||
s=circle_size, facecolors='none', edgecolors=color) | ||
|
||
if split_category is None: | ||
if np.size(color) > 1: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does this interact badly with strings? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's fine with strings. One of my successful test cases was: tp.annotate(f, frames[0],color='purple'). I added this exception after trying tp.annotate(f, frames[0],color=['purple','green']), and the annotation circles alternated between the two colors-- not a good result. np.size('green')=1, np.size(['green','purple']=2 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll have to remember that. Thanks, Becca! |
||
raise ValueError("multiple colors specified, no split category specified") | ||
ax.scatter(centroids['x'], centroids['y'], | ||
s=circle_size, facecolors='none', edgecolors=color) | ||
else: | ||
if len(color) != len(split_thresh) + 1: | ||
raise ValueError("number of colors must be number of thresholds plus 1") | ||
low = centroids[split_category] < split_thresh[0] | ||
ax.scatter(centroids['x'][low], centroids['y'][low], | ||
s=circle_size, facecolors='none', edgecolors=color[0]) | ||
|
||
for c, (bot, top) in izip(color[1:-1], pairwise(split_thresh)): | ||
indx = ((centroids[split_category]) >= bot) & ((centroids[split_category]) < top) | ||
ax.scatter(centroids['x'][indx], centroids['y'][indx], | ||
s=circle_size, facecolors='none', edgecolors=c) | ||
|
||
high = centroids[split_category] >= split_thresh[-1] | ||
ax.scatter(centroids['x'][high], centroids['y'][high], | ||
s=circle_size, facecolors='none', edgecolors=color[-1]) | ||
return ax | ||
|
||
|
||
@make_axes | ||
def mass_ecc(f, ax=None): | ||
"""Plot each particle's mass versus eccentricity.""" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know the train's already left the station, but I don't see how this test wouldn't always evaluate to True. Did you mean to write
if not isinstance(split_thresh, Iterable)
?In any case, a string also counts as iterable, so there has to be another way.
Sorry I've been doing nothing but writing comments. I'll be back on my development computer by tomorrow, I promise :).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My fault. This is what I get for late-night merging.
On Wednesday, July 9, 2014, Nathan Keim notifications@github.com wrote:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This bit of code is to handle the case where split_thresh is a single number. I turn it into a list because split_thresh[0] needs to be valid
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that the code, as written, will never put anything into a list, scalar or not. A multi-element tuple always evaluates to True.
I can say that I definitely get the wrong behavior when I try this code at the IPython prompt.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the case I was trying to handle:
tp.annotate(f, frames[0],color=['purple','green'],split_category='signal',split_thresh=10)
I see now that it is broken. What is the proper way to check if something is iterable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isinstance(foo, Iterable)
will returnTrue
if foo is iterable (and properly implements the ABC stuff from collections).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, Tom! I've been doing it a much less elegant way. I guess the only complication is if you'd like strings to count as single values; then you'd need something like
isinstance(foo, Iterable) and not isinstance(foo, str)
. But it looks like that's not the case in this code.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A wonderful phrase I learned last night, 'duck typing brings a can of worms: either you go fishing with them or eat them'