การบันทึกตัวเลข 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.