Skip to content

Commit 6fe5fa0

Browse files
committed
Make scatter use the new marker class. Add a "scatter_symbol" example. Make legend support the new kind of scatter.
1 parent 85b5e28 commit 6fe5fa0

File tree

8 files changed

+341
-277
lines changed

8 files changed

+341
-277
lines changed

CHANGELOG

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
2001-07-15 The set of markers available in the plot() and scatter()
2+
commands has been unified. In general, this gives more
3+
options to both than were previously available, however,
4+
there is one backward-incompatible change to the markers in
5+
scatter:
6+
7+
"d" used to mean "diamond", it now means "narrow
8+
diamond", now "D" can be used for a "diamond".
9+
110
2011-07-10 Fixed argument handling error in tripcolor/triplot/tricontour,
211
issue #203. - IMT
312

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from matplotlib import pyplot as plt
2+
import numpy as np
3+
import matplotlib
4+
5+
x = np.arange(0.0, 50.0, 2.0)
6+
y = x ** 1.3 + np.random.rand(*x.shape) * 30.0
7+
s = np.random.rand(*x.shape) * 800 + 500
8+
9+
plt.scatter(x, y, s, c="g", alpha=0.5, marker=r'$\clubsuit$',
10+
label="Luck")
11+
plt.xlabel("Leprechauns")
12+
plt.ylabel("Gold")
13+
plt.legend(loc=2)
14+
plt.show()

lib/matplotlib/axes.py

Lines changed: 12 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import matplotlib.image as mimage
2323
import matplotlib.legend as mlegend
2424
import matplotlib.lines as mlines
25+
import matplotlib.markers as mmarkers
2526
import matplotlib.mlab as mlab
2627
import matplotlib.path as mpath
2728
import matplotlib.patches as mpatches
@@ -5651,23 +5652,8 @@ def scatter(self, x, y, s=20, c='b', marker='o', cmap=None, norm=None,
56515652
*marker*:
56525653
can be one of:
56535654
5654-
======= ==============
5655-
Value Description
5656-
======= ==============
5657-
``'s'`` square
5658-
``'o'`` circle
5659-
``'^'`` triangle up
5660-
``'>'`` triangle right
5661-
``'v'`` triangle down
5662-
``'<'`` triangle left
5663-
``'d'`` diamond
5664-
``'p'`` pentagon
5665-
``'h'`` hexagon
5666-
``'8'`` octagon
5667-
``'+'`` plus
5668-
``'x'`` cross
5669-
======= ==============
5670-
5655+
# TODO: Use documentation in MarkerStyle
5656+
56715657
The marker can also be a tuple (*numsides*, *style*,
56725658
*angle*), which will create a custom, regular symbol.
56735659
@@ -5749,21 +5735,6 @@ def scatter(self, x, y, s=20, c='b', marker='o', cmap=None, norm=None,
57495735

57505736
if not self._hold: self.cla()
57515737

5752-
syms = { # a dict from symbol to (numsides, angle)
5753-
's' : (4,math.pi/4.0,0), # square
5754-
'o' : (0,0,3), # circle
5755-
'^' : (3,0,0), # triangle up
5756-
'>' : (3,math.pi/2.0,0), # triangle right
5757-
'v' : (3,math.pi,0), # triangle down
5758-
'<' : (3,3*math.pi/2.0,0), # triangle left
5759-
'd' : (4,0,0), # diamond
5760-
'p' : (5,0,0), # pentagon
5761-
'h' : (6,0,0), # hexagon
5762-
'8' : (8,0,0), # octagon
5763-
'+' : (4,0,2), # plus
5764-
'x' : (4,math.pi/4.0,2) # cross
5765-
}
5766-
57675738
self._process_unit_info(xdata=x, ydata=y, kwargs=kwargs)
57685739
x = self.convert_xunits(x)
57695740
y = self.convert_yunits(y)
@@ -5814,87 +5785,21 @@ def scatter(self, x, y, s=20, c='b', marker='o', cmap=None, norm=None,
58145785
marker = (verts, 0)
58155786
verts = None
58165787

5817-
if is_string_like(marker):
5818-
# the standard way to define symbols using a string character
5819-
sym = syms.get(marker)
5820-
if sym is None and verts is None:
5821-
raise ValueError('Unknown marker symbol to scatter')
5822-
numsides, rotation, symstyle = syms[marker]
5823-
5824-
elif iterable(marker):
5825-
# accept marker to be:
5826-
# (numsides, style, [angle])
5827-
# or
5828-
# (verts[], style, [angle])
5829-
5830-
if len(marker)<2 or len(marker)>3:
5831-
raise ValueError('Cannot create markersymbol from marker')
5832-
5833-
if cbook.is_numlike(marker[0]):
5834-
# (numsides, style, [angle])
5835-
5836-
if len(marker)==2:
5837-
numsides, rotation = marker[0], 0.
5838-
elif len(marker)==3:
5839-
numsides, rotation = marker[0], marker[2]
5840-
sym = True
5841-
5842-
if marker[1] in (1,2,3):
5843-
symstyle = marker[1]
5844-
5845-
else:
5846-
verts = np.asarray(marker[0])
5847-
5848-
if sym is not None:
5849-
if symstyle==0:
5850-
collection = mcoll.RegularPolyCollection(
5851-
numsides, rotation, scales,
5852-
facecolors = colors,
5853-
edgecolors = edgecolors,
5854-
linewidths = linewidths,
5855-
offsets = zip(x,y),
5856-
transOffset = self.transData,
5857-
)
5858-
elif symstyle==1:
5859-
collection = mcoll.StarPolygonCollection(
5860-
numsides, rotation, scales,
5861-
facecolors = colors,
5862-
edgecolors = edgecolors,
5863-
linewidths = linewidths,
5864-
offsets = zip(x,y),
5865-
transOffset = self.transData,
5866-
)
5867-
elif symstyle==2:
5868-
collection = mcoll.AsteriskPolygonCollection(
5869-
numsides, rotation, scales,
5870-
facecolors = colors,
5871-
edgecolors = 'face',
5872-
linewidths = linewidths,
5873-
offsets = zip(x,y),
5874-
transOffset = self.transData,
5875-
)
5876-
elif symstyle==3:
5877-
collection = mcoll.CircleCollection(
5878-
scales,
5879-
facecolors = colors,
5880-
edgecolors = edgecolors,
5881-
linewidths = linewidths,
5882-
offsets = zip(x,y),
5883-
transOffset = self.transData,
5884-
)
5885-
else:
5886-
rescale = np.sqrt(max(verts[:,0]**2+verts[:,1]**2))
5887-
verts /= rescale
5888-
5889-
collection = mcoll.PolyCollection(
5890-
(verts,), scales,
5788+
marker_obj = mmarkers.MarkerStyle(marker)
5789+
path = marker_obj.get_path().transformed(
5790+
marker_obj.get_transform())
5791+
if not marker_obj.is_filled():
5792+
edgecolors = 'face'
5793+
5794+
collection = mcoll.PathCollection(
5795+
(path,), scales,
58915796
facecolors = colors,
58925797
edgecolors = edgecolors,
58935798
linewidths = linewidths,
58945799
offsets = zip(x,y),
58955800
transOffset = self.transData,
58965801
)
5897-
collection.set_transform(mtransforms.IdentityTransform())
5802+
collection.set_transform(mtransforms.IdentityTransform())
58985803
collection.set_alpha(alpha)
58995804
collection.update(kwargs)
59005805

lib/matplotlib/collections.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,7 @@ class PathCollection(Collection):
574574
This is the most basic :class:`Collection` subclass.
575575
"""
576576
@docstring.dedent_interpd
577-
def __init__(self, paths, **kwargs):
577+
def __init__(self, paths, sizes=None, **kwargs):
578578
"""
579579
*paths* is a sequence of :class:`matplotlib.path.Path`
580580
instances.
@@ -584,11 +584,25 @@ def __init__(self, paths, **kwargs):
584584

585585
Collection.__init__(self, **kwargs)
586586
self.set_paths(paths)
587-
587+
self._sizes = sizes
588588

589589
def set_paths(self, paths):
590590
self._paths = paths
591-
591+
592+
def get_paths(self):
593+
return self._paths
594+
595+
def get_sizes(self):
596+
return self._sizes
597+
598+
@allow_rasterization
599+
def draw(self, renderer):
600+
if self._sizes is not None:
601+
self._transforms = [
602+
transforms.Affine2D().scale(
603+
(np.sqrt(x) * self.figure.dpi / 72.0))
604+
for x in self._sizes]
605+
return Collection.draw(self, renderer)
592606

593607
class PolyCollection(Collection):
594608
@docstring.dedent_interpd

lib/matplotlib/legend.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from matplotlib.lines import Line2D
2424
from matplotlib.patches import Patch, Rectangle, Shadow, FancyBboxPatch
2525
from matplotlib.collections import LineCollection, RegularPolyCollection, \
26-
CircleCollection
26+
CircleCollection, PathCollection
2727
from matplotlib.transforms import Bbox, BboxBase, TransformedBbox, BboxTransformTo, BboxTransformFrom
2828

2929
from matplotlib.offsetbox import HPacker, VPacker, TextArea, DrawingArea, DraggableOffsetBox
@@ -481,6 +481,7 @@ def _approx_text_height(self, renderer=None):
481481
CircleCollection:legend_handler.HandlerCircleCollection(),
482482
BarContainer:legend_handler.HandlerPatch(update_func=legend_handler.update_from_first_child),
483483
tuple:legend_handler.HandlerTuple(),
484+
PathCollection:legend_handler.HandlerPathCollection()
484485
}
485486

486487
# (get|set|update)_default_handler_maps are public interfaces to

lib/matplotlib/legend_handler.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,19 @@ def create_artists(self, legend, orig_handle,
362362

363363
return [p]
364364

365+
class HandlerPathCollection(HandlerRegularPolyCollection):
366+
"""
367+
Handler for PathCollections, which are used by scatter
368+
"""
369+
def create_collection(self, orig_handle, sizes, offsets, transOffset):
370+
p = type(orig_handle)([orig_handle.get_paths()[0]],
371+
sizes=sizes,
372+
offsets=offsets,
373+
transOffset=transOffset,
374+
)
375+
return p
365376

377+
366378
class HandlerCircleCollection(HandlerRegularPolyCollection):
367379
"""
368380
Handler for CircleCollections

lib/matplotlib/lines.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,12 @@ class Line2D(Artist):
103103
# Need a list ordered with long names first:
104104
drawStyleKeys = _drawStyles_l.keys() + _drawStyles_s.keys()
105105

106+
# Referenced here to maintain API. These are defined in
107+
# MarkerStyle
108+
markers = MarkerStyle.markers
109+
filled_markers = MarkerStyle.filled_markers
110+
fillStyles = MarkerStyle.fillstyles
111+
106112
zorder = 2
107113
validCap = ('butt', 'round', 'projecting')
108114
validJoin = ('miter', 'round', 'bevel')

0 commit comments

Comments
 (0)