Câu hỏi Nhận trạng thái chu trình màu matplotlib


Có thể truy vấn trạng thái hiện tại của chu trình màu matplotlib không? Nói cách khác là có một hàm get_cycle_state sẽ hành xử theo cách sau đây?

>>> plot(x1, y1)
>>> plot(x2, y2)
>>> state = get_cycle_state()
>>> print state
2

Nơi tôi mong đợi nhà nước là chỉ số của màu tiếp theo sẽ được sử dụng trong một âm mưu. Ngoài ra, nếu nó trả lại màu tiếp theo ("r" cho chu trình mặc định trong ví dụ trên), điều đó cũng sẽ ổn.


76
2017-12-12 01:50


gốc




Các câu trả lời:


Truy cập trình vòng lặp màu chu trình

Không có phương thức "người dùng đối mặt" (a.k.a. "public") để truy cập trình lặp cơ bản, nhưng bạn có thể truy cập nó thông qua các phương thức "riêng" (theo quy ước). Tuy nhiên, bạn không thể có được trạng thái của một iterator mà không thay đổi nó.

Đặt chu kỳ màu

Nhanh sang một bên: Bạn có thể đặt chu trình màu / thuộc tính theo nhiều cách khác nhau (ví dụ: ax.set_color_cycle trong các phiên bản <1.5 hoặc ax.set_prop_cycler trong> = 1,5). Hãy nhìn vào ví dụ ở đây cho phiên bản 1.5 trở lên, hoặc là phong cách trước đây ở đây.

Truy cập trình lặp cơ bản

Tuy nhiên, trong khi không có phương thức công khai nào để truy cập vào iterable, bạn có thể truy cập nó cho một đối tượng trục đã cho (ax) thông qua _get_lines dụ lớp helper. ax._get_lines là một liên lạc gây nhầm lẫn có tên, nhưng đó là máy móc hậu trường cho phép plot lệnh để xử lý tất cả các cách lẻ và đa dạng plot có thể được gọi. Trong số những thứ khác, đó là những gì theo dõi những gì màu sắc để tự động gán. Tương tự, có ax._get_patches_for_fill để kiểm soát đi xe đạp thông qua các màu tô và các thuộc tính vá mặc định.

Ở bất kỳ tỷ lệ nào, chu trình màu có thể lặp lại là ax._get_lines.color_cycle cho các dòng và ax._get_patches_for_fill.color_cycle cho các bản vá lỗi. Trên matplotlib> = 1.5, điều này đã thay đổi thành sử dụng cycler thư việnvà lặp lại được gọi là prop_cycler thay vì color_cycle và mang lại dict các thuộc tính thay vì chỉ một màu.

Tất cả trong tất cả, bạn sẽ làm một cái gì đó như:

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']

Bạn không thể xem trạng thái của một iterator

Tuy nhiên, đối tượng này là "trần" iterator. Chúng tôi có thể dễ dàng có được mục tiếp theo (ví dụ: next_color = next(color_cycle), nhưng điều đó có nghĩa là màu tiếp theo sau đó là những gì sẽ được vẽ. Theo thiết kế, không có cách nào để có được trạng thái hiện tại của một trình lặp mà không thay đổi nó.

Trong v1.5 hoặc lớn hơn, nó sẽ là tốt đẹp để có được cycler đối tượng được sử dụng, vì chúng ta có thể phỏng đoán trạng thái hiện tại của nó. Tuy nhiên, cycler bản thân đối tượng không thể truy cập được (công khai hoặc riêng tư) ở bất cứ đâu. Thay vào đó, chỉ có itertools.cycle ví dụ được tạo từ cycler đối tượng có thể truy cập được. Dù bằng cách nào, không có cách nào để đến trạng thái cơ bản của bộ tạo màu / thuộc tính.

Phù hợp với màu của mục được vẽ trước đó thay vào đó

Trong trường hợp của bạn, nó có vẻ như bạn đang muốn phù hợp với màu sắc của một cái gì đó chỉ là âm mưu. Thay vì cố gắng xác định màu / thuộc tính sẽ là gì, hãy đặt màu / etc của mục mới của bạn dựa trên các thuộc tính của nội dung được vẽ.

Ví dụ, trong trường hợp bạn mô tả, tôi sẽ làm một cái gì đó như thế này:

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

Nó không phải là cách duy nhất, nhưng nó sạch hơn là cố gắng để có được màu sắc của đường vẽ trước khi bàn tay, trong trường hợp này.


86
2017-12-12 02:24



Tôi đang viết một số chức năng cấp cao hơn để tự động hóa các tác vụ âm mưu phổ biến cho miền công việc của tôi. Thường thì những entail vẽ nhiều đối tượng matplotlib, và tôi muốn có một số đối tượng chia sẻ một màu trong chu trình màu, vì vậy tôi không phải luôn luôn cung cấp một đối số màu nếu tôi cảm thấy lười biếng. - mwaskom
Nếu bạn chỉ muốn phù hợp với màu sắc của một đối tượng cụ thể, bạn luôn có thể lấy màu của nó. ví dụ. line, = ax.plot(x, y) và sau đó sử dụng line.get_color() để có được màu sắc của dòng được vẽ trước đó. - Joe Kington
Dường như ax._get_lines.color_cycle không còn tồn tại trong 1,5? - endolith
Tôi nghĩ rằng (gần) tương đương là một iterable ax._get_lines.prop_cyclerbạn có thể xây dựng một cái gì đó như if 'color' in ax._get_lines._prop_keys: theo dõi bởi next(ax._get_lines.prop_cycler)['color'] để làm tương đương với những gì được đề xuất với color_cycle trong câu trả lời, tôi tin. Tôi không chắc liệu bạn có thể nhận được một lần lặp lại chỉ màu sắc hay không, tôi cần khám phá thêm. - J Richard Snape
@endolith Chắc chắn (và cảm ơn) - nhưng tôi chỉ cảm thấy nó sẽ là tốt hơn ngồi trong câu trả lời của Joe đã rất tốt và được chấp nhận. Nếu anh ta không nhận được vòng để đưa nó vào thứ hai - Tôi sẽ dính một câu trả lời thích hợp lên đó thay vì sợ hãi "trả lời trong ý kiến" - J Richard Snape


Lưu ý: Trong các phiên bản mới nhất của matplotlib (> = 1.5) _get_lines đã thay đổi. Bây giờ bạn cần phải sử dụng next(ax._get_lines.prop_cycler)['color'] trong Python 2 hoặc 3 (hoặc ax._get_lines.prop_cycler.next()['color'] trong Python 2) để có được màu tiếp theo từ chu trình màu.

Bất cứ nơi nào có thể sử dụng cách tiếp cận trực tiếp hơn được thể hiện ở phần dưới của câu trả lời của @ joe-kington. Như _get_lines không phải là đối mặt với API, nó có thể thay đổi một lần nữa theo cách không tương thích ngược trong tương lai.


13
2017-11-09 11:56



Làm thế nào để bạn tránh được việc truy vấn phần tử tiếp theo của bộ tạo màu thay đổi trạng thái của cycler? - kazemakase


Đây là một cách làm việc trong 1.5 mà hy vọng sẽ được chứng minh trong tương lai vì nó không dựa vào các phương thức được thêm vào với dấu gạch dưới:

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

Điều này sẽ cung cấp cho bạn một danh sách các màu được xác định theo thứ tự cho phong cách hiện tại.


9
2017-07-23 06:47



Hoạt động trong 2.0.2. - KevinG


Chắc chắn, điều này sẽ làm điều đó.

#rainbow

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0,2*np.pi)
ax= plt.subplot(1,1,1)
ax.plot(np.sin(x))
ax.plot(np.cos(x))

rainbow = ax._get_lines.color_cycle
print rainbow
for i, color in enumerate(rainbow):
    if i<10:
        print color,

Cung cấp:

<itertools.cycle object at 0x034CB288>
r c m y k b g r c m

Đây là hàm itertools mà matplotlib sử dụng itertools.cycle

Chỉnh sửa: Cảm ơn nhận xét, có vẻ như không thể sao chép một trình lặp. Một ý tưởng sẽ là đổ một chu kỳ đầy đủ và theo dõi giá trị bạn đang sử dụng, hãy để tôi lấy lại điều đó.

Edit2: Được rồi, điều này sẽ cung cấp cho bạn màu tiếp theo và tạo một trình lặp mới hoạt động như thể tiếp theo không được gọi. Điều này không bảo vệ thứ tự của màu, chỉ là giá trị màu sắc tiếp theo, tôi để lại điều đó cho bạn.

Điều này cho kết quả sau, lưu ý rằng độ dốc trong ô tương ứng với chỉ mục, ví dụ g đầu tiên là biểu đồ bottomest và vân vân.

#rainbow

import matplotlib.pyplot as plt
import numpy as np
import collections
import itertools

x = np.linspace(0,2*np.pi)
ax= plt.subplot(1,1,1)


def create_rainbow():
    rainbow = [ax._get_lines.color_cycle.next()]
    while True:
        nextval = ax._get_lines.color_cycle.next()
        if nextval not in rainbow:
            rainbow.append(nextval)
        else:
            return rainbow

def next_color(axis_handle=ax):
    rainbow = create_rainbow()
    double_rainbow = collections.deque(rainbow)
    nextval = ax._get_lines.color_cycle.next()
    double_rainbow.rotate(-1)
    return nextval, itertools.cycle(double_rainbow)


for i in range(1,10):
    nextval, ax._get_lines.color_cycle = next_color(ax)
    print "Next color is: ", nextval
    ax.plot(i*(x))


plt.savefig("SO_rotate_color.png")
plt.show()

Bảng điều khiển

Next color is:  g
Next color is:  c
Next color is:  y
Next color is:  b
Next color is:  r
Next color is:  m
Next color is:  k
Next color is:  g
Next color is:  c

Rotate color


6
2017-12-12 02:20



Cảm ơn! Chỉ cần làm rõ, nó không giống như trả về một bản sao, mà là một tham chiếu đến chu kỳ thực tế. Vì vậy, gọi rainbow.next () thực sự sẽ thay đổi cốt truyện tiếp theo sẽ trông như thế nào. - mwaskom


Tôi chỉ muốn thêm vào những gì @Andi đã nói ở trên. Vì color_cycle không được chấp nhận trong matplotlib 1.5, bạn phải sử dụng prop_cyclerTuy nhiên, giải pháp của Andi (ax._get_lines.prop_cycler.next()['color']) trả lại lỗi này cho tôi:

AttributeError: đối tượng 'itertools.cycle' không có thuộc tính 'next'

Mã làm việc cho tôi là: next(ax._get_lines.prop_cycler), thực sự không xa đáp ứng ban đầu của @ joe-kington.

Cá nhân, tôi đã gặp phải vấn đề này khi tạo một trục kép (), để đặt lại bộ tạo màu. Tôi cần một cách để làm cho màu sắc chu kỳ chính xác vì tôi đã sử dụng style.use('ggplot'). Có thể có một cách dễ dàng hơn / tốt hơn để làm điều này, vì vậy hãy sửa tôi.


3
2017-12-15 17:20



hãy định dạng bài đăng của bạn thành một câu trả lời, không phải là một tài liệu thảo luận (nếu không nó là một bình luận), điểm chính của bạn là next(ax._get_lines.prop_cycler) là một sự thay thế có thể. - UmNyobe
Vì vậy, nếu tôi không có đủ danh tiếng để bình luận, tôi có nên đề nghị chỉnh sửa câu trả lời của Andi không? - Carson
@ Làm thế nào để bạn tránh được cuộc gọi next(ax._get_lines.prop_cycler) thực sự thay đổi trạng thái của bộ tạo màu? - kazemakase
Đó thực sự là những gì tôi đã cố gắng làm - thay đổi trạng thái của prop_cycler. Nếu bạn đọc câu trả lời được chấp nhận cho câu hỏi này, bạn sẽ thấy rằng không có cách nào để truy cập trạng thái prop_cycler mà không thay đổi trạng thái của nó vì nó có thể lặp lại được. - Carson
Lỗi bạn chỉ ra là một sự khác biệt Python 2 / Python 3. Cảm ơn bạn đã chỉ ra điều này, tôi đã chỉnh sửa câu trả lời của mình cho phù hợp. - Andi


Vì matplotlib sử dụng itertools.cycle chúng tôi thực sự có thể xem xét toàn bộ chu trình màu và sau đó khôi phục trình lặp lại về trạng thái trước đó của nó:

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

Điều này sẽ trả về danh sách mà không thay đổi trạng thái của trình lặp.

Sử dụng nó với matplotlib> = 1.5:

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

hoặc với matplotlib <1.5:

>>> list_from_cycle(ax._get_lines.color_cycle)
['r', 'g', 'b']

1
2017-09-22 08:38