Nhận trạng thái chu kỳ màu matplotlib


89

Có thể truy vấn trạng thái hiện tại của chu kỳ màu matplotlib không? Nói cách khác, có một hàm get_cycle_statesẽ hoạt động theo cách sau không?

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

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


Điều này có thể được thực hiện, thậm chí không làm thay đổi tình trạng hiện tại, xem câu trả lời dưới đây
sancho.s ReinstateMonicaCellio

Câu trả lời:


108

Truy cập trình lặp chu kỳ màu

Không có phương thức "hướng tới người dùng" (hay còn gọi là "công khai") để truy cập trình vòng lặp bên dưới, nhưng bạn có thể truy cập nó thông qua các phương thức "riêng tư" (theo quy ước). Tuy nhiên, bạn không thể có được trạng thái của một iteratormà không thay đổi nó.

Thiết lập chu kỳ màu

Nhanh chóng sang một bên: Bạn có thể đặt chu kỳ màu / thuộc tính theo nhiều cách khác nhau (ví dụ: ax.set_color_cycletrong các phiên bản <1.5 hoặc ax.set_prop_cyclertrong> = 1.5). Hãy xem ví dụ ở đây cho phiên bản 1.5 trở lên hoặc kiểu trước đó tại đây .

Truy cập trình lặp bên dưới

Tuy nhiên, trong khi không có phương thức công khai nào để truy cập có thể lặp lại, bạn có thể truy cập nó cho một đối tượng trục đã cho ( ax) thông qua _get_linescá thể lớp trợ giúp. ax._get_lineslà một liên lạc được đặt tên một cách khó hiểu, nhưng đó là cỗ máy hậu trường cho phép plotlệnh xử lý tất cả các cách kỳ lạ và đa dạng plotcó thể được gọi. Trong số những thứ khác, đó là thứ theo dõi những màu nào để tự động gán. Tương tự, có ax._get_patches_for_fillđể kiểm soát chu kỳ thông qua các màu tô mặc định và thuộc tính vá.

Ở bất kỳ tỷ lệ nào, chu kỳ màu có thể lặp lại là ax._get_lines.color_cyclecho các đường và ax._get_patches_for_fill.color_cyclecho các mảng. Trên matplotlib> = 1.5, điều này đã thay đổi để sử dụng cyclerthư viện và có thể lặp lại được gọi prop_cyclerthay vì color_cyclevà mang lại một dictthuộc tính thay vì chỉ một màu.

Nói chung, bạn sẽ làm điều 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à một "trần" iterator. Chúng ta có thể dễ dàng nhận đượ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à màu 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.5hoặc lớn hơn, sẽ rất tuyệt nếu có được cyclerđối tượng được sử dụng, vì chúng ta có thể suy ra trạng thái hiện tại của nó. Tuy nhiên, cyclerbản thân đối tượng không thể truy cập (công khai hoặc riêng tư) ở bất kỳ đâu. Thay vào đó, chỉ itertools.cyclecá thể được tạo từ cyclerđối tượng mới có thể truy cập được. Dù bằng cách nào, không có cách nào để đi đến trạng thái cơ bản của bộ tuần hoàn màu / thuộc tính.

Thay vào đó, hãy khớp với màu của mục được vẽ trước đó

Trong trường hợp của bạn, có vẻ như bạn đang muốn kết hợp màu sắc của thứ gì đó vừa được vẽ. Thay vì cố gắng xác định màu / thuộc tính sẽ là gì, hãy đặt màu / vv của mặt hàng mới của bạn dựa trên các thuộc tính của thứ được vẽ.

Ví dụ, trong trường hợp bạn mô tả, tôi sẽ làm như sau:

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()

nhập mô tả hình ảnh ở đây

Đó không phải là cách duy nhất, nhưng nó rõ ràng hơn là cố gắng lấy màu của đường vẽ trước đó, trong trường hợp này.


Tôi đang viết một số hàm cấp cao hơn để tự động hóa các tác vụ vẽ sơ đồ chung cho lĩnh vực công việc của tôi. Thường thì những đối tượng này đòi hỏi phải vẽ nhiều đối tượng matplotlib và tôi muốn có một số đối tượng có chung một màu trong chu kỳ màu, vì vậy tôi không phải lúc nào cũng phải cung cấp đối số màu nếu tôi cảm thấy lười biếng.
mwaskom 12/12/12

12
Nếu bạn chỉ muốn kết hợp màu của một đối tượng cụ thể, bạn luôn có thể lấy màu của nó. vd: line, = ax.plot(x, y)và sau đó sử dụng line.get_color()để lấy màu của đường được vẽ trước đó.
Joe Kington

3
Nó dường như ax._get_lines.color_cyclekhông còn tồn tại trong 1.5?
endolith

6
Tôi nghĩ rằng tương đương (gần) là một phép lặp lại ax._get_lines.prop_cyclermà bạn có thể có một cấu trúc như if 'color' in ax._get_lines._prop_keys:sau next(ax._get_lines.prop_cycler)['color']để làm tương đương với những gì được đề xuất color_cycletrong câu trả lời, tôi tin. Tôi không chắc liệu bạn có thể mua được một chiếc có thể lặp lại chỉ với các màu hay không, tôi cần khám phá thêm.
J Richard Snape

1
@endolith Chắc chắn (và cảm ơn) - nhưng tôi chỉ cảm thấy tốt hơn là nên ngồi trong câu trả lời đã rất hay và được chấp nhận của Joe. Nếu anh ta không nhận được vòng để đặt nó trong ngày thứ Hai - Tôi sẽ dính vào một câu trả lời thích hợp lên đó thay vì sợ hãi "câu trả lời-in-ý kiến"
J Richard Snape

25

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

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

Thao tác này sẽ cung cấp cho bạn danh sách các màu được xác định theo kiểu hiện tại.


1
Hoạt động trong 2.0.2.
KevinG

16

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 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 ) để lấy màu tiếp theo từ chu kỳ màu.

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


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

6

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

#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 bạn đã nhận xét, có vẻ như không thể sao chép trình lặp. Một ý tưởng sẽ là thực hiện một chu kỳ đầy đủ và theo dõi giá trị nào 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 toàn thứ tự màu, chỉ là giá trị màu tiếp theo, tôi để đó cho bạn.

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

#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

Xoay màu


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 trình thực tế. Vì vậy, việc gọi cầu vồng.next () thực sự cũng sẽ thay đổi cốt truyện tiếp theo sẽ trông như thế nào.
mwaskom 12/12/12

5

Tôi chỉ muốn thêm vào những gì @Andi đã nói ở trên. Vì color_cyclekhông được chấp nhận trong matplotlib 1.5, bạn phải sử dụng prop_cycler, tuy 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ã phù hợp với tôi là next(ax._get_lines.prop_cycler):, thực ra không khác xa so với phản hồi 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 twinx (), trục này đặt lại bộ tuần hoàn màu. Tôi cần một cách để làm cho màu sắc thay đổi một cách chính xác vì tôi đang 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 cho tôi.


vui lòng định dạng bài đăng của bạn dưới dạng một câu trả lời , không phải như một tài liệu thảo luận (nếu không nó là một nhận xét), điểm chính của bạn là đó next(ax._get_lines.prop_cycler)là một sự thay thế khả thi.
UmNyobe

@Carson Bạn làm cách nào để tránh rằng lệnh gọi next(ax._get_lines.prop_cycler)thực sự thay đổi trạng thái của bộ tuần hoàn 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_cyclermà không thay đổi trạng thái của nó vì nó có thể lặp lại.
Carson

Lỗi bạn chỉ ra là sự khác biệt giữa Python 2 / Python 3. Cảm ơn vì đã 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

1

Vì sử dụng matplotlib, itertools.cyclechúng tôi thực sự có thể xem qua toàn bộ chu kỳ màu và sau đó khôi phục trình lặp 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

Thao tác 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']

0

Cách đơn giản nhất có thể mà tôi có thể tìm thấy mà không cần thực hiện toàn bộ vòng lặp qua cycler là ax1.lines[-1].get_color().


-1

Trong phiên bản matplotlib 2.2.3 có một get_next_color()phương thức trên thuộc _get_linestính:

import from matplotlib import pyplot as plt
fig, ax = plt.subplots()
next_color = ax._get_lines.get_next_color()

get_next_color() trả về một chuỗi màu html và nâng cao trình lặp chu kỳ màu.


-1

Làm thế nào để truy cập chu trình màu (và kiểu hoàn chỉnh)?

Trạng thái hiện tại được lưu trữ trong ax._get_lines.prop_cycler. Không có phương thức tích hợp nào để hiển thị "danh sách cơ sở" cho một danh sách chung itertools.cyclevà cụ thể cho ax._get_lines.prop_cycler(xem bên dưới).

Tôi đã đăng ở đây một vài chức năng để có được thông tin về a itertools.cycle. Sau đó người ta có thể sử dụng

style_cycle = ax._get_lines.prop_cycler
curr_style = get_cycle_state(style_cycle)  # <-- my (non-builtin) function
curr_color = curr_style['color']

để có được màu hiện tại mà không thay đổi trạng thái của chu kỳ .


TL; DR

Chu trình màu (và kiểu hoàn chỉnh) được lưu trữ ở đâu?

Chu trình kiểu được lưu trữ ở hai nơi khác nhau, một cho mặc định và một cho các trục hiện tại (giả sử import matplotlib.pyplot as pltaxlà một trình xử lý trục):

default_prop_cycler = plt.rcParams['axes.prop_cycle']
current_prop_cycle = ax._get_lines.prop_cycler

Lưu ý rằng chúng có các lớp khác nhau. Mặc định là "cài đặt chu kỳ cơ sở" và nó không biết về bất kỳ trạng thái hiện tại nào cho bất kỳ trục nào, trong khi dòng điện biết về chu kỳ sẽ tuân theo và trạng thái hiện tại của nó:

print('type(default_prop_cycler) =', type(default_prop_cycler))
print('type(current_prop_cycle) =', type(current_prop_cycle))

[]: type(default_prop_cycler) = <class 'cycler.Cycler'>
[]: type(current_prop_cycle) = <class 'itertools.cycle'>

Chu kỳ mặc định có thể có một số khóa (thuộc tính) để xoay vòng và một người chỉ có thể nhận được các màu:

print('default_prop_cycler.keys =', default_prop_cycler.keys)
default_prop_cycler2 = plt.rcParams['axes.prop_cycle'].by_key()
print(default_prop_cycler2)
print('colors =', default_prop_cycler2['color'])

[]: default_prop_cycler.keys = {'color', 'linestyle'}
[]: {'color': ['r', 'g', 'b', 'y'], 'linestyle': ['-', '--', ':', '-.']}
[]: colors = ['r', 'g', 'b', 'y']

Người ta thậm chí có thể thay đổi cyclercách sử dụng cho một thứ nhất định axes, sau khi xác định điều đó custom_prop_cycler, với

ax.set_prop_cycle(custom_prop_cycler)

Nhưng không có phương thức tích hợp nào để hiển thị "danh sách cơ sở" cho một cái chung itertools.cyclevà đặc biệt cho ax._get_lines.prop_cycler.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.