Get matplotlib color cycle state

后端 未结 9 1559
慢半拍i
慢半拍i 2020-11-27 10:36

Is it possible to query the current state of the matplotlib color cycle? In other words is there a function get_cycle_state that will behave in the following wa

相关标签:
9条回答
  • 2020-11-27 11:23

    Accessing the color cycle iterator

    There's no "user-facing" (a.k.a. "public") method to access the underlying iterator, but you can access it through "private" (by convention) methods. However, you'd can't get the state of an iterator without changing it.

    Setting the color cycle

    Quick aside: You can set the color/property cycle in a variety of ways (e.g. ax.set_color_cycle in versions <1.5 or ax.set_prop_cycler in >=1.5). Have a look at the example here for version 1.5 or greater, or the previous style here.

    Accessing the underlying iterator

    However, while there's no public-facing method to access the iterable, you can access it for a given axes object (ax) through the _get_lines helper class instance. ax._get_lines is a touch confusingly named, but it's the behind-the-scenes machinery that allows the plot command to process all of the odd and varied ways that plot can be called. Among other things, it's what keeps track of what colors to automatically assign. Similarly, there's ax._get_patches_for_fill to control cycling through default fill colors and patch properties.

    At any rate, the color cycle iterable is ax._get_lines.color_cycle for lines and ax._get_patches_for_fill.color_cycle for patches. On matplotlib >=1.5, this has changed to use the cycler library, and the iterable is called prop_cycler instead of color_cycle and yields a dict of properties instead of only a color.

    All in all, you'd do something like:

    import matplotlib.pyplot as plt
    
    fig, ax = plt.subplots()
    color_cycle = ax._get_lines.color_cycle
    # or ax._get_lines.prop_cycler on version >= 1.5
    # Note that prop_cycler cycles over dicts, so you'll want next(cycle)['color']
    

    You can't view the state of an iterator

    However, this object is a "bare" iterator. We can easily get the next item (e.g. next_color = next(color_cycle), but that means that the next color after that is what will be plotted. By design, there's no way to get the current state of an iterator without changing it.

    In v1.5 or greater, it would be nice to get the cycler object that's used, as we could infer its current state. However, the cycler object itself isn't accessible (publicly or privately) anywhere. Instead, only the itertools.cycle instance created from the cycler object is accessible. Either way, there's no way to get to the underlying state of the color/property cycler.

    Match the color of the previously plotted item instead

    In your case, it sounds like you're wanting to match the color of something that was just plotted. Instead of trying to determine what the color/property will be, set the color/etc of your new item based on the properties of what's plotted.

    For example, in the case you described, I'd do something like this:

    import matplotlib.pyplot as plt
    import numpy as np
    
    def custom_plot(x, y, **kwargs):
        ax = kwargs.pop('ax', plt.gca())
        base_line, = ax.plot(x, y, **kwargs)
        ax.fill_between(x, 0.9*y, 1.1*y, facecolor=base_line.get_color(), alpha=0.5)
    
    x = np.linspace(0, 1, 10)
    custom_plot(x, x)
    custom_plot(x, 2*x)
    custom_plot(x, -x, color='yellow', lw=3)
    
    plt.show()
    

    enter image description here

    It's not the only way, but its cleaner than trying to get the color of the plotted line before-hand, in this case.

    0 讨论(0)
  • 2020-11-27 11:25

    Here's a way that works in 1.5 which will hopefully be future-proof as it doesn't rely on methods prepended with underscores:

    colors = plt.rcParams["axes.prop_cycle"].by_key()["color"]
    

    This will give you a list of the colors defined in order for the present style.

    0 讨论(0)
  • 2020-11-27 11:25

    Since matplotlib uses itertools.cycle we can actually look through the entire color cycle and then restore the iterator to its previous state:

    def list_from_cycle(cycle):
        first = next(cycle)
        result = [first]
        for current in cycle:
            if current == first:
                break
            result.append(current)
    
        # Reset iterator state:
        for current in cycle:
            if current == result[-1]:
                break
        return result
    

    This should return the list without changing the state of the iterator.

    Use it with matplotlib >= 1.5:

    >>> list_from_cycle(ax._get_lines.prop_cycler)
    [{'color': 'r'}, {'color': 'g'}, {'color': 'b'}]
    

    or with matplotlib < 1.5:

    >>> list_from_cycle(ax._get_lines.color_cycle)
    ['r', 'g', 'b']
    
    0 讨论(0)
提交回复
热议问题