การบันทึกตัวเลข Matplotlib แบบโต้ตอบ


120

มีวิธีบันทึกรูป Matplotlib เพื่อให้สามารถเปิดใหม่และเรียกคืนการโต้ตอบทั่วไปได้หรือไม่? (เช่นเดียวกับรูปแบบ. fig ใน MATLAB หรือไม่)

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

คำตอบ:


31

นี่จะเป็นคุณสมบัติที่ยอดเยี่ยม แต่ AFAIK ไม่ได้ใช้ใน Matplotlib และอาจเป็นเรื่องยากที่จะใช้งานด้วยตัวเองเนื่องจากวิธีการจัดเก็บตัวเลข

ฉันขอแนะนำอย่างใดอย่างหนึ่ง (a) การประมวลผลข้อมูลแยกจากการสร้างรูป (ซึ่งบันทึกข้อมูลด้วยชื่อที่ไม่ซ้ำกัน) และเขียนสคริปต์การสร้างตัวเลข (โหลดไฟล์ที่ระบุของข้อมูลที่บันทึกไว้) และแก้ไขตามที่เห็นสมควรหรือ (b ) บันทึกเป็นรูปแบบ PDF / SVG / PostScriptและแก้ไขในโปรแกรมแก้ไขรูปแฟนซีเช่นAdobe Illustrator (หรือInkscape )

แก้ไขโพสต์ฤดูใบไม้ร่วง 2012 : ตามที่คนอื่น ๆ ระบุไว้ด้านล่าง (แม้ว่าจะกล่าวถึงที่นี่เนื่องจากเป็นคำตอบที่ยอมรับ) Matplotlib ตั้งแต่เวอร์ชัน 1.2 อนุญาตให้คุณดองตัวเลข ตามที่ระบุในบันทึกประจำรุ่นเป็นคุณลักษณะทดลองและไม่สนับสนุนการบันทึกรูปใน matplotlib เวอร์ชันเดียวและเปิดในอีกเวอร์ชันหนึ่ง โดยทั่วไปแล้วการกู้คืนของดองจากแหล่งที่ไม่น่าเชื่อถือนั้นไม่ปลอดภัย

สำหรับการแบ่งปัน / แก้ไขแปลงในภายหลัง (ซึ่งต้องมีการประมวลผลข้อมูลที่สำคัญก่อนและอาจต้องปรับแต่งหลายเดือนต่อมาพูดในระหว่างการตรวจสอบโดยเพื่อนสำหรับสิ่งพิมพ์ทางวิทยาศาสตร์) ฉันยังคงแนะนำขั้นตอนการทำงานของ (1) มีสคริปต์การประมวลผลข้อมูลที่ก่อนสร้างพล็อต บันทึกข้อมูลที่ประมวลผลแล้ว (ซึ่งจะเข้าสู่พล็อตของคุณ) ลงในไฟล์และ (2) มีสคริปต์การสร้างพล็อตแยกต่างหาก (ซึ่งคุณปรับเปลี่ยนตามความจำเป็น) เพื่อสร้างพล็อตใหม่ วิธีนี้สำหรับแต่ละพล็อตคุณสามารถเรียกใช้สคริปต์และสร้างใหม่ได้อย่างรวดเร็ว (และคัดลอกการตั้งค่าพล็อตของคุณอย่างรวดเร็วด้วยข้อมูลใหม่) ที่กล่าวว่าการดองร่างอาจสะดวกสำหรับการวิเคราะห์ข้อมูลระยะสั้น / เชิงโต้ตอบ / เชิงสำรวจ


2
ค่อนข้างแปลกใจที่ไม่มีการใช้งานนี้ .. แต่โอเคฉันจะบันทึกข้อมูลที่ประมวลผลแล้วในไฟล์กลางและส่งและสคริปต์สำหรับวางแผนให้เพื่อนร่วมงาน ขอบคุณ
— Matt

2
ฉันสงสัยว่าการนำไปใช้งานนั้นยากซึ่งเป็นสาเหตุที่ทำให้ MATLAB ทำงานได้ไม่ดีนัก ย้อนกลับไปเมื่อฉันใช้มันตัวเลขที่เคยทำให้ MATLAB พังและแม้แต่เวอร์ชันที่แตกต่างกันเล็กน้อยก็ไม่สามารถอ่านไฟล์. fig ของกันและกันได้
— Adrian Ratnapala

6
pickleตอนนี้ใช้งานได้กับตัวเลข MPL ดังนั้นจึงสามารถทำได้และดูเหมือนว่าจะทำงานได้ดีพอสมควร - เกือบจะเหมือนกับไฟล์รูป Matlab ".fig" ดูคำตอบของฉันด้านล่าง (สำหรับตอนนี้?) สำหรับตัวอย่างวิธีการทำ
— Demis

@ Demis: ตามที่ ptomato ชี้ให้เห็นในคำตอบของเขาด้านล่างมันมีอยู่แล้วในเวลานั้น
— strpeter

@strpeter - ตัวเลข Matlab ไม่สามารถดองได้ในปี 2010 ตามที่ระบุไว้ในความคิดเห็นนี้ คุณลักษณะทดลองได้รับการเพิ่มเข้ามาด้วยmatplotlib 1.2 เปิดตัวในฤดูใบไม้ร่วง 2012 ตามที่ระบุไว้คุณไม่ควรคาดหวังว่ามันจะทำงานระหว่างเวอร์ชัน matplotlib และคุณไม่ควรเปิดผักดองที่มาจากแหล่งที่ไม่น่าเชื่อถือ
— dr jimbob

64

ฉันเพิ่งค้นพบวิธีการทำสิ่งนี้ "การสนับสนุนการดองแบบทดลอง" ที่กล่าวถึงโดย @pelson ทำงานได้ดีทีเดียว

ลองสิ่งนี้:

# Plot something
import matplotlib.pyplot as plt
fig,ax = plt.subplots()
ax.plot([1,2,3],[10,-10,30])

หลังจากปรับแต่งแบบโต้ตอบของคุณแล้วให้บันทึกวัตถุรูปเป็นไฟล์ไบนารี:

import pickle
pickle.dump(fig, open('FigureObject.fig.pickle', 'wb')) # This is for Python 3 - py2 may need `file` instead of `open`

หลังจากนั้นให้เปิดรูปและควรบันทึกการปรับแต่งและควรมีการโต้ตอบ GUI:

import pickle
figx = pickle.load(open('FigureObject.fig.pickle', 'rb'))

figx.show() # Show the figure, edit it, etc.!

คุณยังสามารถดึงข้อมูลจากแปลง:

data = figx.axes[0].lines[0].get_data()

(ใช้ได้กับเส้น, pcolor & imshow - pcolormesh ทำงานร่วมกับเทคนิคบางอย่างเพื่อสร้างข้อมูลที่แบนใหม่)

ผมได้เคล็ดลับที่ดีเยี่ยมจากการประหยัดการใช้ตัวเลข Matplotlib ดอง


ฉันเชื่อว่าสิ่งนี้ไม่สามารถใช้ได้กับการเปลี่ยนแปลงเวอร์ชัน ฯลฯ และไม่สามารถใช้งานร่วมกันได้ระหว่าง py2.x และ py3.x นอกจากนี้ฉันคิดว่าpickleเอกสารประกอบระบุว่าเราจำเป็นต้องตั้งค่าสภาพแวดล้อมเช่นเดียวกับเมื่อวัตถุถูกดอง (บันทึก) แต่ฉันพบว่าคุณไม่จำเป็นต้องทำimport matplotlib.pyplot as pltเมื่อยกเลิกการคลิก (กำลังโหลด) - มันจะบันทึกคำสั่งนำเข้าในไฟล์ดอง .
— Demis

5
คุณควรพิจารณาปิดไฟล์ที่เปิด: เช่นwith open('FigureObject.fig.pickle', 'rb') as file: figx = pickle.load(file)
— strpeter

1
ฉันเพิ่งได้รับ: 'AttributeError:' Figure 'object ไม่มีแอตทริบิวต์' _cachedRenderer ''
— user2673238

หากคุณไม่ต้องการให้สคริปต์ทำงานต่อและอาจยุติทันทีหลังจากfigx.show()นั้นคุณควรโทรหาplt.show()แทนซึ่งเป็นการบล็อก
— maechler

38

สำหรับ Matplotlib 1.2 ตอนนี้เรามีการสนับสนุนการดองแบบทดลอง ลองดูว่าใช้ได้ดีกับกรณีของคุณหรือไม่ หากคุณมีปัญหาใด ๆ โปรดแจ้งให้เราทราบในรายชื่อ Matplotlib ทางไปรษณีย์หรือโดยการเปิดประเด็นในgithub.com/matplotlib/matplotlib


2
เหตุผลใดก็ตามที่สามารถเพิ่มคุณลักษณะที่มีประโยชน์นี้ลงใน "บันทึกเป็น" ของรูปได้เอง อาจจะเพิ่ม. pkl?
— มืดมน

ไอเดียดี @dashesy. ฉันสนับสนุนว่าหากคุณต้องการนำไปใช้จริงหรือไม่?
— pelson

1
สิ่งนี้ใช้ได้เฉพาะกับแบ็กเอนด์ชุดย่อยหรือไม่ เมื่อฉันพยายามที่จะดองร่างที่เรียบง่ายใน OSX PicklingError: Can't pickle <type '_macosx.GraphicsContext'>: it's not found as _macosx.GraphicsContextฉันได้รับ
— farenorth

ข้างต้นPicklingErrorเกิดขึ้นเฉพาะเมื่อคุณโทรplt.show()ก่อนทำการดอง สถานที่ดังนั้นเพียงแค่หลังplt.show() pickle.dump()
— salomonvh

ใน py3.5 ของฉันบน MacOS 10.11 ลำดับของfig.show()ดูเหมือนจะไม่สำคัญ - ข้อบกพร่องนั้นอาจได้รับการแก้ไขแล้ว ฉันสามารถดองก่อน / หลังได้show()โดยไม่มีปัญหา
— Demis

7

ทำไมไม่ส่งสคริปต์ Python ล่ะ? ไฟล์. fig ของ MATLAB ต้องการให้ผู้รับมี MATLAB เพื่อแสดงไฟล์ดังนั้นจึงเทียบเท่ากับการส่งสคริปต์ Python ที่ต้องใช้ Matplotlib เพื่อแสดง

อีกทางเลือกหนึ่ง (ข้อจำกัดความรับผิดชอบ: ฉันยังไม่ได้ลอง) คุณสามารถลองดองร่าง:

import pickle
output = open('interactive figure.pickle', 'wb')
pickle.dump(gcf(), output)
output.close()

3
น่าเสียดายที่ตัวเลข matplotlib ไม่สามารถดองได้ดังนั้นแนวทางดังกล่าวจึงไม่ได้ผล เบื้องหลังมีนามสกุล C มากเกินไปที่ไม่รองรับการดอง ฉันเห็นด้วยอย่างยิ่งกับการส่งสคริปต์ + ข้อมูลแม้ว่า ... ฉันเดาว่าฉันไม่เคยเห็นจุดของ. fig ที่บันทึกไว้ของ matlab จริงๆดังนั้นฉันจึงไม่เคยใช้มัน การส่งรหัสและข้อมูลแบบสแตนด์อะโลนให้ใครสักคนเป็นวิธีที่ง่ายที่สุดในระยะยาวจากประสบการณ์ของฉัน อย่างไรก็ตามมันจะดีถ้าวัตถุในรูปของ matplotlib ที่สามารถดองได้
— Joe Kington

1
แม้แต่ข้อมูลที่ผ่านการประมวลผลล่วงหน้าของเราก็มีขนาดค่อนข้างใหญ่และขั้นตอนการลงจุดก็ซับซ้อน ดูเหมือนการขอความช่วยเหลือเพียงอย่างเดียว ขอบคุณ
— Matt

1
เห็นได้ชัดว่าตอนนี้ตัวเลขเป็นของดอง - ใช้งานได้ดีทีเดียว! ตัวอย่างด้านล่าง
— Demis

ประโยชน์ของการดองคือคุณไม่จำเป็นต้องปรับระยะห่าง / ตำแหน่งของรูป / พล็อตย่อยทั้งหมดโดยทางโปรแกรม คุณสามารถใช้ GUI ของพล็อต MPL เพื่อทำให้รูปดูสวยงามเป็นต้นจากนั้นบันทึกMyPlot.fig.pickleไฟล์ - รักษาความสามารถในภายหลังในการปรับเปลี่ยนการนำเสนอพล็อตตามต้องการ นี่คือสิ่งที่ยอดเยี่ยมเกี่ยวกับ.figไฟล์ของ Matlab มีประโยชน์อย่างยิ่งเมื่อคุณต้องการเปลี่ยนขนาด / อัตราส่วนภาพ (สำหรับแทรกลงในงานนำเสนอ / เอกสาร)
— Demis

1

คำถามที่ดี. นี่คือข้อความเอกสารจากpylab.save:

pylab ไม่มีฟังก์ชัน save อีกต่อไปแม้ว่าฟังก์ชัน pylab แบบเก่าจะยังคงใช้งานได้ในรูปแบบ matplotlib.mlab.save (คุณยังสามารถอ้างถึงใน pylab ว่า "mlab.save" ได้) อย่างไรก็ตามสำหรับไฟล์ข้อความธรรมดาเราขอแนะนำ numpy.savetxt สำหรับการบันทึกอาร์เรย์ numpy เราขอแนะนำ numpy.save และ analog numpy.load ซึ่งมีอยู่ใน pylab เป็น np.save และ np.load


ซึ่งจะบันทึกข้อมูลจากวัตถุไพแล็บ แต่ไม่อนุญาตให้คุณสร้างรูปใหม่
— dr jimbob

แก้ไข. pylab.saveฉันควรจะชี้แจงว่าคำตอบคือไม่ได้คำแนะนำการใช้ ในความเป็นจริงจากข้อความ doc ปรากฏว่าไม่ควรใช้
— Steve Tjoa

มีวิธีภายนอกในการส่ง 3D Figure หรือไม่? เป็นไปได้แม้กระทั่ง GUI ธรรมดา ๆ เพื่อ exe ..
— CromeX

0

ฉันคิดหาวิธีที่ค่อนข้างง่าย (แต่แหวกแนวเล็กน้อย) ในการบันทึกตัวเลข matplotlib ของฉัน การทำงานเช่นนี้:

import libscript

import matplotlib.pyplot as plt
import numpy as np

t = np.arange(0.0, 2.0, 0.01)
s = 1 + np.sin(2*np.pi*t)

#<plot>
plt.plot(t, s)
plt.xlabel('time (s)')
plt.ylabel('voltage (mV)')
plt.title('About as simple as it gets, folks')
plt.grid(True)
plt.show()
#</plot>

save_plot(fileName='plot_01.py',obj=sys.argv[0],sel='plot',ctx=libscript.get_ctx(ctx_global=globals(),ctx_local=locals()))

ด้วยฟังก์ชันที่save_plotกำหนดไว้เช่นนี้ (เวอร์ชันง่าย ๆ เพื่อทำความเข้าใจตรรกะ):

def save_plot(fileName='',obj=None,sel='',ctx={}):
    """
    Save of matplolib plot to a stand alone python script containing all the data and configuration instructions to regenerate the interactive matplotlib figure.

    Parameters
    ----------
    fileName : [string] Path of the python script file to be created.
    obj : [object] Function or python object containing the lines of code to create and configure the plot to be saved.
    sel : [string] Name of the tag enclosing the lines of code to create and configure the plot to be saved.
    ctx : [dict] Dictionary containing the execution context. Values for variables not defined in the lines of code for the plot will be fetched from the context.

    Returns
    -------
    Return ``'done'`` once the plot has been saved to a python script file. This file contains all the input data and configuration to re-create the original interactive matplotlib figure.
    """
    import os
    import libscript

    N_indent=4

    src=libscript.get_src(obj=obj,sel=sel)
    src=libscript.prepend_ctx(src=src,ctx=ctx,debug=False)
    src='\n'.join([' '*N_indent+line for line in src.split('\n')])

    if(os.path.isfile(fileName)): os.remove(fileName)
    with open(fileName,'w') as f:
        f.write('import sys\n')
        f.write('sys.dont_write_bytecode=True\n')
        f.write('def main():\n')
        f.write(src+'\n')

        f.write('if(__name__=="__main__"):\n')
        f.write(' '*N_indent+'main()\n')

return 'done'

หรือกำหนดฟังก์ชันsave_plotเช่นนี้ (เวอร์ชันที่ดีกว่าโดยใช้การบีบอัด zip เพื่อสร้างไฟล์รูปที่มีน้ำหนักเบา):

def save_plot(fileName='',obj=None,sel='',ctx={}):

    import os
    import json
    import zlib
    import base64
    import libscript

    N_indent=4
    level=9#0 to 9, default: 6
    src=libscript.get_src(obj=obj,sel=sel)
    obj=libscript.load_obj(src=src,ctx=ctx,debug=False)
    bin=base64.b64encode(zlib.compress(json.dumps(obj),level))

    if(os.path.isfile(fileName)): os.remove(fileName)
    with open(fileName,'w') as f:
        f.write('import sys\n')
        f.write('sys.dont_write_bytecode=True\n')
        f.write('def main():\n')
        f.write(' '*N_indent+'import base64\n')
        f.write(' '*N_indent+'import zlib\n')
        f.write(' '*N_indent+'import json\n')
        f.write(' '*N_indent+'import libscript\n')
        f.write(' '*N_indent+'bin="'+str(bin)+'"\n')
        f.write(' '*N_indent+'obj=json.loads(zlib.decompress(base64.b64decode(bin)))\n')
        f.write(' '*N_indent+'libscript.exec_obj(obj=obj,tempfile=False)\n')

        f.write('if(__name__=="__main__"):\n')
        f.write(' '*N_indent+'main()\n')

return 'done'

สิ่งนี้ทำให้ใช้โมดูลlibscriptของฉันเองซึ่งส่วนใหญ่อาศัยโมดูลinspectและast. ฉันสามารถลองแชร์บน Github ได้หากมีการแสดงความสนใจ (ก่อนอื่นต้องมีการล้างข้อมูลและฉันจึงจะเริ่มต้นใช้งาน Github ได้)

แนวคิดเบื้องหลังsave_plotฟังก์ชันและlibscriptโมดูลนี้คือการดึงคำสั่ง python ที่สร้างรูป (โดยใช้โมดูลinspect) วิเคราะห์ (โดยใช้โมดูลast) เพื่อแยกตัวแปรฟังก์ชั่นและโมดูลทั้งหมดที่นำเข้าซึ่งอาศัยแยกสิ่งเหล่านี้ออกจากบริบทการดำเนินการและทำให้เป็นอนุกรม ตามคำสั่ง python (โค้ดสำหรับตัวแปรจะเป็นเหมือนt=[0.0,2.0,0.01]... และโค้ดสำหรับโมดูลจะเป็นเหมือนimport matplotlib.pyplot as plt... ) นำหน้าคำสั่งรูป คำสั่ง python ที่ได้จะถูกบันทึกเป็นสคริปต์ python ซึ่งการดำเนินการจะสร้างรูป matplotlib ดั้งเดิมขึ้นมาใหม่

อย่างที่คุณสามารถจินตนาการได้ว่าสิ่งนี้ใช้ได้ดีกับตัวเลข matplotlib ส่วนใหญ่ (ถ้าไม่ใช่ทั้งหมด)

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