programing

Matplotlib의 하위 그림에서 행 및 열 머리글

codeshow 2023. 10. 24. 21:38
반응형

Matplotlib의 하위 그림에서 행 및 열 머리글

루프에서 생성된 하위 플롯의 그리드에 행과 열 헤더를 추가하는 가장 좋은 방법은 무엇입니까?matplotlib? 두 사람을 생각할 수는 있지만, 특별히 단정하지는 않습니다.

  1. 열의 경우 루프에 대한 카운터를 사용하여set_title()맨 앞줄만행의 경우에는 작동하지 않습니다.당신은 그림을 그려야 할 것입니다.text줄거리 밖에서
  2. 위에 부분구 행을 추가하고 왼쪽에 부분구 열을 추가한 다음 해당 부분구 중간에 텍스트를 그립니다.

더 좋은 대안을 제시해 주실 수 있습니까?

enter image description here

이렇게 하는 방법에는 여러 가지가 있습니다.쉬운 방법은 그림의 y 레이블과 제목을 이용한 다음 사용하는 것입니다.fig.tight_layout()라벨을 넣을 공간을 만들기 위해.또는 추가 텍스트를 올바른 위치에 배치한 다음 반수동으로 공간을 만들 수 있습니다.


축에 y라벨이 없으면 축의 첫 번째 행과 열의 제목과 y라벨을 쉽게 이용할 수 있습니다.

import matplotlib.pyplot as plt

cols = ['Column {}'.format(col) for col in range(1, 4)]
rows = ['Row {}'.format(row) for row in ['A', 'B', 'C', 'D']]

fig, axes = plt.subplots(nrows=4, ncols=3, figsize=(12, 8))

for ax, col in zip(axes[0], cols):
    ax.set_title(col)

for ax, row in zip(axes[:,0], rows):
    ax.set_ylabel(row, rotation=0, size='large')

fig.tight_layout()
plt.show()

enter image description here


y라벨이 있거나 조금 더 유연성이 좋다면 사용할 수 있습니다.annotate라벨을 붙일 수 있습니다.이것은 더 복잡하지만 행 및 열 레이블 외에도 개별 플롯 제목, ylabel 등을 가질 수 있습니다.

import matplotlib.pyplot as plt
from matplotlib.transforms import offset_copy


cols = ['Column {}'.format(col) for col in range(1, 4)]
rows = ['Row {}'.format(row) for row in ['A', 'B', 'C', 'D']]

fig, axes = plt.subplots(nrows=4, ncols=3, figsize=(12, 8))
plt.setp(axes.flat, xlabel='X-label', ylabel='Y-label')

pad = 5 # in points

for ax, col in zip(axes[0], cols):
    ax.annotate(col, xy=(0.5, 1), xytext=(0, pad),
                xycoords='axes fraction', textcoords='offset points',
                size='large', ha='center', va='baseline')

for ax, row in zip(axes[:,0], rows):
    ax.annotate(row, xy=(0, 0.5), xytext=(-ax.yaxis.labelpad - pad, 0),
                xycoords=ax.yaxis.label, textcoords='offset points',
                size='large', ha='right', va='center')

fig.tight_layout()
# tight_layout doesn't take these labels into account. We'll need 
# to make some room. These numbers are are manually tweaked. 
# You could automatically calculate them, but it's a pain.
fig.subplots_adjust(left=0.15, top=0.95)

plt.show()

enter image description here

Joe Kington의 답변을 바탕으로 코드 베이스 전체에서 재사용할 수 있는 함수를 올렸습니다.

인수는 다음과 같습니다.

  • fig: 작업할 축을 포함하는 그림
  • row_headers,col_headers: 머리말로 쓸 일련의 문자열
  • row_pad,col_pad:int패딩을 조정할 값
  • rotate_row_headers: 행 헤더를 90° 회전할지 여부
  • **text_kwargs: 에 전달된ax.annotate(...)

기능: 아래 예:

import numpy as np

def add_headers(
    fig,
    *,
    row_headers=None,
    col_headers=None,
    row_pad=1,
    col_pad=5,
    rotate_row_headers=True,
    **text_kwargs
):
    # Based on https://stackoverflow.com/a/25814386

    axes = fig.get_axes()

    for ax in axes:
        sbs = ax.get_subplotspec()

        # Putting headers on cols
        if (col_headers is not None) and sbs.is_first_row():
            ax.annotate(
                col_headers[sbs.colspan.start],
                xy=(0.5, 1),
                xytext=(0, col_pad),
                xycoords="axes fraction",
                textcoords="offset points",
                ha="center",
                va="baseline",
                **text_kwargs,
            )

        # Putting headers on rows
        if (row_headers is not None) and sbs.is_first_col():
            ax.annotate(
                row_headers[sbs.rowspan.start],
                xy=(0, 0.5),
                xytext=(-ax.yaxis.labelpad - row_pad, 0),
                xycoords=ax.yaxis.label,
                textcoords="offset points",
                ha="right",
                va="center",
                rotation=rotate_row_headers * 90,
                **text_kwargs,
            )

다음은 표준 그리드(축이 여러 행/콜에 걸쳐 있지 않음)에서 사용하는 예입니다.

import random
import matplotlib.pyplot as plt

mosaic = [
    ["A0", "A1", "A2"],
    ["B0", "B1", "B2"],
]
row_headers = ["Row A", "Row B"]
col_headers = ["Col 0", "Col 1", "Col 2"]

subplots_kwargs = dict(sharex=True, sharey=True, figsize=(10, 6))
fig, axes = plt.subplot_mosaic(mosaic, **subplots_kwargs)

font_kwargs = dict(fontfamily="monospace", fontweight="bold", fontsize="large")
add_headers(fig, col_headers=col_headers, row_headers=row_headers, **font_kwargs)

plt.show()

result: regular grid

일부 축이 여러 행/콜에 걸쳐 있는 경우 행/콜 헤더를 올바르게 할당하는 것이 약간 간단해집니다.기능 내부에서 잘 정리하지는 못했지만, 기본적으로 주의해야 합니다.row_headers그리고.col_headers인수를 사용하면 쉽게 작동할 수 있습니다.

mosaic = [
    ["A0", "A1", "A1", "A2"],
    ["A0", "A1", "A1", "A2"],
    ["B0", "B1", "B1", "B2"],
]

row_headers = ["A", "A", "B"]  # or
row_headers = ["A", None, "B"]  # or
row_headers = {0: "A", 2: "B"}

col_headers = ["0", "1", "1", "2"]  # or
col_headers = ["0", "1", None, "2"]  # or
col_headers = {0: "0", 1: "1", 3: "2"}

fig, axes = plt.subplot_mosaic(mosaic, **subplots_kwargs)
add_headers(fig, col_headers=col_headers, row_headers=row_headers, **font_kwargs)
plt.show()

result: non-regular grid

위의 답변이 효과가 있습니다.답변의 두 번째 버전에서는 다음과 같은 내용이 있습니다.

for ax, row in zip(axes[:,0], rows):
    ax.annotate(col, xy=(0, 0.5), xytext=(-ax.yaxis.labelpad-pad,0),
                xycoords=ax.yaxis.label, textcoords='offset points',
                size='large', ha='right', va='center')

대신:

for ax, row in zip(axes[:,0], rows):
    ax.annotate(row,xy=(0, 0.5), xytext=(-ax.yaxis.labelpad-pad,0),                    
                xycoords=ax.yaxis.label, textcoords='offset points',
                size='large', ha='right', va='center')

Joe의 답변의 연장선상에서 Y-label 접근법을 사용할 수 있습니다. 기존 Y-label을 사용하는 것보다 훨씬 쉽다고 생각합니다.annotate, 특히 축이 일반 그리드에 있지 않을 경우.

이를 위해 축을 으로 연결하여 왼쪽으로 이동할 수 있습니다.

import matplotlib.pyplot as plt

fig, axes = plt.subplots(nrows=3, ncols=3,
                         sharex=True, sharey=True)

cols = [1, 2, 3]
rows = ['A', 'B', 'C']

# set existing Y label
axes[0][0].set_ylabel('Y label')

for c, ax in zip(cols, axes[0]):
    ax.set_title(c, size='large')

for r, ax in zip(rows, axes[:, 0]):
    ax2 = ax.twinx()
    # move extra axis to the left, with offset
    ax2.yaxis.set_label_position('left')
    ax2.spines['left'].set_position(('axes', -0.4))
    # hide spine and ticks, set group label
    ax2.spines['left'].set_visible(False)
    ax2.set_yticks([])
    ax2.set_ylabel(r, rotation=0, size='large',
                   ha='right', va='center')

또는 기존 타이틀도 가지고 있다면 X축과 동일한 작업을 수행하여 상단에 레이블을 추가할 수도 있습니다.

출력:

matplotlib row column titles

비정규 그리드의 출력:

matplotlib row columns titles non regular grid

언급URL : https://stackoverflow.com/questions/25812255/row-and-column-headers-in-matplotlibs-subplots

반응형