|
2 | 2 | import itertools
|
3 | 3 | import logging
|
4 | 4 | import math
|
| 5 | +import operator |
5 | 6 | from numbers import Number
|
6 | 7 | import warnings
|
7 | 8 |
|
@@ -3760,6 +3761,146 @@ def dopatch(xs, ys, **kwargs):
|
3760 | 3761 | return dict(whiskers=whiskers, caps=caps, boxes=boxes,
|
3761 | 3762 | medians=medians, fliers=fliers, means=means)
|
3762 | 3763 |
|
| 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 | + if kwcolor is not None and c is not None: |
| 3817 | + raise ValueError("Supply a 'c' argument or a 'color'" |
| 3818 | + " kwarg but not both; they differ but" |
| 3819 | + " their functionalities overlap.") |
| 3820 | + |
| 3821 | + if kwcolor is not None: |
| 3822 | + try: |
| 3823 | + mcolors.to_rgba_array(kwcolor) |
| 3824 | + except ValueError: |
| 3825 | + raise ValueError("'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 | + if edgecolors is None: |
| 3830 | + edgecolors = kwcolor |
| 3831 | + if facecolors is None: |
| 3832 | + facecolors = kwcolor |
| 3833 | + |
| 3834 | + if edgecolors is None and not rcParams['_internal.classic_mode']: |
| 3835 | + edgecolors = 'face' |
| 3836 | + |
| 3837 | + c_is_none = c is None |
| 3838 | + if c is None: |
| 3839 | + if facecolors is not None: |
| 3840 | + c = facecolors |
| 3841 | + else: |
| 3842 | + c = ('b' if rcParams['_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 | + if c_is_none or kwcolor is not None: |
| 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 | + if c_array.shape in [xshape, yshape]: |
| 3859 | + c = np.ma.ravel(c_array) |
| 3860 | + else: |
| 3861 | + if c_array.shape in ((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 | + except ValueError: |
| 3873 | + # Failed to make a floating-point array; c must be color specs. |
| 3874 | + c_array = None |
| 3875 | + if c_array is None: |
| 3876 | + try: # Then is 'c' acceptable as PathCollection facecolors? |
| 3877 | + colors = mcolors.to_rgba_array(c) |
| 3878 | + n_elem = colors.shape[0] |
| 3879 | + if colors.shape[0] not in (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 | + raise ValueError |
| 3884 | + except ValueError: |
| 3885 | + if not valid_shape: # but at least one conversion succeeded. |
| 3886 | + raise ValueError( |
| 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 | + raise ValueError( |
| 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 | + return c, colors, edgecolors |
| 3903 | + |
3763 | 3904 | @_preprocess_data(replace_names=["x", "y", "s", "linewidths",
|
3764 | 3905 | "edgecolors", "c", "facecolor",
|
3765 | 3906 | "facecolors", "color"],
|
@@ -3865,124 +4006,27 @@ def scatter(self, x, y, s=None, c=None, marker=None, cmap=None, norm=None,
|
3865 | 4006 |
|
3866 | 4007 | """
|
3867 | 4008 | # 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 |
| - if fc is not None: |
3873 |
| - facecolors = fc |
3874 |
| - co = kwargs.pop('color', None) |
3875 |
| - if co is not None: |
3876 |
| - try: |
3877 |
| - mcolors.to_rgba_array(co) |
3878 |
| - except ValueError: |
3879 |
| - raise ValueError("'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 |
| - if edgecolors is None: |
3884 |
| - edgecolors = co |
3885 |
| - if facecolors is None: |
3886 |
| - facecolors = co |
3887 |
| - if c is not None: |
3888 |
| - raise ValueError("Supply a 'c' argument or a 'color'" |
3889 |
| - " kwarg but not both; they differ but" |
3890 |
| - " their functionalities overlap.") |
3891 |
| - if c is None: |
3892 |
| - if facecolors is not None: |
3893 |
| - c = facecolors |
3894 |
| - else: |
3895 |
| - if rcParams['_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 |
| - if edgecolors is None and not rcParams['_internal.classic_mode']: |
3904 |
| - edgecolors = 'face' |
3905 | 4009 |
|
3906 | 4010 | self._process_unit_info(xdata=x, ydata=y, kwargs=kwargs)
|
3907 | 4011 | x = self.convert_xunits(x)
|
3908 | 4012 | y = self.convert_yunits(y)
|
3909 | 4013 |
|
3910 | 4014 | # np.ma.ravel yields an ndarray, not a masked array,
|
3911 | 4015 | # 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) |
3913 | 4017 | x = np.ma.ravel(x)
|
3914 | 4018 | y = np.ma.ravel(y)
|
3915 | 4019 | if x.size != y.size:
|
3916 | 4020 | raise ValueError("x and y must be the same size")
|
3917 | 4021 |
|
3918 | 4022 | if s is None:
|
3919 |
| - if rcParams['_internal.classic_mode']: |
3920 |
| - s = 20 |
3921 |
| - else: |
3922 |
| - s = rcParams['lines.markersize'] ** 2.0 |
3923 |
| - |
| 4023 | + s = (20 if rcParams['_internal.classic_mode'] else |
| 4024 | + rcParams['lines.markersize'] ** 2.0) |
3924 | 4025 | s = np.ma.ravel(s) # This doesn't have to match x, y in size.
|
3925 | 4026 |
|
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 |
| - if c_none or co is not None: |
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 |
| - if c_array.shape in xy_shape: |
3942 |
| - c = np.ma.ravel(c_array) |
3943 |
| - else: |
3944 |
| - if c_array.shape in ((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 |
| - except ValueError: |
3956 |
| - # Failed to make a floating-point array; c must be color specs. |
3957 |
| - c_array = None |
3958 |
| - |
3959 |
| - if c_array is None: |
3960 |
| - try: # Then is 'c' acceptable as PathCollection facecolors? |
3961 |
| - colors = mcolors.to_rgba_array(c) |
3962 |
| - n_elem = colors.shape[0] |
3963 |
| - if colors.shape[0] not in (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 |
| - raise ValueError |
3968 |
| - except ValueError: |
3969 |
| - if not valid_shape: # but at least one conversion succeeded. |
3970 |
| - raise ValueError( |
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 |
| - raise ValueError( |
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) |
3986 | 4030 |
|
3987 | 4031 | # `delete_masked_points` only modifies arguments of the same length as
|
3988 | 4032 | # `x`.
|
|
0 commit comments