รับสถานะวัฏจักรสี matplotlib


90

เป็นไปได้ไหมที่จะสอบถามสถานะปัจจุบันของวงจรสี matplotlib กล่าวอีกนัยหนึ่งมีฟังก์ชั่นget_cycle_stateที่จะทำงานในลักษณะต่อไปนี้หรือไม่?

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

โดยที่ฉันคาดว่ารัฐจะเป็นดัชนีของสีถัดไปที่จะใช้ในพล็อต หรืออีกวิธีหนึ่งหากส่งคืนสีถัดไป ("r" สำหรับรอบเริ่มต้นในตัวอย่างด้านบน) ก็จะดีเช่นกัน


สิ่งนี้สามารถทำได้แม้ไม่เปลี่ยนสถานะปัจจุบันดูคำตอบด้านล่าง
sancho.s ReinstateMonicaCellio

คำตอบ:


110

การเข้าถึงตัววนรอบสี

ไม่มีวิธี "ผู้ใช้หันหน้าไปทาง" (หรือที่เรียกว่า "สาธารณะ") ในการเข้าถึงตัววนซ้ำพื้นฐาน แต่คุณสามารถเข้าถึงได้โดยใช้วิธี "ส่วนตัว" (ตามแบบแผน) อย่างไรก็ตามคุณไม่สามารถรับสถานะiteratorโดยไม่ต้องเปลี่ยนแปลงได้

การตั้งค่ารอบสี

กันอย่างรวดเร็ว: คุณสามารถกำหนดรอบสี / คุณสมบัติได้หลายวิธี (เช่นax.set_color_cycleในเวอร์ชัน <1.5 หรือax.set_prop_cyclerใน> = 1.5) ดูตัวอย่างที่นี่สำหรับเวอร์ชัน 1.5 ขึ้นไปหรือสไตล์ก่อนหน้านี้ที่นี่รูปแบบก่อนหน้านี้ที่นี่

การเข้าถึงตัววนซ้ำพื้นฐาน

อย่างไรก็ตามในขณะที่ไม่มีวิธีการเปิดเผยต่อสาธารณะในการเข้าถึงการทำซ้ำคุณสามารถเข้าถึงได้สำหรับอ็อบเจ็กต์แกนที่กำหนด ( ax) ผ่าน_get_linesอินสแตนซ์คลาสผู้ช่วย ax._get_linesเป็นสัมผัสที่ตั้งชื่ออย่างสับสน แต่เป็นเครื่องจักรเบื้องหลังที่ช่วยให้plotคำสั่งประมวลผลวิธีแปลก ๆ และหลากหลายที่plotสามารถเรียกใช้ได้ เหนือสิ่งอื่นใดสิ่งที่ติดตามว่าจะกำหนดสีใดโดยอัตโนมัติ ในทำนองเดียวกันมีax._get_patches_for_fillการควบคุมการหมุนผ่านสีเติมเริ่มต้นและคุณสมบัติการแก้ไข

ไม่ว่าในอัตราใดก็ตามรอบสีที่ทำซ้ำได้ax._get_lines.color_cycleสำหรับเส้นและax._get_patches_for_fill.color_cycleสำหรับแพทช์ บน matplotlib> = 1.5 สิ่งนี้ได้เปลี่ยนไปใช้cyclerไลบรารีและสามารถทำซ้ำได้ถูกเรียกprop_cyclerใช้แทนcolor_cycleและให้ a dictของคุณสมบัติแทนที่จะเป็นสีเท่านั้น

สรุปแล้วคุณจะทำสิ่งที่ชอบ:

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

คุณไม่สามารถดูสถานะของไฟล์ iterator

แต่วัตถุนี้เป็น iterator"เปล่า" เราสามารถรับรายการถัดไปได้อย่างง่ายดาย (เช่นnext_color = next(color_cycle)แต่นั่นหมายความว่าสีถัดไปหลังจากนั้นคือสิ่งที่จะถูกพล็อตโดยการออกแบบไม่มีทางที่จะรับสถานะปัจจุบันของตัววนซ้ำได้โดยไม่ต้องเปลี่ยน

ในv1.5หรือมากกว่าก็จะมีความสุขที่จะได้รับcyclerวัตถุที่ใช้ในขณะที่เราสามารถสรุปสถานะปัจจุบันของมัน อย่างไรก็ตามcyclerวัตถุนั้นไม่สามารถเข้าถึงได้ทุกที่ (แบบสาธารณะหรือแบบส่วนตัว) แต่จะมีเพียงitertools.cycleอินสแตนซ์ที่สร้างจากcyclerวัตถุเท่านั้นที่สามารถเข้าถึงได้ ไม่ว่าจะด้วยวิธีใดก็ไม่มีทางที่จะเข้าสู่สถานะพื้นฐานของวงจรสี / คุณสมบัติได้

จับคู่สีของรายการที่ลงจุดไว้ก่อนหน้านี้แทน

ในกรณีของคุณดูเหมือนว่าคุณต้องการจับคู่สีของสิ่งที่เพิ่งวางแผนไว้ แทนที่จะพยายามกำหนดสี / คุณสมบัติให้ตั้งค่าสี / ฯลฯ ของรายการใหม่ของคุณตามคุณสมบัติของสิ่งที่พล็อต

ตัวอย่างเช่นในกรณีที่คุณอธิบายฉันจะทำสิ่งนี้:

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

ป้อนคำอธิบายภาพที่นี่

ไม่ใช่วิธีเดียว แต่จะสะอาดกว่าการพยายามดึงสีของเส้นพล็อตก่อนมือในกรณีนี้


ฉันกำลังเขียนฟังก์ชันระดับสูงขึ้นเพื่อทำให้งานพล็อตทั่วไปสำหรับโดเมนงานของฉันเป็นไปโดยอัตโนมัติ บ่อยครั้งที่สิ่งเหล่านี้ทำให้เกิดการวาดวัตถุ matplotlib หลาย ๆ ชิ้นและฉันต้องการให้หลาย ๆ วัตถุแบ่งปันสีในวงจรสีดังนั้นฉันไม่จำเป็นต้องระบุอาร์กิวเมนต์สีเสมอไปหากฉันรู้สึกขี้เกียจ
mwaskom

12
หากคุณแค่ต้องการจับคู่สีของวัตถุชิ้นใดชิ้นหนึ่งคุณสามารถจับสีของมันได้ตลอดเวลา เช่นline, = ax.plot(x, y)แล้วใช้line.get_color()เพื่อให้ได้สีของเส้นที่พล็อตก่อนหน้านี้
Joe Kington

3
ดูเหมือนว่าจะax._get_lines.color_cycleไม่มีอยู่ใน 1.5 อีกต่อไป?
endolith

6
ฉันคิดว่าการเทียบเท่า (ใกล้) เป็นax._get_lines.prop_cyclerสิ่งที่สามารถทำซ้ำได้คุณสามารถสร้างสิ่งที่ต้องการif 'color' in ax._get_lines._prop_keys:ตามด้วยnext(ax._get_lines.prop_cycler)['color']เพื่อทำสิ่งที่เทียบเท่ากับสิ่งที่แนะนำด้วยcolor_cycleในคำตอบฉันเชื่อ ฉันไม่แน่ใจว่าคุณจะสามารถทำซ้ำได้เพียงแค่สีหรือไม่ฉันต้องสำรวจเพิ่มเติม
J Richard Snape

1
@endolith แน่นอน (และขอบคุณ) - แต่ฉันแค่รู้สึกว่ามันจะดีกว่าที่จะนั่งอยู่ในคำตอบที่ดีและเป็นที่ยอมรับของโจ ถ้าเขาไม่เข้าใกล้ภายในวันจันทร์ - ฉันจะตอบคำถามที่ถูกต้องแทน "คำตอบในความคิดเห็น" ที่น่ากลัว
J Richard Snape

26

นี่คือวิธีการทำงานใน 1.5 ซึ่งหวังว่าจะสามารถพิสูจน์ได้ในอนาคตเนื่องจากไม่ต้องพึ่งพาวิธีการที่มีเครื่องหมายขีดล่าง:

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

ซึ่งจะให้รายการสีที่กำหนดตามลำดับสำหรับสไตล์ปัจจุบัน


1
ทำงานใน 2.0.2
KevinG

16

หมายเหตุ: ในเวอร์ชันล่าสุดของ matplotlib (> = 1.5) _get_linesมีการเปลี่ยนแปลง ตอนนี้คุณต้องใช้next(ax._get_lines.prop_cycler)['color']ใน Python 2 หรือ 3 (หรือax._get_lines.prop_cycler.next()['color']ในPython 2 ) เพื่อรับสีถัดไปจากวงจรสี

หากเป็นไปได้ให้ใช้วิธีการที่ตรงกว่าที่แสดงไว้ในส่วนล่างของคำตอบของ @ joe-kington เนื่องจาก_get_linesไม่ใช่ API ซึ่งอาจมีการเปลี่ยนแปลงอีกครั้งในลักษณะที่ไม่สามารถใช้งานร่วมกันได้ในอนาคต


คุณจะหลีกเลี่ยงได้อย่างไรว่าการสอบถามองค์ประกอบถัดไปของคัลเลอร์ไซเคิลจะเปลี่ยนสถานะของไซเคิลเลอร์
kazemakase

6

แน่นอนว่าจะทำเช่นนี้

#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,

ให้:

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

นี่คือฟังก์ชัน itertools ที่ matplotlib ใช้ itertools.cycle

แก้ไข: ขอบคุณสำหรับความคิดเห็นดูเหมือนว่าจะไม่สามารถคัดลอกตัววนซ้ำได้ แนวคิดคือการถ่ายโอนข้อมูลแบบครบวงจรและติดตามว่าคุณใช้ค่าใดให้ฉันกลับมาดู

แก้ไข 2: เอาล่ะนี่จะให้สีถัดไปและสร้างตัวทำซ้ำใหม่ที่ทำงานราวกับว่าต่อไปจะไม่ถูกเรียก สิ่งนี้ไม่ได้รักษาลำดับของการระบายสีเพียงแค่ค่าสีถัดไปฉันฝากไว้ให้คุณ

สิ่งนี้ให้ผลลัพธ์ต่อไปนี้สังเกตว่าความชันในพล็อตสอดคล้องกับดัชนีเช่น g แรกเป็นกราฟที่ต่ำที่สุดเป็นต้น

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

คอนโซล

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

หมุนสี


ขอบคุณ! เพื่อชี้แจงดูเหมือนว่าจะไม่ส่งคืนสำเนา แต่เป็นการอ้างอิงถึงวัฏจักรจริง ดังนั้นการเรียก rainbow.next () จะเปลี่ยนลักษณะของพล็อตถัดไปด้วยเช่นกัน
mwaskom

5

ฉันแค่อยากจะเพิ่มสิ่งที่ @Andi กล่าวไว้ข้างต้น เนื่องจากcolor_cycleเลิกใช้งานแล้วใน matplotlib 1.5 คุณต้องใช้prop_cyclerอย่างไรก็ตามโซลูชันของ Andi ( ax._get_lines.prop_cycler.next()['color']) ส่งคืนข้อผิดพลาดนี้ให้ฉัน:

AttributeError: วัตถุ 'itertools.cycle' ไม่มีแอตทริบิวต์ 'next'

รหัสที่ใช้ได้กับฉันคือ: next(ax._get_lines.prop_cycler)ซึ่งจริงๆแล้วไม่ไกลจากคำตอบเดิมของ @ joe-kington

โดยส่วนตัวแล้วฉันพบปัญหานี้เมื่อสร้างแกน twinx () ซึ่งรีเซ็ตตัวหมุนสี style.use('ggplot')ผมจำเป็นต้องมีวิธีที่จะทำให้วงจรสีอย่างถูกต้องเพราะผมใช้ อาจมีวิธีที่ง่ายกว่า / ดีกว่าในการทำเช่นนี้ดังนั้นอย่าลังเลที่จะแก้ไขฉัน


โปรดจัดรูปแบบโพสต์ของคุณเป็นคำตอบไม่ใช่เพื่อเป็นข้อมูลสำหรับการสนทนา (มิฉะนั้นจะเป็นความคิดเห็น) ประเด็นหลักของคุณnext(ax._get_lines.prop_cycler)คือเป็นทางเลือกที่เป็นไปได้
UmNyobe

@ คาร์สันคุณจะหลีกเลี่ยงไม่ให้การโทรnext(ax._get_lines.prop_cycler)เปลี่ยนสถานะของไซเคิลสีได้อย่างไร?
kazemakase

นั่นคือสิ่งที่ฉันพยายามทำ - เปลี่ยนสถานะของไฟล์prop_cycler. หากคุณอ่านคำตอบที่ได้รับการยอมรับสำหรับคำถามนี้คุณจะเห็นว่าไม่มีทางเข้าถึงสถานะprop_cyclerโดยไม่เปลี่ยนสถานะได้เนื่องจากสามารถทำซ้ำได้
Carson

ข้อผิดพลาดที่คุณชี้ให้เห็นคือความแตกต่างของ Python 2 / Python 3 ขอบคุณที่ชี้ประเด็นนี้ฉันแก้ไขคำตอบตามนั้น
Andi

1

เนื่องจาก matplotlib ใช้itertools.cycleเราจึงสามารถดูรอบสีทั้งหมดจากนั้นเรียกคืนตัววนซ้ำเป็นสถานะก่อนหน้า

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

สิ่งนี้ควรส่งคืนรายการโดยไม่เปลี่ยนสถานะของตัววนซ้ำ

ใช้กับ matplotlib> = 1.5:

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

หรือด้วย matplotlib <1.5:

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

0

วิธีที่ง่ายที่สุดที่เป็นไปได้ที่จะหาได้โดยไม่ต้องทำห่วงทั้งผ่าน Cycler ax1.lines[-1].get_color()คือ


-1

ใน matplotlib เวอร์ชัน 2.2.3 มีget_next_color()วิธีการเกี่ยวกับ_get_linesคุณสมบัติ:

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

get_next_color() ส่งคืนสตริงสี html และเลื่อนตัววนรอบสี


-1

จะเข้าถึงวงจรสี (และรูปแบบที่สมบูรณ์) ได้อย่างไร?

ax._get_lines.prop_cyclerสถานะปัจจุบันถูกเก็บไว้ใน ไม่มีวิธีการในตัวเพื่อแสดง "รายการฐาน" สำหรับรายการทั่วไปitertools.cycleและโดยเฉพาะสำหรับax._get_lines.prop_cycler (ดูด้านล่าง)

ผมได้โพสต์ที่นี่itertools.cycleฟังก์ชั่นไม่กี่ที่จะได้รับข้อมูลเกี่ยวกับ หนึ่งสามารถใช้

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

เพื่อให้ได้สีปัจจุบันโดยไม่ต้องเปลี่ยนสถานะของวงจร


TL; ดร

วงจรสี (และรูปแบบที่สมบูรณ์) เก็บไว้ที่ไหน?

วัฏจักรสไตล์จะถูกเก็บไว้ในสองตำแหน่งที่แตกต่างกันหนึ่งสำหรับค่าเริ่มต้นและอีกหนึ่งสำหรับแกนปัจจุบัน (สมมติว่าimport matplotlib.pyplot as pltและaxเป็นตัวจัดการแกน):

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

โปรดทราบว่าสิ่งเหล่านี้มีคลาสที่แตกต่างกัน ค่าเริ่มต้นคือ "การตั้งค่ารอบฐาน" และจะไม่ทราบเกี่ยวกับสถานะปัจจุบันของแกนใด ๆ ในขณะที่กระแสจะรู้เกี่ยวกับวงจรที่จะติดตามและสถานะปัจจุบัน:

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

รอบเริ่มต้นอาจมีหลายปุ่ม (คุณสมบัติ) ในการหมุนเวียนและหนึ่งจะได้รับเฉพาะสี:

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

เราสามารถเปลี่ยนการcyclerใช้สำหรับสิ่งที่กำหนดaxesหลังจากกำหนดสิ่งcustom_prop_cyclerนั้นด้วย

ax.set_prop_cycle(custom_prop_cycler)

แต่มีในตัวไม่มีวิธีที่จะเปิดเผยรายการ "ฐาน" สำหรับทั่วไปและโดยเฉพาะอย่างยิ่งสำหรับitertools.cycleax._get_lines.prop_cycler

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.