ฉันจะกำหนดอัตราส่วนภาพใน matplotlib ได้อย่างไร


108

ฉันกำลังพยายามสร้างพล็อตสี่เหลี่ยม (โดยใช้ imshow) เช่นอัตราส่วน 1: 1 แต่ทำไม่ได้ ไม่มีงานเหล่านี้:

import matplotlib.pyplot as plt

ax = fig.add_subplot(111,aspect='equal')
ax = fig.add_subplot(111,aspect=1.0)
ax.set_aspect('equal')
plt.axes().set_aspect('equal')

ดูเหมือนว่าการโทรจะถูกเพิกเฉย (ปัญหาที่ฉันมักจะมีกับ matplotlib)


6
คุณได้ลองax.axis('equal')โดยบังเอิญ? ดังที่ทุกคนกล่าวไว้สิ่งที่คุณทำควรได้ผล แต่ax.axisอาจเป็นอีกเส้นทางหนึ่งที่จะลองหาวิธีแก้ปัญหา
Joe Kington

คำตอบ:


77

ครั้งที่สามของเสน่ห์ ฉันเดาว่านี่เป็นข้อบกพร่องและคำตอบของ Zhenyaแนะนำว่าได้รับการแก้ไขแล้วในเวอร์ชันล่าสุด ฉันมีเวอร์ชัน 0.99.1.1 และฉันได้สร้างโซลูชันต่อไปนี้:

import matplotlib.pyplot as plt
import numpy as np

def forceAspect(ax,aspect=1):
    im = ax.get_images()
    extent =  im[0].get_extent()
    ax.set_aspect(abs((extent[1]-extent[0])/(extent[3]-extent[2]))/aspect)

data = np.random.rand(10,20)

fig = plt.figure()
ax = fig.add_subplot(111)
ax.imshow(data)
ax.set_xlabel('xlabel')
ax.set_aspect(2)
fig.savefig('equal.png')
ax.set_aspect('auto')
fig.savefig('auto.png')
forceAspect(ax,aspect=1)
fig.savefig('force.png')

นี่คือ 'force.png': ป้อนคำอธิบายภาพที่นี่

ด้านล่างนี้เป็นความพยายามที่ไม่ประสบความสำเร็จ แต่หวังว่าจะเป็นประโยชน์

คำตอบที่สอง:

ของฉัน 'คำตอบเดิม' ด้านล่างเป็น overkill axes.set_aspect()ขณะที่มันไม่สิ่งที่คล้ายกับ ฉันคิดว่าคุณต้องการใช้axes.set_aspect('auto'). ฉันไม่เข้าใจว่าทำไมถึงเป็นเช่นนี้ แต่มันสร้างพล็อตภาพสี่เหลี่ยมสำหรับฉันตัวอย่างเช่นสคริปต์นี้:

import matplotlib.pyplot as plt
import numpy as np

data = np.random.rand(10,20)

fig = plt.figure()
ax = fig.add_subplot(111)
ax.imshow(data)
ax.set_aspect('equal')
fig.savefig('equal.png')
ax.set_aspect('auto')
fig.savefig('auto.png')

สร้างพล็อตภาพที่มีอัตราส่วนภาพ "เท่ากัน" ป้อนคำอธิบายภาพที่นี่ และอีกภาพหนึ่งที่มีอัตราส่วนภาพ "อัตโนมัติ": ป้อนคำอธิบายภาพที่นี่

รหัสที่ให้ไว้ด้านล่างใน 'คำตอบเดิม' เป็นจุดเริ่มต้นสำหรับอัตราส่วนภาพที่ควบคุมอย่างชัดเจน แต่ดูเหมือนว่าจะถูกละเว้นเมื่อมีการเรียกใช้ imshow

คำตอบเดิม:

นี่คือตัวอย่างของกิจวัตรที่จะปรับพารามิเตอร์แผนภาพย่อยเพื่อให้คุณได้อัตราส่วนภาพที่ต้องการ:

import matplotlib.pyplot as plt

def adjustFigAspect(fig,aspect=1):
    '''
    Adjust the subplot parameters so that the figure has the correct
    aspect ratio.
    '''
    xsize,ysize = fig.get_size_inches()
    minsize = min(xsize,ysize)
    xlim = .4*minsize/xsize
    ylim = .4*minsize/ysize
    if aspect < 1:
        xlim *= aspect
    else:
        ylim /= aspect
    fig.subplots_adjust(left=.5-xlim,
                        right=.5+xlim,
                        bottom=.5-ylim,
                        top=.5+ylim)

fig = plt.figure()
adjustFigAspect(fig,aspect=.5)
ax = fig.add_subplot(111)
ax.plot(range(10),range(10))

fig.savefig('axAspect.png')

สิ่งนี้ก่อให้เกิดรูปร่างดังนี้: ป้อนคำอธิบายภาพที่นี่

ฉันสามารถจินตนาการได้ว่าหากคุณมีพล็อตย่อยหลายจุดภายในรูปคุณจะต้องรวมจำนวนพล็อตย่อย y และ x เป็นพารามิเตอร์คำหลัก (โดยค่าเริ่มต้นเป็น 1 จุด) ในรูทีนที่ให้ไว้ จากนั้นใช้ตัวเลขเหล่านั้นและhspaceและwspaceคำหลักคุณสามารถทำให้พล็อตย่อยทั้งหมดมีอัตราส่วนภาพที่ถูกต้อง


1
สำหรับกรณีที่get_imagesมีรายการว่าง (เช่นเดียวกับที่จะเกิดขึ้นax.plot([0,1],[0,2])คุณสามารถใช้get_xlimและget_ylim
Joel

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

1
สาเหตุที่ลักษณะไม่เท่ากันเนื่องจากลักษณะที่เท่ากันหมายความว่าระยะการมองเห็นใน x จะเท่ากับ y หากรูปภาพเป็นสี่เหลี่ยมจัตุรัส แต่พล็อต dx และ dy ต่างกันแสดงว่าไม่ใช่อัตราส่วน 1: 1 อัตราส่วนภาพจะเป็น dy / dx ในกรณีนั้น
bart cubrich

22

อะไรคือสิ่งที่matplotlibรุ่นที่คุณกำลังทำงานอยู่หรือไม่ เมื่อเร็ว ๆ นี้ฉันต้องอัปเกรดเป็น1.1.0และด้วยมันก็add_subplot(111,aspect='equal')ใช้ได้กับฉัน


1
ทำงานได้ดีสำหรับฉันในรุ่นmatplotlib รุ่น. ขอบคุณ. 2.0.2jupyter notebook5.0.0
Sathish

5

หลังจากหลายปีแห่งความสำเร็จกับคำตอบข้างต้นฉันพบว่าสิ่งนี้จะไม่ได้ผลอีก - แต่ฉันพบวิธีแก้ปัญหาสำหรับพล็อตย่อยที่

https://jdhao.github.io/2017/06/03/change-aspect-ratio-in-mpl

ด้วยเครดิตเต็มรูปแบบสำหรับผู้เขียนข้างต้น (ซึ่งอาจจะค่อนข้างโพสต์ที่นี่) บรรทัดที่เกี่ยวข้องคือ:

ratio = 1.0
xleft, xright = ax.get_xlim()
ybottom, ytop = ax.get_ylim()
ax.set_aspect(abs((xright-xleft)/(ybottom-ytop))*ratio)

ลิงก์ยังมีคำอธิบายที่ชัดเจนเกี่ยวกับระบบพิกัดต่างๆที่ matplotlib ใช้

ขอบคุณสำหรับคำตอบที่ยอดเยี่ยมทั้งหมดที่ได้รับ - โดยเฉพาะ @ Yann ซึ่งจะยังคงเป็นผู้ชนะ


3

คุณควรลองใช้ figaspect มันใช้ได้กับฉัน จากเอกสาร:

สร้างภาพที่มีอัตราส่วนภาพที่ระบุ หากอาร์กิวเมนต์เป็นตัวเลขให้ใช้อัตราส่วนภาพนั้น > ถ้าargเป็นอาร์เรย์ figaspect จะกำหนดความกว้างและความสูงสำหรับรูปที่พอดีกับอาร์เรย์ที่รักษาอัตราส่วน แสดงความกว้างความสูงเป็นนิ้ว อย่าลืมสร้างแกนที่มีความสูงและความสูงเท่ากันเช่น

ตัวอย่างการใช้งาน:

  # make a figure twice as tall as it is wide
  w, h = figaspect(2.)
  fig = Figure(figsize=(w,h))
  ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
  ax.imshow(A, **kwargs)

  # make a figure with the proper aspect for an array
  A = rand(5,3)
  w, h = figaspect(A)
  fig = Figure(figsize=(w,h))
  ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
  ax.imshow(A, **kwargs)

แก้ไข: ฉันไม่แน่ใจในสิ่งที่คุณกำลังมองหา โค้ดด้านบนเปลี่ยนผ้าใบ (ขนาดพล็อต) หากคุณต้องการเปลี่ยนขนาดของหน้าต่าง matplotlib ของรูปให้ใช้:

In [68]: f = figure(figsize=(5,1))

สิ่งนี้จะสร้างหน้าต่าง 5x1 (wxh)


ขอบคุณสำหรับสิ่งนี้ - มันมีผลบางอย่างในการเปลี่ยนอัตราส่วนภาพของผืนผ้าใบ: เพื่อให้เฉพาะเจาะจงมากขึ้นฉันต้องเปลี่ยนอัตราส่วนของภาพซึ่งการทำสิ่งต่อไปนี้ไม่ได้ (การจัดรูปแบบ apols .. ): fig = plt.figure (figsize = (plt.figaspect (2.0)))
jtlz2

3

คำตอบนี้มาจากคำตอบของ Yann จะกำหนดอัตราส่วนภาพสำหรับพล็อตเชิงเส้นหรือบันทึกล็อก ฉันใช้ข้อมูลเพิ่มเติมจากhttps://stackoverflow.com/a/16290035/2966723เพื่อทดสอบว่าแกนเป็นล็อกสเกลหรือไม่

def forceAspect(ax,aspect=1):
    #aspect is width/height
    scale_str = ax.get_yaxis().get_scale()
    xmin,xmax = ax.get_xlim()
    ymin,ymax = ax.get_ylim()
    if scale_str=='linear':
        asp = abs((xmax-xmin)/(ymax-ymin))/aspect
    elif scale_str=='log':
        asp = abs((scipy.log(xmax)-scipy.log(xmin))/(scipy.log(ymax)-scipy.log(ymin)))/aspect
    ax.set_aspect(asp)

เห็นได้ชัดว่าคุณสามารถใช้เวอร์ชันใดก็ได้ที่logคุณต้องการฉันเคยใช้scipyแต่numpyหรือmathควรจะใช้ได้

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