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

Commitb4bc63b

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

File tree

2 files changed

+205
-103
lines changed

2 files changed

+205
-103
lines changed

‎lib/matplotlib/axes/_axes.py

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

39064010
self._process_unit_info(xdata=x,ydata=y,kwargs=kwargs)
39074011
x=self.convert_xunits(x)
39084012
y=self.convert_yunits(y)
39094013

39104014
# np.ma.ravel yields an ndarray, not a masked array,
39114015
# unless its argument is a masked array.
3912-
xy_shape= (np.shape(x),np.shape(y))
4016+
xshape,yshape=np.shape(x),np.shape(y)
39134017
x=np.ma.ravel(x)
39144018
y=np.ma.ravel(y)
39154019
ifx.size!=y.size:
39164020
raiseValueError("x and y must be the same size")
39174021

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

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
4027+
c,colors,edgecolors= \
4028+
self._parse_scatter_color_args(c,edgecolors,kwargs,
4029+
xshape,yshape)
39864030

39874031
# `delete_masked_points` only modifies arguments of the same length as
39884032
# `x`.

‎lib/matplotlib/tests/test_axes.py

Lines changed: 58 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,63 @@ 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(edgecolors=['r','g']), ['r','g']),
1832+
(dict(edgecolor='r'),'r'),
1833+
(dict(edgecolors='face'),'face'),
1834+
(dict(edgecolors='none'),'none'),
1835+
(dict(edgecolor='r',edgecolors='g'),'r'),
1836+
(dict(c='b',edgecolor='r',edgecolors='g'),'r'),
1837+
(dict(color='r'),'r'),
1838+
(dict(color='r',edgecolor='g'),'g'),
1839+
])
1840+
deftest_parse_scatter_color_args_edgecolors(kwargs,expected_edgecolors):
1841+
frommatplotlib.axesimportAxes
1842+
dummyself='UNUSED'# self is only used in one case, which we do not
1843+
# test. Therefore we can get away without costly
1844+
# creating an Axes instance.
1845+
c=kwargs.pop('c',None)
1846+
edgecolors=kwargs.pop('edgecolors',None)
1847+
_,_,result_edgecolors= \
1848+
Axes._parse_scatter_color_args(dummyself,c,edgecolors,kwargs,
1849+
xshape=(2,),yshape=(2,))
1850+
assertresult_edgecolors==expected_edgecolors
1851+
1852+
17951853
deftest_as_mpl_axes_api():
17961854
# tests the _as_mpl_axes api
17971855
frommatplotlib.projections.polarimportPolarAxes

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp