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

Commit99cae22

Browse files
committed
Refactor color parsing of Axes.scatter
1 parentbed61ee commit99cae22

File tree

2 files changed

+211
-108
lines changed

2 files changed

+211
-108
lines changed

‎lib/matplotlib/axes/_axes.py

Lines changed: 151 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
importitertools
44
importlogging
55
importmath
6+
importoperator
67
fromnumbersimportNumber
78
importwarnings
89

@@ -4012,6 +4013,150 @@ def dopatch(xs, ys, **kwargs):
40124013
returndict(whiskers=whiskers,caps=caps,boxes=boxes,
40134014
medians=medians,fliers=fliers,means=means)
40144015

4016+
def_parse_scatter_color_args(self,c,edgecolors,kwargs,xshape,yshape):
4017+
"""
4018+
Helper function to process color related arguments of `.Axes.scatter`.
4019+
4020+
Argument precedence for facecolors:
4021+
4022+
- c (if not None)
4023+
- kwargs['facecolors']
4024+
- kwargs['facecolor']
4025+
- kwargs['color'] (==kwcolor)
4026+
- 'b' if in classic mode else next color from color cycle
4027+
4028+
Argument precedence for edgecolors:
4029+
4030+
- edgecolors (is an explicit kw argument in scatter())
4031+
- kwargs['edgecolor']
4032+
- kwargs['color'] (==kwcolor)
4033+
- 'face' if not in classic mode else None
4034+
4035+
Arguments
4036+
---------
4037+
c : color or sequence or sequence of color or None
4038+
See argument description of `.Axes.scatter`.
4039+
edgecolors : color or sequence of color or {'face', 'none'} or None
4040+
See argument description of `.Axes.scatter`.
4041+
kwargs : dict
4042+
Additional kwargs. If these keys exist, we pop and process them:
4043+
'facecolors', 'facecolor', 'edgecolor', 'color'
4044+
Note: The dict is modified by this function.
4045+
xshape, yshape : tuple of int
4046+
The shape of the x and y arrays passed to `.Axes.scatter`.
4047+
4048+
Returns
4049+
-------
4050+
c
4051+
The input *c* if it was not *None*, else some color specification
4052+
derived from the other inputs or defaults.
4053+
colors : array(N, 4) or None
4054+
The facecolors as RGBA values or *None* if a colormap is used.
4055+
edgecolors
4056+
The edgecolor specification.
4057+
4058+
"""
4059+
xsize=functools.reduce(operator.mul,xshape,1)
4060+
ysize=functools.reduce(operator.mul,yshape,1)
4061+
4062+
facecolors=kwargs.pop('facecolors',None)
4063+
facecolors=kwargs.pop('facecolor',facecolors)
4064+
edgecolors=kwargs.pop('edgecolor',edgecolors)
4065+
4066+
kwcolor=kwargs.pop('color',None)
4067+
4068+
ifkwcolorisnotNoneandcisnotNone:
4069+
raiseValueError("Supply a 'c' argument or a 'color'"
4070+
" kwarg but not both; they differ but"
4071+
" their functionalities overlap.")
4072+
4073+
ifkwcolorisnotNone:
4074+
try:
4075+
mcolors.to_rgba_array(kwcolor)
4076+
exceptValueError:
4077+
raiseValueError("'color' kwarg must be an mpl color"
4078+
" spec or sequence of color specs.\n"
4079+
"For a sequence of values to be color-mapped,"
4080+
" use the 'c' argument instead.")
4081+
ifedgecolorsisNone:
4082+
edgecolors=kwcolor
4083+
iffacecolorsisNone:
4084+
facecolors=kwcolor
4085+
4086+
ifedgecolorsisNoneandnotrcParams['_internal.classic_mode']:
4087+
edgecolors='face'
4088+
4089+
c_was_none=cisNone
4090+
ifcisNone:
4091+
c= (facecolorsiffacecolorsisnotNone
4092+
else"b"ifrcParams['_internal.classic_mode']
4093+
elseself._get_patches_for_fill.get_next_color())
4094+
4095+
# After this block, c_array will be None unless
4096+
# c is an array for mapping. The potential ambiguity
4097+
# with a sequence of 3 or 4 numbers is resolved in
4098+
# favor of mapping, not rgb or rgba.
4099+
# Convenience vars to track shape mismatch *and* conversion failures.
4100+
valid_shape=True# will be put to the test!
4101+
n_elem=-1# used only for (some) exceptions
4102+
4103+
if (c_was_noneor
4104+
kwcolorisnotNoneor
4105+
isinstance(c,str)or
4106+
(isinstance(c,collections.abc.Iterable)and
4107+
len(c)>0and
4108+
isinstance(cbook.safe_first_element(c),str))):
4109+
c_array=None
4110+
else:
4111+
try:# First, does 'c' look suitable for value-mapping?
4112+
c_array=np.asanyarray(c,dtype=float)
4113+
n_elem=c_array.shape[0]
4114+
ifc_array.shapein [xshape,yshape]:
4115+
c=np.ma.ravel(c_array)
4116+
else:
4117+
ifc_array.shapein ((3,), (4,)):
4118+
_log.warning(
4119+
"'c' argument looks like a single numeric RGB or "
4120+
"RGBA sequence, which should be avoided as value-"
4121+
"mapping will have precedence in case its length "
4122+
"matches with 'x' & 'y'. Please use a 2-D array "
4123+
"with a single row if you really want to specify "
4124+
"the same RGB or RGBA value for all points.")
4125+
# Wrong size; it must not be intended for mapping.
4126+
valid_shape=False
4127+
c_array=None
4128+
exceptValueError:
4129+
# Failed to make a floating-point array; c must be color specs.
4130+
c_array=None
4131+
ifc_arrayisNone:
4132+
try:# Then is 'c' acceptable as PathCollection facecolors?
4133+
colors=mcolors.to_rgba_array(c)
4134+
n_elem=colors.shape[0]
4135+
ifcolors.shape[0]notin (0,1,xsize,ysize):
4136+
# NB: remember that a single color is also acceptable.
4137+
# Besides *colors* will be an empty array if c == 'none'.
4138+
valid_shape=False
4139+
raiseValueError
4140+
exceptValueError:
4141+
ifnotvalid_shape:# but at least one conversion succeeded.
4142+
raiseValueError(
4143+
"'c' argument has {nc} elements, which is not "
4144+
"acceptable for use with 'x' with size {xs}, "
4145+
"'y' with size {ys}."
4146+
.format(nc=n_elem,xs=xsize,ys=ysize)
4147+
)
4148+
# Both the mapping *and* the RGBA conversion failed: pretty
4149+
# severe failure => one may appreciate a verbose feedback.
4150+
raiseValueError(
4151+
"'c' argument must either be valid as mpl color(s) "
4152+
"or as numbers to be mapped to colors. "
4153+
"Here c = {}."# <- beware, could be long depending on c.
4154+
.format(c)
4155+
)
4156+
else:
4157+
colors=None# use cmap, norm after collection is created
4158+
returnc,colors,edgecolors
4159+
40154160
@_preprocess_data(replace_names=["x","y","s","linewidths",
40164161
"edgecolors","c","facecolor",
40174162
"facecolors","color"],
@@ -4125,129 +4270,27 @@ def scatter(self, x, y, s=None, c=None, marker=None, cmap=None, norm=None,
41254270
41264271
"""
41274272
# Process **kwargs to handle aliases, conflicts with explicit kwargs:
4128-
facecolors=None
4129-
edgecolors=kwargs.pop('edgecolor',edgecolors)
4130-
fc=kwargs.pop('facecolors',None)
4131-
fc=kwargs.pop('facecolor',fc)
4132-
iffcisnotNone:
4133-
facecolors=fc
4134-
co=kwargs.pop('color',None)
4135-
ifcoisnotNone:
4136-
try:
4137-
mcolors.to_rgba_array(co)
4138-
exceptValueError:
4139-
raiseValueError("'color' kwarg must be an mpl color"
4140-
" spec or sequence of color specs.\n"
4141-
"For a sequence of values to be color-mapped,"
4142-
" use the 'c' argument instead.")
4143-
ifedgecolorsisNone:
4144-
edgecolors=co
4145-
iffacecolorsisNone:
4146-
facecolors=co
4147-
ifcisnotNone:
4148-
raiseValueError("Supply a 'c' argument or a 'color'"
4149-
" kwarg but not both; they differ but"
4150-
" their functionalities overlap.")
4151-
ifcisNone:
4152-
iffacecolorsisnotNone:
4153-
c=facecolors
4154-
else:
4155-
ifrcParams['_internal.classic_mode']:
4156-
c='b'# The original default
4157-
else:
4158-
c=self._get_patches_for_fill.get_next_color()
4159-
c_none=True
4160-
else:
4161-
c_none=False
4162-
4163-
ifedgecolorsisNoneandnotrcParams['_internal.classic_mode']:
4164-
edgecolors='face'
41654273

41664274
self._process_unit_info(xdata=x,ydata=y,kwargs=kwargs)
41674275
x=self.convert_xunits(x)
41684276
y=self.convert_yunits(y)
41694277

41704278
# np.ma.ravel yields an ndarray, not a masked array,
41714279
# unless its argument is a masked array.
4172-
xy_shape= (np.shape(x),np.shape(y))
4280+
xshape,yshape=np.shape(x),np.shape(y)
41734281
x=np.ma.ravel(x)
41744282
y=np.ma.ravel(y)
41754283
ifx.size!=y.size:
41764284
raiseValueError("x and y must be the same size")
41774285

41784286
ifsisNone:
4179-
ifrcParams['_internal.classic_mode']:
4180-
s=20
4181-
else:
4182-
s=rcParams['lines.markersize']**2.0
4183-
4287+
s= (20ifrcParams['_internal.classic_mode']else
4288+
rcParams['lines.markersize']**2.0)
41844289
s=np.ma.ravel(s)# This doesn't have to match x, y in size.
41854290

4186-
# After this block, c_array will be None unless
4187-
# c is an array for mapping. The potential ambiguity
4188-
# with a sequence of 3 or 4 numbers is resolved in
4189-
# favor of mapping, not rgb or rgba.
4190-
4191-
# Convenience vars to track shape mismatch *and* conversion failures.
4192-
valid_shape=True# will be put to the test!
4193-
n_elem=-1# used only for (some) exceptions
4194-
4195-
if (c_noneor
4196-
coisnotNoneor
4197-
isinstance(c,str)or
4198-
(isinstance(c,collections.Iterable)and
4199-
len(c)>0and
4200-
isinstance(cbook.safe_first_element(c),str))):
4201-
c_array=None
4202-
else:
4203-
try:# First, does 'c' look suitable for value-mapping?
4204-
c_array=np.asanyarray(c,dtype=float)
4205-
n_elem=c_array.shape[0]
4206-
ifc_array.shapeinxy_shape:
4207-
c=np.ma.ravel(c_array)
4208-
else:
4209-
ifc_array.shapein ((3,), (4,)):
4210-
_log.warning(
4211-
"'c' argument looks like a single numeric RGB or "
4212-
"RGBA sequence, which should be avoided as value-"
4213-
"mapping will have precedence in case its length "
4214-
"matches with 'x' & 'y'. Please use a 2-D array "
4215-
"with a single row if you really want to specify "
4216-
"the same RGB or RGBA value for all points.")
4217-
# Wrong size; it must not be intended for mapping.
4218-
valid_shape=False
4219-
c_array=None
4220-
exceptValueError:
4221-
# Failed to make a floating-point array; c must be color specs.
4222-
c_array=None
4223-
4224-
ifc_arrayisNone:
4225-
try:# Then is 'c' acceptable as PathCollection facecolors?
4226-
colors=mcolors.to_rgba_array(c)
4227-
n_elem=colors.shape[0]
4228-
ifcolors.shape[0]notin (0,1,x.size,y.size):
4229-
# NB: remember that a single color is also acceptable.
4230-
# Besides *colors* will be an empty array if c == 'none'.
4231-
valid_shape=False
4232-
raiseValueError
4233-
exceptValueError:
4234-
ifnotvalid_shape:# but at least one conversion succeeded.
4235-
raiseValueError(
4236-
"'c' argument has {nc} elements, which is not "
4237-
"acceptable for use with 'x' with size {xs}, "
4238-
"'y' with size {ys}."
4239-
.format(nc=n_elem,xs=x.size,ys=y.size)
4240-
)
4241-
# Both the mapping *and* the RGBA conversion failed: pretty
4242-
# severe failure => one may appreciate a verbose feedback.
4243-
raiseValueError(
4244-
"'c' argument must either be valid as mpl color(s) "
4245-
"or as numbers to be mapped to colors. "
4246-
"Here c = {}."# <- beware, could be long depending on c.
4247-
.format(c)
4248-
)
4249-
else:
4250-
colors=None# use cmap, norm after collection is created
4291+
c,colors,edgecolors= \
4292+
self._parse_scatter_color_args(c,edgecolors,kwargs,
4293+
xshape,yshape)
42514294

42524295
# `delete_masked_points` only modifies arguments of the same length as
42534296
# `x`.

‎lib/matplotlib/tests/test_axes.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
fromcollectionsimportnamedtuple
12
fromitertoolsimportproduct
23
fromdistutils.versionimportLooseVersion
34
importio
@@ -1807,6 +1808,65 @@ def test_scatter_c(self, c_case, re_key):
18071808
ax.scatter(x,y,c=c_case,edgecolors="black")
18081809

18091810

1811+
def_params(c=None,xshape=(2,),yshape=(2,),**kwargs):
1812+
edgecolors=kwargs.pop('edgecolors',None)
1813+
return (c,edgecolors,kwargsifkwargsisnotNoneelse {},
1814+
xshape,yshape)
1815+
_result=namedtuple('_result','c, colors')
1816+
1817+
1818+
@pytest.mark.parametrize('params, expected_result',
1819+
[(_params(),
1820+
_result(c='b',colors=np.array([[0,0,1,1]]))),
1821+
(_params(c='r'),
1822+
_result(c='r',colors=np.array([[1,0,0,1]]))),
1823+
(_params(c='r',colors='b'),
1824+
_result(c='r',colors=np.array([[1,0,0,1]]))),
1825+
# color
1826+
(_params(color='b'),
1827+
_result(c='b',colors=np.array([[0,0,1,1]]))),
1828+
(_params(color=['b','g']),
1829+
_result(c=['b','g'],colors=np.array([[0,0,1,1], [0,.5,0,1]]))),
1830+
])
1831+
deftest_parse_scatter_color_args(params,expected_result):
1832+
frommatplotlib.axesimportAxes
1833+
dummyself='UNUSED'# self is only used in one case, which we do not
1834+
# test. Therefore we can get away without costly
1835+
# creating an Axes instance.
1836+
c,colors,_edgecolors=Axes._parse_scatter_color_args(dummyself,*params)
1837+
assertc==expected_result.c
1838+
assert_allclose(colors,expected_result.colors)
1839+
1840+
del_params
1841+
del_result
1842+
1843+
1844+
@pytest.mark.parametrize('kwargs, expected_edgecolors',
1845+
[(dict(),None),
1846+
(dict(c='b'),None),
1847+
(dict(edgecolors='r'),'r'),
1848+
(dict(edgecolors=['r','g']), ['r','g']),
1849+
(dict(edgecolor='r'),'r'),
1850+
(dict(edgecolors='face'),'face'),
1851+
(dict(edgecolors='none'),'none'),
1852+
(dict(edgecolor='r',edgecolors='g'),'r'),
1853+
(dict(c='b',edgecolor='r',edgecolors='g'),'r'),
1854+
(dict(color='r'),'r'),
1855+
(dict(color='r',edgecolor='g'),'g'),
1856+
])
1857+
deftest_parse_scatter_color_args_edgecolors(kwargs,expected_edgecolors):
1858+
frommatplotlib.axesimportAxes
1859+
dummyself='UNUSED'# self is only used in one case, which we do not
1860+
# test. Therefore we can get away without costly
1861+
# creating an Axes instance.
1862+
c=kwargs.pop('c',None)
1863+
edgecolors=kwargs.pop('edgecolors',None)
1864+
_,_,result_edgecolors= \
1865+
Axes._parse_scatter_color_args(dummyself,c,edgecolors,kwargs,
1866+
xshape=(2,),yshape=(2,))
1867+
assertresult_edgecolors==expected_edgecolors
1868+
1869+
18101870
deftest_as_mpl_axes_api():
18111871
# tests the _as_mpl_axes api
18121872
frommatplotlib.projections.polarimportPolarAxes

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp