Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit25f2fb7

Browse files
committed
Refactor color parsing of Axes.scatter
1 parent677a3b2 commit25f2fb7

File tree

2 files changed

+195
-103
lines changed

2 files changed

+195
-103
lines changed

‎lib/matplotlib/axes/_axes.py

Lines changed: 138 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
importitertools
33
importlogging
44
importmath
5+
importoperator
56
fromnumbersimportNumber
67
importwarnings
78

@@ -3760,6 +3761,137 @@ def dopatch(xs, ys, **kwargs):
37603761
returndict(whiskers=whiskers,caps=caps,boxes=boxes,
37613762
medians=medians,fliers=fliers,means=means)
37623763

3764+
def_parse_scatter_color_args(self,c,edgecolors,kwargs,xshape,yshape):
3765+
"""
3766+
Helper function to process color related arguments of `.Axes.scatter`.
3767+
3768+
Argument precedence for facecolors:
3769+
3770+
- c (if not None)
3771+
- kwargs['facecolors']
3772+
- kwargs['facecolor']
3773+
- kwargs['color'] (==kwcolor)
3774+
- 'b' if in classic mode else next color from color cycle
3775+
3776+
Argument precedence for edgecolors:
3777+
3778+
- edgecolors (is an explicit kw argument in scatter())
3779+
- kwargs['edgecolor']
3780+
- kwargs['color'] (==kwcolor)
3781+
- 'face' if not in classic mode else None
3782+
3783+
3784+
Arguments
3785+
---------
3786+
c : color or sequence or sequence of color or None
3787+
See argument description of `.Axes.scatter`.
3788+
edgecolors : color or sequence of color or {'face', 'none'} or None
3789+
See argument description of `.Axes.scatter`.
3790+
kwargs : dict
3791+
Additional kwargs. If these keys exist, we pop and process them:
3792+
'facecolors', 'facecolor', 'edgecolor', 'color'
3793+
Note: The dict is modified by this function.
3794+
xshape, yshape : tuple of int
3795+
The shape of the x and y arrays passed to `.Axes.scatter`.
3796+
3797+
"""
3798+
xsize=functools.reduce(operator.mul,xshape)
3799+
ysize=functools.reduce(operator.mul,yshape)
3800+
3801+
facecolors=kwargs.pop('facecolors',None)
3802+
facecolors=kwargs.pop('facecolor',facecolors)
3803+
edgecolors=kwargs.pop('edgecolor',edgecolors)
3804+
3805+
kwcolor=kwargs.pop('color',None)
3806+
3807+
ifkwcolorisnotNoneandcisnotNone:
3808+
raiseValueError("Supply a 'c' argument or a 'color'"
3809+
" kwarg but not both; they differ but"
3810+
" their functionalities overlap.")
3811+
3812+
ifkwcolorisnotNone:
3813+
try:
3814+
mcolors.to_rgba_array(kwcolor)
3815+
exceptValueError:
3816+
raiseValueError("'color' kwarg must be an mpl color"
3817+
" spec or sequence of color specs.\n"
3818+
"For a sequence of values to be color-mapped,"
3819+
" use the 'c' argument instead.")
3820+
ifedgecolorsisNone:
3821+
edgecolors=kwcolor
3822+
iffacecolorsisNone:
3823+
facecolors=kwcolor
3824+
3825+
ifedgecolorsisNoneandnotrcParams['_internal.classic_mode']:
3826+
edgecolors='face'
3827+
3828+
c_none=cisNone
3829+
ifcisNone:
3830+
iffacecolorsisnotNone:
3831+
c=facecolors
3832+
else:
3833+
c= ('b'ifrcParams['_internal.classic_mode']else
3834+
self._get_patches_for_fill.get_next_color())
3835+
3836+
# After this block, c_array will be None unless
3837+
# c is an array for mapping. The potential ambiguity
3838+
# with a sequence of 3 or 4 numbers is resolved in
3839+
# favor of mapping, not rgb or rgba.
3840+
# Convenience vars to track shape mismatch *and* conversion failures.
3841+
valid_shape=True# will be put to the test!
3842+
n_elem=-1# used only for (some) exceptions
3843+
ifc_noneorkwcolorisnotNone:
3844+
c_array=None
3845+
else:
3846+
try:# First, does 'c' look suitable for value-mapping?
3847+
c_array=np.asanyarray(c,dtype=float)
3848+
n_elem=c_array.shape[0]
3849+
ifc_array.shapein [xshape,yshape]:
3850+
c=np.ma.ravel(c_array)
3851+
else:
3852+
ifc_array.shapein ((3,), (4,)):
3853+
_log.warning(
3854+
"'c' argument looks like a single numeric RGB or "
3855+
"RGBA sequence, which should be avoided as value-"
3856+
"mapping will have precedence in case its length "
3857+
"matches with 'x' & 'y'. Please use a 2-D array "
3858+
"with a single row if you really want to specify "
3859+
"the same RGB or RGBA value for all points.")
3860+
# Wrong size; it must not be intended for mapping.
3861+
valid_shape=False
3862+
c_array=None
3863+
exceptValueError:
3864+
# Failed to make a floating-point array; c must be color specs.
3865+
c_array=None
3866+
ifc_arrayisNone:
3867+
try:# Then is 'c' acceptable as PathCollection facecolors?
3868+
colors=mcolors.to_rgba_array(c)
3869+
n_elem=colors.shape[0]
3870+
ifcolors.shape[0]notin (0,1,xsize,ysize):
3871+
# NB: remember that a single color is also acceptable.
3872+
# Besides *colors* will be an empty array if c == 'none'.
3873+
valid_shape=False
3874+
raiseValueError
3875+
exceptValueError:
3876+
ifnotvalid_shape:# but at least one conversion succeeded.
3877+
raiseValueError(
3878+
"'c' argument has {nc} elements, which is not "
3879+
"acceptable for use with 'x' with size {xs}, "
3880+
"'y' with size {ys}."
3881+
.format(nc=n_elem,xs=xsize,ys=ysize)
3882+
)
3883+
# Both the mapping *and* the RGBA conversion failed: pretty
3884+
# severe failure => one may appreciate a verbose feedback.
3885+
raiseValueError(
3886+
"'c' argument must either be valid as mpl color(s) "
3887+
"or as numbers to be mapped to colors. "
3888+
"Here c = {}."# <- beware, could be long depending on c.
3889+
.format(c)
3890+
)
3891+
else:
3892+
colors=None# use cmap, norm after collection is created
3893+
returnc,colors,edgecolors
3894+
37633895
@_preprocess_data(replace_names=["x","y","s","linewidths",
37643896
"edgecolors","c","facecolor",
37653897
"facecolors","color"],
@@ -3865,124 +3997,27 @@ def scatter(self, x, y, s=None, c=None, marker=None, cmap=None, norm=None,
38653997
38663998
"""
38673999
# Process **kwargs to handle aliases, conflicts with explicit kwargs:
3868-
facecolors=None
3869-
edgecolors=kwargs.pop('edgecolor',edgecolors)
3870-
fc=kwargs.pop('facecolors',None)
3871-
fc=kwargs.pop('facecolor',fc)
3872-
iffcisnotNone:
3873-
facecolors=fc
3874-
co=kwargs.pop('color',None)
3875-
ifcoisnotNone:
3876-
try:
3877-
mcolors.to_rgba_array(co)
3878-
exceptValueError:
3879-
raiseValueError("'color' kwarg must be an mpl color"
3880-
" spec or sequence of color specs.\n"
3881-
"For a sequence of values to be color-mapped,"
3882-
" use the 'c' argument instead.")
3883-
ifedgecolorsisNone:
3884-
edgecolors=co
3885-
iffacecolorsisNone:
3886-
facecolors=co
3887-
ifcisnotNone:
3888-
raiseValueError("Supply a 'c' argument or a 'color'"
3889-
" kwarg but not both; they differ but"
3890-
" their functionalities overlap.")
3891-
ifcisNone:
3892-
iffacecolorsisnotNone:
3893-
c=facecolors
3894-
else:
3895-
ifrcParams['_internal.classic_mode']:
3896-
c='b'# The original default
3897-
else:
3898-
c=self._get_patches_for_fill.get_next_color()
3899-
c_none=True
3900-
else:
3901-
c_none=False
3902-
3903-
ifedgecolorsisNoneandnotrcParams['_internal.classic_mode']:
3904-
edgecolors='face'
39054000

39064001
self._process_unit_info(xdata=x,ydata=y,kwargs=kwargs)
39074002
x=self.convert_xunits(x)
39084003
y=self.convert_yunits(y)
39094004

39104005
# np.ma.ravel yields an ndarray, not a masked array,
39114006
# unless its argument is a masked array.
3912-
xy_shape= (np.shape(x),np.shape(y))
4007+
xshape,yshape=np.shape(x),np.shape(y)
39134008
x=np.ma.ravel(x)
39144009
y=np.ma.ravel(y)
39154010
ifx.size!=y.size:
39164011
raiseValueError("x and y must be the same size")
39174012

39184013
ifsisNone:
3919-
ifrcParams['_internal.classic_mode']:
3920-
s=20
3921-
else:
3922-
s=rcParams['lines.markersize']**2.0
3923-
4014+
s= (20ifrcParams['_internal.classic_mode']else
4015+
rcParams['lines.markersize']**2.0)
39244016
s=np.ma.ravel(s)# This doesn't have to match x, y in size.
39254017

3926-
# After this block, c_array will be None unless
3927-
# c is an array for mapping. The potential ambiguity
3928-
# with a sequence of 3 or 4 numbers is resolved in
3929-
# favor of mapping, not rgb or rgba.
3930-
3931-
# Convenience vars to track shape mismatch *and* conversion failures.
3932-
valid_shape=True# will be put to the test!
3933-
n_elem=-1# used only for (some) exceptions
3934-
3935-
ifc_noneorcoisnotNone:
3936-
c_array=None
3937-
else:
3938-
try:# First, does 'c' look suitable for value-mapping?
3939-
c_array=np.asanyarray(c,dtype=float)
3940-
n_elem=c_array.shape[0]
3941-
ifc_array.shapeinxy_shape:
3942-
c=np.ma.ravel(c_array)
3943-
else:
3944-
ifc_array.shapein ((3,), (4,)):
3945-
_log.warning(
3946-
"'c' argument looks like a single numeric RGB or "
3947-
"RGBA sequence, which should be avoided as value-"
3948-
"mapping will have precedence in case its length "
3949-
"matches with 'x' & 'y'. Please use a 2-D array "
3950-
"with a single row if you really want to specify "
3951-
"the same RGB or RGBA value for all points.")
3952-
# Wrong size; it must not be intended for mapping.
3953-
valid_shape=False
3954-
c_array=None
3955-
exceptValueError:
3956-
# Failed to make a floating-point array; c must be color specs.
3957-
c_array=None
3958-
3959-
ifc_arrayisNone:
3960-
try:# Then is 'c' acceptable as PathCollection facecolors?
3961-
colors=mcolors.to_rgba_array(c)
3962-
n_elem=colors.shape[0]
3963-
ifcolors.shape[0]notin (0,1,x.size,y.size):
3964-
# NB: remember that a single color is also acceptable.
3965-
# Besides *colors* will be an empty array if c == 'none'.
3966-
valid_shape=False
3967-
raiseValueError
3968-
exceptValueError:
3969-
ifnotvalid_shape:# but at least one conversion succeeded.
3970-
raiseValueError(
3971-
"'c' argument has {nc} elements, which is not "
3972-
"acceptable for use with 'x' with size {xs}, "
3973-
"'y' with size {ys}."
3974-
.format(nc=n_elem,xs=x.size,ys=y.size)
3975-
)
3976-
# Both the mapping *and* the RGBA conversion failed: pretty
3977-
# severe failure => one may appreciate a verbose feedback.
3978-
raiseValueError(
3979-
"'c' argument must either be valid as mpl color(s) "
3980-
"or as numbers to be mapped to colors. "
3981-
"Here c = {}."# <- beware, could be long depending on c.
3982-
.format(c)
3983-
)
3984-
else:
3985-
colors=None# use cmap, norm after collection is created
4018+
c,colors,edgecolors= \
4019+
self._parse_scatter_color_args(c,edgecolors,kwargs,
4020+
xshape,yshape)
39864021

39874022
# `delete_masked_points` only modifies arguments of the same length as
39884023
# `x`.

‎lib/matplotlib/tests/test_axes.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
fromcollectionsimportnamedtuple
12
fromitertoolsimportproduct
23
fromdistutils.versionimportLooseVersion
34
importio
@@ -1792,6 +1793,62 @@ def test_scatter_c(self, c_case, re_key):
17921793
ax.scatter(x,y,c=c_case,edgecolors="black")
17931794

17941795

1796+
def_params(c=None,xshape=(2,),yshape=(2,),**kwargs):
1797+
edgecolors=kwargs.pop('edgecolors',None)
1798+
return (c,edgecolors,kwargsifkwargsisnotNoneelse {},
1799+
xshape,yshape)
1800+
_result=namedtuple('_result','c, colors')
1801+
@pytest.mark.parametrize('params, expected_result',
1802+
[(_params(),
1803+
_result(c='b',colors=np.array([[0,0,1,1]]))),
1804+
(_params(c='r'),
1805+
_result(c='r',colors=np.array([[1,0,0,1]]))),
1806+
(_params(c='r',colors='b'),
1807+
_result(c='r',colors=np.array([[1,0,0,1]]))),
1808+
# color
1809+
(_params(color='b'),
1810+
_result(c='b',colors=np.array([[0,0,1,1]]))),
1811+
(_params(color=['b','g']),
1812+
_result(c=['b','g'],colors=np.array([[0,0,1,1], [0,.5,0,1]]))),
1813+
])
1814+
deftest_parse_scatter_color_args(params,expected_result):
1815+
frommatplotlib.axesimportAxes
1816+
dummyself='UNUSED'# self is only used in one case, which we do not
1817+
# test. Therefore we can get away without costly
1818+
# creating an Axes instance.
1819+
c,colors,_edgecolors=Axes._parse_scatter_color_args(dummyself,*params)
1820+
assertc==expected_result.c
1821+
assert_allclose(colors,expected_result.colors)
1822+
1823+
del_params
1824+
del_result
1825+
1826+
1827+
@pytest.mark.parametrize('kwargs, expected_edgecolors',
1828+
[(dict(),None),
1829+
(dict(c='b'),None),
1830+
(dict(edgecolors='r'),'r'),
1831+
(dict(edgecolor='r'),'r'),
1832+
(dict(edgecolors='face'),'face'),
1833+
(dict(edgecolors='none'),'none'),
1834+
(dict(edgecolor='r',edgecolors='g'),'r'),
1835+
(dict(c='b',edgecolor='r',edgecolors='g'),'r'),
1836+
(dict(color='r'),'r'),
1837+
(dict(color='r',edgecolor='g'),'g'),
1838+
])
1839+
deftest_parse_scatter_color_args_edgecolors(kwargs,expected_edgecolors):
1840+
frommatplotlib.axesimportAxes
1841+
dummyself='UNUSED'# self is only used in one case, which we do not
1842+
# test. Therefore we can get away without costly
1843+
# creating an Axes instance.
1844+
c=kwargs.pop('c',None)
1845+
edgecolors=kwargs.pop('edgecolors',None)
1846+
_,_,result_edgecolors= \
1847+
Axes._parse_scatter_color_args(dummyself,c,edgecolors,kwargs,
1848+
xshape=(2,),yshape=(2,))
1849+
assertresult_edgecolors==expected_edgecolors
1850+
1851+
17951852
deftest_as_mpl_axes_api():
17961853
# tests the _as_mpl_axes api
17971854
frommatplotlib.projections.polarimportPolarAxes

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp