Xlabel / ylabel phổ biến cho các ô con matplotlib


141

Tôi có cốt truyện sau:

fig,ax = plt.subplots(5,2,sharex=True,sharey=True,figsize=fig_size)

và bây giờ tôi muốn đưa ra biểu đồ này nhãn chung trục x và nhãn trục y. Với "chung", tôi muốn nói rằng nên có một nhãn trục x lớn bên dưới toàn bộ lưới của các ô con và một nhãn trục y lớn ở bên phải. Tôi không thể tìm thấy bất cứ điều gì về điều này trong tài liệu cho plt.subplots, và các nhân viên của tôi gợi ý rằng tôi cần phải làm cho lớn plt.subplot(111)để bắt đầu - nhưng làm thế nào để tôi đặt các ô con 5 * 2 của mình vào đó bằng cách sử dụng plt.subplots?


2
Với bản cập nhật cho câu hỏi và các bình luận để lại trong các câu trả lời bên dưới, đây là bản sao của stackoverflow.com/questions/6963035/
Lỗi

Không thực sự, vì câu hỏi của tôi là dành cho plt.subplots () và câu hỏi bạn liên kết để sử dụng add_subplot - Tôi không thể sử dụng phương thức đó trừ khi tôi chuyển sang add_subplot, điều mà tôi muốn tránh. Tôi có thể sử dụng giải pháp plt.text được đưa ra như một giải pháp thay thế trong liên kết của bạn, nhưng nó không phải là giải pháp thanh lịch nhất.
jolindbe

Để giải thích, theo như tôi hiểu, plt.subplots không thể tạo ra một tập hợp các ô con trong môi trường trục hiện có mà luôn tạo ra một hình mới. Đúng?
jolindbe

Một giải pháp tao nhã nhất có thể được tìm thấy ở đây: stackoverflow.com/questions/6963035/
Mr.H

Liên kết của bạn được cung cấp bởi người dùng Hooked hơn 4 năm trước (chỉ một vài bình luận ở trên của bạn). Như tôi đã nói trước đây, giải pháp đó liên quan đến add_subplot chứ không phải plt.subplots ().
jolindbe

Câu trả lời:


206

Điều này trông giống như những gì bạn thực sự muốn. Nó áp dụng cách tiếp cận tương tự của câu trả lời này cho trường hợp cụ thể của bạn:

import matplotlib.pyplot as plt

fig, ax = plt.subplots(nrows=3, ncols=3, sharex=True, sharey=True, figsize=(6, 6))

fig.text(0.5, 0.04, 'common X', ha='center')
fig.text(0.04, 0.5, 'common Y', va='center', rotation='vertical')

Nhiều ô có nhãn trục chung


4
lưu ý rằng 0,5 cho tọa độ x của nhãn x không đặt nhãn ở giữa của ô phụ ở giữa. bạn cần phải đi lớn hơn một chút để tính đến yticklabels.
dbliss

2
Hãy xem câu trả lời này cho một phương pháp không sử dụng plt.text. Bạn tạo các ô con của mình, nhưng sau đó thêm một ô bit, làm cho nó vô hình và gắn nhãn x và y của nó.
James Owers

Cảm ơn, làm việc nói chung. Bất kỳ giải pháp để phá vỡ khi sử dụng tight_layout?
phục vụ

3
@ serv-inc với tight_layoutthay thế 0.04bằng 0dường như làm việc.
divenex

3
Sử dụng fig.textkhông phải là một ý tưởng tốt. Điều này làm rối tung mọi thứ nhưplt.tight_layout()
Hòa bình

53

Vì tôi cho rằng nó phù hợp và đủ thanh lịch (không cần chỉ định tọa độ để đặt văn bản), tôi sao chép (với một chút thích ứng) một câu trả lời cho một câu hỏi liên quan khác .

import matplotlib.pyplot as plt
fig, axes = plt.subplots(5, 2, sharex=True, sharey=True, figsize=(6,15))
# add a big axis, hide frame
fig.add_subplot(111, frameon=False)
# hide tick and tick label of the big axis
plt.tick_params(labelcolor='none', top=False, bottom=False, left=False, right=False)
plt.xlabel("common X")
plt.ylabel("common Y")

Điều này dẫn đến kết quả sau (với phiên bản matplotlib 2.2.0):

Các ô con 5 hàng và 2 cột với nhãn trục x và y chung


3
Do đơn giản nên đây là câu trả lời được chấp nhận. Rất đơn giản. Vẫn phù hợp với matplotlib v3.x.
Kyle Swanson

Tôi muốn biết làm thế nào nó có thể được sử dụng với nhiều đối tượng hình? fig.xlabel ("foo") không hoạt động.
Vacui kinh dị

FYI: Bây giờ mọi người sử dụng các chủ đề tối trong StackOverflow, các nhãn hầu như không thể đọc được vì vậy tốt hơn là xuất png của bạn với nền trắng
xyzzyqed

@xyzzyqed Tôi không biết có một thứ như "chủ đề" trong stackoverflow và tôi thậm chí không nhớ mình đã xuất hình như thế nào. Làm cách nào để kiểm soát nền khi xuất?
bli

2
Vấn đề duy nhất của giải pháp này là nó không hoạt động khi sử dụng constrained_layout=Truevì nó tạo ra các nhãn chồng lấp. Trong trường hợp này, bạn phải điều chỉnh thủ công các đường viền của các ô con.
baccandr

35

Không có sharex=True, sharey=Truebạn nhận được:

nhập mô tả hình ảnh ở đây

Với nó, bạn sẽ làm cho nó đẹp hơn:

fig, axes2d = plt.subplots(nrows=3, ncols=3,
                           sharex=True, sharey=True,
                           figsize=(6,6))

for i, row in enumerate(axes2d):
    for j, cell in enumerate(row):
        cell.imshow(np.random.rand(32,32))

plt.tight_layout()

nhập mô tả hình ảnh ở đây

Nhưng nếu bạn muốn thêm nhãn bổ sung, bạn chỉ nên thêm chúng vào các ô cạnh:

fig, axes2d = plt.subplots(nrows=3, ncols=3,
                           sharex=True, sharey=True,
                           figsize=(6,6))

for i, row in enumerate(axes2d):
    for j, cell in enumerate(row):
        cell.imshow(np.random.rand(32,32))
        if i == len(axes2d) - 1:
            cell.set_xlabel("noise column: {0:d}".format(j + 1))
        if j == 0:
            cell.set_ylabel("noise row: {0:d}".format(i + 1))

plt.tight_layout()

nhập mô tả hình ảnh ở đây

Thêm nhãn cho mỗi ô sẽ làm hỏng nó (có thể có một cách để tự động phát hiện các nhãn lặp lại, nhưng tôi không biết về một nhãn).


Điều này khó hơn rất nhiều nếu, chẳng hạn, số lượng ô không xác định (ví dụ: bạn đã có một hàm tổng quát hóa hoạt động cho bất kỳ số lượng ô phụ nào).
ness101

15

Vì lệnh:

fig,ax = plt.subplots(5,2,sharex=True,sharey=True,figsize=fig_size)

bạn đã sử dụng trả về một tuple bao gồm hình và một danh sách các trường hợp trục, nó đã đủ để làm một cái gì đó như (nhớ rằng tôi đã thay đổi fig,axthành fig,axes):

fig,axes = plt.subplots(5,2,sharex=True,sharey=True,figsize=fig_size)

for ax in axes:
    ax.set_xlabel('Common x-label')
    ax.set_ylabel('Common y-label')

Nếu bạn tình cờ muốn thay đổi một số chi tiết trên một ô phụ cụ thể, bạn có thể truy cập nó thông qua axes[i]nơi ilặp lại trên các ô phụ của bạn.

Nó cũng có thể rất hữu ích để bao gồm một

fig.tight_layout()

ở cuối tập tin, trước plt.show(), để tránh các nhãn chồng chéo.


5
Tôi xin lỗi tôi đã không rõ ràng ở trên. Với "chung" tôi có nghĩa là một nhãn x đơn bên dưới tất cả các ô và một nhãn y ở bên trái các ô, tôi đã cập nhật câu hỏi để phản ánh điều này.
jolindbe

2
@JohanLindberg: Liên quan đến ý kiến ​​của bạn ở đây và ở trên: Thật vậy plt.subplots()sẽ tạo ra một ví dụ hình mới. Nếu bạn muốn gắn bó với lệnh này, bạn có thể dễ dàng thêm một big_ax = fig.add_subplot(111), vì bạn đã có một hình và có thể thêm một trục khác. Sau đó, bạn có thể thao tác big_axtheo cách nó được hiển thị trong liên kết từ Hooked.
Marius

Cảm ơn những lời đề nghị của bạn, nhưng nếu tôi làm điều đó, tôi phải thêm big_ax sau plt.subplots () và tôi có được phần phụ đó trên đầu mọi thứ khác - bằng cách nào đó tôi có thể minh bạch hoặc gửi nó ra phía sau không? Ngay cả khi tôi đặt tất cả các màu thành không như trong liên kết của Hooked, nó vẫn là một hộp màu trắng bao gồm tất cả các ô con của tôi.
jolindbe

2
@JohanLindberg, bạn nói đúng, tôi đã không kiểm tra điều đó. Nhưng bạn có thể dễ dàng đặt màu nền của trục lớn nonebằng cách thực hiện: big_ax.set_axis_bgcolor('none')Bạn cũng nên tạo nhãn màu none(trái ngược với ví dụ được liên kết bởi Hooked):big_ax.tick_params(labelcolor='none', top='off', bottom='off', left='off', right='off')
Marius

2
Tôi nhận được một lỗi: AttributeError: 'numpy.ndarray' object has no attribute 'set_xlabel'trong tuyên bố ax.set_xlabel('Common x-label'). Bạn có thể hình dung về nó?
hengxin

5

Nó sẽ trông tốt hơn nếu bạn dành chỗ cho các nhãn chung bằng cách tạo nhãn vô hình cho ô phụ ở góc dưới bên trái. Nó cũng là tốt để vượt qua trong các phông chữ từ RCParams. Bằng cách này, các nhãn chung sẽ thay đổi kích thước với thiết lập RC của bạn và các trục cũng sẽ được điều chỉnh để chừa không gian cho các nhãn chung.

fig_size = [8, 6]
fig, ax = plt.subplots(5, 2, sharex=True, sharey=True, figsize=fig_size)
# Reserve space for axis labels
ax[-1, 0].set_xlabel('.', color=(0, 0, 0, 0))
ax[-1, 0].set_ylabel('.', color=(0, 0, 0, 0))
# Make common axis labels
fig.text(0.5, 0.04, 'common X', va='center', ha='center', fontsize=rcParams['axes.labelsize'])
fig.text(0.04, 0.5, 'common Y', va='center', ha='center', rotation='vertical', fontsize=rcParams['axes.labelsize'])

nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây


1
Sử dụng tốt đẹp của các nhãn vô hình! Cảm ơn bạn
colelemonz

3

Tôi gặp phải một vấn đề tương tự trong khi vẽ một lưới đồ thị. Các biểu đồ bao gồm hai phần (trên và dưới). Nhãn y được cho là tập trung vào cả hai phần.

Tôi không muốn sử dụng một giải pháp phụ thuộc vào việc biết vị trí trong hình bên ngoài (như fig.text ()), vì vậy tôi đã thao tác vị trí y của hàm set_ylabel (). Nó thường là 0,5, phần giữa của cốt truyện được thêm vào. Vì phần đệm giữa các phần (hspace) trong mã của tôi bằng 0, tôi có thể tính được phần giữa của hai phần so với phần trên.

import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec

# Create outer and inner grid
outerGrid = gridspec.GridSpec(2, 3, width_ratios=[1,1,1], height_ratios=[1,1])
somePlot = gridspec.GridSpecFromSubplotSpec(2, 1,
               subplot_spec=outerGrid[3], height_ratios=[1,3], hspace = 0)

# Add two partial plots
partA = plt.subplot(somePlot[0])
partB = plt.subplot(somePlot[1])

# No x-ticks for the upper plot
plt.setp(partA.get_xticklabels(), visible=False)

# The center is (height(top)-height(bottom))/(2*height(top))
# Simplified to 0.5 - height(bottom)/(2*height(top))
mid = 0.5-somePlot.get_height_ratios()[1]/(2.*somePlot.get_height_ratios()[0])
# Place the y-label
partA.set_ylabel('shared label', y = mid)

plt.show()

hình ảnh

Nhược điểm:

  • Khoảng cách ngang đến cốt truyện dựa trên phần trên cùng, các dấu tick phía dưới có thể mở rộng vào nhãn.

  • Công thức không tính khoảng trống giữa các phần.

  • Ném một ngoại lệ khi chiều cao của phần trên cùng là 0.

Có lẽ có một giải pháp chung có tính đến phần đệm giữa các số liệu.


Này, tôi đã tìm ra một cách để làm điều này rất nhiều trong mạch câu trả lời của bạn, nhưng có thể giải quyết một số vấn đề này; xem stackoverflow.com/a/44020303/4970632 (bên dưới)
Luke Davis

2

Cập nhật:

Tính năng này hiện là một phần của gói matplotlib proplot mà tôi mới phát hành trên pypi. Theo mặc định, khi bạn tạo số liệu, các nhãn được "chia sẻ" giữa các trục.


Câu trả lời gốc:

Tôi phát hiện ra một phương pháp mạnh mẽ hơn:

Nếu bạn biết bottomvà các topkwarg đã bắt đầu GridSpeckhởi tạo hoặc bạn biết các vị trí cạnh của trục trong Figuretọa độ , bạn cũng có thể chỉ định vị trí ylabel trong Figuretọa độ bằng một số phép thuật "biến đổi" lạ mắt. Ví dụ:

import matplotlib.transforms as mtransforms
bottom, top = .1, .9
f, a = plt.subplots(nrows=2, ncols=1, bottom=bottom, top=top)
avepos = (bottom+top)/2
a[0].yaxis.label.set_transform(mtransforms.blended_transform_factory(
       mtransforms.IdentityTransform(), f.transFigure # specify x, y transform
       )) # changed from default blend (IdentityTransform(), a[0].transAxes)
a[0].yaxis.label.set_position((0, avepos))
a[0].set_ylabel('Hello, world!')

... Và bạn sẽ thấy rằng nhãn vẫn điều chỉnh một cách thích hợp từ trái sang phải để không bị chồng chéo với ticklabels, giống như bình thường - nhưng bây giờ nó sẽ điều chỉnh để luôn chính xác giữa các ô con mong muốn.

Hơn nữa, nếu bạn thậm chí không sử dụng set_position, ylabel sẽ hiển thị theo mặc định chính xác một nửa con số . Tôi đoán điều này là do khi nhãn cuối cùng được vẽ, matplotlibsử dụng 0,5 cho y-coordine mà không kiểm tra xem biến đổi tọa độ cơ bản có thay đổi hay không.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.