Bagaimana cara mengatur rasio aspek di matplotlib?

108

Saya mencoba membuat plot persegi (menggunakan imshow), yaitu rasio aspek 1: 1, tetapi saya tidak bisa. Tak satu pun dari pekerjaan ini:

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

Sepertinya panggilan hanya diabaikan (masalah yang sering saya alami dengan matplotlib).

jtlz2.dll
sumber
6
Apakah Anda mencoba ax.axis('equal'), secara kebetulan? Seperti yang dikatakan semua orang, apa yang Anda lakukan harus berhasil, tetapi ax.axismungkin merupakan cara lain untuk mencoba mencari solusi.
Joe Kington

Jawaban:

77

Ketiga kali pesona. Dugaan saya ini adalah bug dan jawaban Zhenya menyarankan itu diperbaiki di versi terbaru. Saya memiliki versi 0.99.1.1 dan saya telah membuat solusi berikut:

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

Ini adalah 'force.png': masukkan deskripsi gambar di sini

Di bawah ini adalah upaya saya yang gagal, namun semoga informatif.

Jawaban Kedua:

'Jawaban asli' saya di bawah ini berlebihan, karena mirip dengan axes.set_aspect(). Saya pikir Anda ingin menggunakan axes.set_aspect('auto'). Saya tidak mengerti mengapa ini terjadi, tetapi menghasilkan plot gambar persegi untuk saya, misalnya skrip ini:

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

Menghasilkan plot gambar dengan rasio aspek 'sama': masukkan deskripsi gambar di sini dan plot dengan rasio aspek 'otomatis': masukkan deskripsi gambar di sini

Kode yang diberikan di bawah ini dalam 'jawaban asli' memberikan titik awal untuk rasio aspek yang dikontrol secara eksplisit, tetapi tampaknya diabaikan begitu imshow dipanggil.

Jawaban Asli:

Berikut adalah contoh rutinitas yang akan menyesuaikan parameter subplot sehingga Anda mendapatkan rasio aspek yang diinginkan:

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

Ini menghasilkan sosok seperti: masukkan deskripsi gambar di sini

Saya dapat membayangkan jika Anda memiliki beberapa subplot dalam gambar, Anda ingin memasukkan jumlah subplot y dan x sebagai parameter kata kunci (masing-masing default ke 1) ke rutin yang disediakan. Kemudian menggunakan angka-angka dan hspacedan wspacekata kunci, Anda dapat membuat semua subplot memiliki aspek rasio yang benar.

Yann
sumber
1
Untuk kasus di mana get_imagesada daftar kosong (seperti yang akan terjadi dengan ax.plot([0,1],[0,2]), Anda dapat menggunakan get_xlimdanget_ylim
Joel
Menurut saya ini tidak akan berhasil jika dilakukan dengan logscale. Saya telah menambahkan jawaban yang menguji untuk itu dan menanganinya. Jangan ragu untuk memasukkannya ke dalam jawaban Anda dan kemudian saya akan menghapus milik saya.
Yoel
1
Alasan aspek terlihat tidak sama adalah karena aspek yang sama berarti jarak visual pada x akan sama dengan y. Jika gambar berbentuk persegi, tetapi plot dx dan dy berbeda, maka itu bukan rasio aspek 1: 1. Rasio aspek akan menjadi dy / dx dalam kasus tersebut.
bart cubrich
22

Apa matplotlibversi yang Anda jalankan? Saya baru-baru ini harus meningkatkan ke 1.1.0, dan dengan itu, add_subplot(111,aspect='equal')berfungsi untuk saya.

ev-br
sumber
1
Bekerja dengan baik untuk saya dalam matplotlibversi 2.0.2. jupyter notebookversi 5.0.0. Terima kasih.
Sathish
5

Setelah bertahun-tahun sukses dengan jawaban di atas, saya merasa ini tidak berfungsi lagi - tetapi saya menemukan solusi yang berfungsi untuk subplot di

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

Dengan kredit penuh tentu saja kepada penulis di atas (yang mungkin lebih suka memposting di sini), baris yang relevan adalah:

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

Tautan juga memiliki penjelasan yang sangat jelas tentang sistem koordinat berbeda yang digunakan oleh matplotlib.

Terima kasih atas semua jawaban bagus yang diterima - terutama @ Yann yang akan tetap menjadi pemenang.

jtlz2.dll
sumber
3

Anda harus mencoba dengan figaspect. Ini bekerja untuk saya. Dari dokumen:

Buat gambar dengan rasio aspek yang ditentukan. Jika arg adalah angka, gunakan rasio aspek tersebut. > Jika arg adalah larik, figaspect akan menentukan lebar dan tinggi untuk gambar yang sesuai dengan rasio aspek pelestarian larik. Lebar gambar, tinggi dalam inci dikembalikan. Pastikan untuk membuat sumbu dengan tinggi dan sama, misalnya

Contoh penggunaan:

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

Sunting: Saya tidak yakin dengan apa yang Anda cari. Kode di atas mengubah kanvas (ukuran plot). Jika Anda ingin mengubah ukuran jendela matplotlib, dari gambar, gunakan:

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

ini menghasilkan jendela 5x1 (wxh).

joaquin
sumber
Terima kasih untuk ini - ini memang berpengaruh, dalam mengubah rasio aspek kanvas: Untuk lebih spesifik, saya perlu mengubah rasio aspek dari gambar itu sendiri, yang tidak melakukan hal berikut (format apols ..): ara = plt.figure (figsize = (plt.figaspect (2.0)))
jtlz2
3

Jawaban ini berdasarkan jawaban Yann. Ini akan mengatur rasio aspek untuk plot linier atau log-log. Saya telah menggunakan informasi tambahan dari https://stackoverflow.com/a/16290035/2966723 untuk menguji apakah sumbu berskala log.

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)

Jelas Anda dapat menggunakan versi apa pun yang logAnda inginkan, saya telah menggunakan scipy, tetapi numpyatau mathseharusnya baik-baik saja.

Joel
sumber