|
3 | 3 | import itertools
|
4 | 4 | import logging
|
5 | 5 | import math
|
| 6 | +import operator |
6 | 7 | from numbers import Number
|
7 | 8 | import warnings
|
8 | 9 |
|
@@ -4012,6 +4013,150 @@ def dopatch(xs, ys, **kwargs):
|
4012 | 4013 | return dict(whiskers=whiskers, caps=caps, boxes=boxes,
|
4013 | 4014 | medians=medians, fliers=fliers, means=means)
|
4014 | 4015 |
|
| 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 | + if kwcolor is not None and c is not None: |
| 4069 | + raise ValueError("Supply a 'c' argument or a 'color'" |
| 4070 | + " kwarg but not both; they differ but" |
| 4071 | + " their functionalities overlap.") |
| 4072 | + |
| 4073 | + if kwcolor is not None: |
| 4074 | + try: |
| 4075 | + mcolors.to_rgba_array(kwcolor) |
| 4076 | + except ValueError: |
| 4077 | + raise ValueError("'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 | + if edgecolors is None: |
| 4082 | + edgecolors = kwcolor |
| 4083 | + if facecolors is None: |
| 4084 | + facecolors = kwcolor |
| 4085 | + |
| 4086 | + if edgecolors is None and not rcParams['_internal.classic_mode']: |
| 4087 | + edgecolors = 'face' |
| 4088 | + |
| 4089 | + c_was_none = c is None |
| 4090 | + if c is None: |
| 4091 | + c = (facecolors if facecolors is not None |
| 4092 | + else "b" if rcParams['_internal.classic_mode'] |
| 4093 | + else self._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_none or |
| 4104 | + kwcolor is not None or |
| 4105 | + isinstance(c, str) or |
| 4106 | + (isinstance(c, collections.abc.Iterable) and |
| 4107 | + len(c) > 0 and |
| 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 | + if c_array.shape in [xshape, yshape]: |
| 4115 | + c = np.ma.ravel(c_array) |
| 4116 | + else: |
| 4117 | + if c_array.shape in ((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 | + except ValueError: |
| 4129 | + # Failed to make a floating-point array; c must be color specs. |
| 4130 | + c_array = None |
| 4131 | + if c_array is None: |
| 4132 | + try: # Then is 'c' acceptable as PathCollection facecolors? |
| 4133 | + colors = mcolors.to_rgba_array(c) |
| 4134 | + n_elem = colors.shape[0] |
| 4135 | + if colors.shape[0] not in (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 | + raise ValueError |
| 4140 | + except ValueError: |
| 4141 | + if not valid_shape: # but at least one conversion succeeded. |
| 4142 | + raise ValueError( |
| 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 | + raise ValueError( |
| 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 | + return c, colors, edgecolors |
| 4159 | + |
4015 | 4160 | @_preprocess_data(replace_names=["x", "y", "s", "linewidths",
|
4016 | 4161 | "edgecolors", "c", "facecolor",
|
4017 | 4162 | "facecolors", "color"],
|
@@ -4125,129 +4270,27 @@ def scatter(self, x, y, s=None, c=None, marker=None, cmap=None, norm=None,
|
4125 | 4270 |
|
4126 | 4271 | """
|
4127 | 4272 | # 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 |
| - if fc is not None: |
4133 |
| - facecolors = fc |
4134 |
| - co = kwargs.pop('color', None) |
4135 |
| - if co is not None: |
4136 |
| - try: |
4137 |
| - mcolors.to_rgba_array(co) |
4138 |
| - except ValueError: |
4139 |
| - raise ValueError("'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 |
| - if edgecolors is None: |
4144 |
| - edgecolors = co |
4145 |
| - if facecolors is None: |
4146 |
| - facecolors = co |
4147 |
| - if c is not None: |
4148 |
| - raise ValueError("Supply a 'c' argument or a 'color'" |
4149 |
| - " kwarg but not both; they differ but" |
4150 |
| - " their functionalities overlap.") |
4151 |
| - if c is None: |
4152 |
| - if facecolors is not None: |
4153 |
| - c = facecolors |
4154 |
| - else: |
4155 |
| - if rcParams['_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 |
| - if edgecolors is None and not rcParams['_internal.classic_mode']: |
4164 |
| - edgecolors = 'face' |
4165 | 4273 |
|
4166 | 4274 | self._process_unit_info(xdata=x, ydata=y, kwargs=kwargs)
|
4167 | 4275 | x = self.convert_xunits(x)
|
4168 | 4276 | y = self.convert_yunits(y)
|
4169 | 4277 |
|
4170 | 4278 | # np.ma.ravel yields an ndarray, not a masked array,
|
4171 | 4279 | # 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) |
4173 | 4281 | x = np.ma.ravel(x)
|
4174 | 4282 | y = np.ma.ravel(y)
|
4175 | 4283 | if x.size != y.size:
|
4176 | 4284 | raise ValueError("x and y must be the same size")
|
4177 | 4285 |
|
4178 | 4286 | if s is None:
|
4179 |
| - if rcParams['_internal.classic_mode']: |
4180 |
| - s = 20 |
4181 |
| - else: |
4182 |
| - s = rcParams['lines.markersize'] ** 2.0 |
4183 |
| - |
| 4287 | + s = (20 if rcParams['_internal.classic_mode'] else |
| 4288 | + rcParams['lines.markersize'] ** 2.0) |
4184 | 4289 | s = np.ma.ravel(s) # This doesn't have to match x, y in size.
|
4185 | 4290 |
|
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_none or |
4196 |
| - co is not None or |
4197 |
| - isinstance(c, str) or |
4198 |
| - (isinstance(c, collections.Iterable) and |
4199 |
| - len(c) > 0 and |
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 |
| - if c_array.shape in xy_shape: |
4207 |
| - c = np.ma.ravel(c_array) |
4208 |
| - else: |
4209 |
| - if c_array.shape in ((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 |
| - except ValueError: |
4221 |
| - # Failed to make a floating-point array; c must be color specs. |
4222 |
| - c_array = None |
4223 |
| - |
4224 |
| - if c_array is None: |
4225 |
| - try: # Then is 'c' acceptable as PathCollection facecolors? |
4226 |
| - colors = mcolors.to_rgba_array(c) |
4227 |
| - n_elem = colors.shape[0] |
4228 |
| - if colors.shape[0] not in (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 |
| - raise ValueError |
4233 |
| - except ValueError: |
4234 |
| - if not valid_shape: # but at least one conversion succeeded. |
4235 |
| - raise ValueError( |
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 |
| - raise ValueError( |
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) |
4251 | 4294 |
|
4252 | 4295 | # `delete_masked_points` only modifies arguments of the same length as
|
4253 | 4296 | # `x`.
|
|
0 commit comments