【Matplotlib】table.CustomCellで実現する!データ可視化のためのセルカスタマイズ

2025-05-31

Matplotlibで表を作成する場合、通常は matplotlib.pyplot.table() 関数を使用します。この関数は、内部的に matplotlib.table.Table オブジェクトを作成し、さらにその Table オブジェクトが Cell オブジェクト(または CustomCell オブジェクト)のグリッドで構成されています。

CustomCell は、各セルの枠線(罫線)を個別に設定したい場合に特に役立ちます。例えば、特定のセルの下線だけを表示したり、右線だけを非表示にしたりといったカスタマイズが可能です。

  1. matplotlib.table.Cell の拡張: CustomCellCell クラスを継承しています。そのため、Cell が持つ xy (位置)、width (幅)、height (高さ)、edgecolor (枠線の色)、facecolor (背景色)、text (表示テキスト)、loc (テキストの配置) などのプロパティをすべて持っています。

  2. visible_edges プロパティ: CustomCell の最も重要な特徴は visible_edges プロパティです。これにより、セルのどの辺を表示するかを制御できます。

    • 'closed' (デフォルト): すべての辺を表示します。
    • 'open': すべての辺を非表示にします。
    • 'horizontal': 上下の辺のみ表示します。
    • 'vertical': 左右の辺のみ表示します。
    • 'BRTL' のサブ文字列: 'B' (下), 'R' (右), 'T' (上), 'L' (左) の任意の組み合わせで辺を指定できます。例えば、'BL' と指定すると下と左の辺が表示されます。
  3. 直接操作: 通常、ユーザーが CustomCell オブジェクトを直接作成することはほとんどありません。matplotlib.pyplot.table()Table.add_cell() 関数を使うと、自動的に CustomCell インスタンスが作成されます。作成されたテーブルオブジェクトから個々のセルを取得し、その visible_edges などのプロパティを変更することで、見た目をカスタマイズできます。

    import matplotlib.pyplot as plt
    
    fig, ax = plt.subplots(figsize=(6, 2))
    ax.axis('off') # グラフの軸を非表示にする
    
    data = [['A', 'B', 'C'],
            ['1', '2', '3'],
            ['X', 'Y', 'Z']]
    
    table = ax.table(cellText=data,
                     loc='center',
                     cellLoc='center')
    
    # 特定のセルを取得してカスタマイズする例
    # table[行インデックス, 列インデックス] でセルにアクセスできる
    cell_0_0 = table[0, 0]
    cell_0_0.set_facecolor('lightgray') # 背景色を変更
    
    cell_1_1 = table[1, 1]
    cell_1_1.visible_edges = 'B' # 下の辺のみ表示
    
    cell_2_2 = table[2, 2]
    cell_2_2.set_text('Custom') # テキストを変更
    cell_2_2.set_fontsize(14) # フォントサイズを変更
    cell_2_2.set_edgecolor('red') # 枠線の色を変更
    cell_2_2.visible_edges = 'open' # すべての辺を非表示
    
    plt.show()
    


セルが期待通りに表示されない(見切れる、サイズがおかしい)

原因:

  • セルの幅/高さの調整不足: table.auto_set_column_width(ax)table.auto_set_font_size(False) などを使っていない、または手動での幅/高さ調整が不十分な場合。
  • 自動レイアウトの問題: Matplotlibの自動レイアウト (fig.tight_layout()) は、テーブルを含む複雑なレイアウトでは意図通りに機能しないことがあります。
  • Figure/Axesのサイズ不足: テーブル全体や個々のセルが大きすぎると、FigureやAxesの描画領域からはみ出して見切れることがあります。特に、多くの行や列を持つテーブルの場合に発生しやすいです。

トラブルシューティング:

  • bbox パラメータの活用: ax.table()bbox パラメータを使用して、Axes内でのテーブルの位置とサイズを明示的に指定します。例えば、bbox=[0, 0, 1, 1] はAxes全体を使用します。
  • 手動でのセルサイズの調整:
    • table.auto_set_column_width(ax) を呼び出して、各列の幅を自動調整させます。
    • 個々のセルの幅や高さを cell.set_width(w)cell.set_height(h) で手動で調整します。
  • fig.tight_layout() の試用: プロットとテーブルの間に十分なスペースを確保するために、plt.show() の前に fig.tight_layout() を呼び出してみてください。ただし、常に万能ではありません。
  • Figureサイズの調整: plt.figure(figsize=(width, height))fig.set_size_inches(width, height) で、Figureのサイズを十分に大きく設定します。

visible_edges の設定が反映されない

原因:

  • Cell オブジェクトへの変更: CustomCell ではなく、基底クラスの Cell オブジェクトにアクセスして visible_edges を変更しようとしている(Cell クラスにはこのプロパティがないため、AttributeError が発生する)。table.CustomCelltable.Cell のサブクラスであり、visible_edges プロパティは CustomCell に固有のものです。ax.table() で作成されるセルは通常 CustomCell ですが、もし自作のセルクラスを使用している場合は注意が必要です。
  • 誤った値の指定: visible_edges プロパティには特定の文字列 ('B', 'R', 'T', 'L' の組み合わせ、または 'open', 'closed', 'horizontal', 'vertical') を指定する必要があります。スペルミスや認識されない値を指定すると、デフォルトの挙動に戻るか、エラーが発生します。

トラブルシューティング:

  • CustomCell オブジェクトへのアクセス確認: 確実に matplotlib.table.CustomCell インスタンスの visible_edges を操作しているかを確認します。通常、table[row_idx, col_idx] で取得されるセルは CustomCell ですが、念のため isinstance(cell, matplotlib.table.CustomCell) で確認するのも良いでしょう。
  • 正確な文字列の確認: visible_edges に指定する文字列が Matplotlib のドキュメントに記載されている正しい値であることを確認します。

テキストの配置やフォントサイズが意図しない

原因:

  • フォントサイズの自動調整: ax.table()auto_set_font_size=True (デフォルト) が有効になっていると、テーブル全体に収まるようにフォントサイズが自動調整され、小さくなりすぎることがあります。
  • デフォルトの配置: セル内のテキストの配置はデフォルトで中央揃えなどになっています。

トラブルシューティング:

  • cellLoc パラメータ: ax.table()cellLoc パラメータで、デフォルトのセル内のテキスト配置を指定できます(例: cellLoc='left')。
  • auto_set_font_size の無効化: テーブル作成時に auto_set_font_size=False を指定し、フォントサイズを手動で設定できるようにします。
  • cell.set_text_props() の使用: cell.set_text_props(ha='left', va='center', fontsize=12) のように、ha (horizontal alignment) や va (vertical alignment)、fontsize を設定してテキストの配置やサイズを個別に調整します。

テーブルの行や列の追加/削除が難しい

原因:

  • 静的な性質: Matplotlibのテーブルは、一度作成されると動的に行や列を追加・削除する機能が直接的に提供されていません。これはデータフレームのような柔軟なデータ構造とは異なります。

トラブルシューティング:

  • add_cell() の活用: 既存のテーブルに新しいセルを追加したい場合は、table.add_cell(row, col, width, height, text_label) メソッドを使用できます。ただし、既存のセルの位置やサイズとの兼ね合いを考慮する必要があります。
  • テーブルの再構築: 行や列の構造が大きく変わる場合は、新しいデータでテーブルを再構築するのが最も簡単な方法です。

AttributeError が発生する

原因:

  • 存在しないプロパティへのアクセス: CustomCell オブジェクトに存在しないプロパティやメソッドにアクセスしようとすると発生します。例えば、cell.set_line_width の代わりに cell.linewidth のように直接設定しようとするなど。

トラブルシューティング:

  • 正しいセッターの使用: 多くのプロパティは set_ プレフィックスの付いたセッターメソッド(例: set_facecolor(), set_edgecolor(), set_linewidth(), set_text_props())を通じて設定する必要があります。
  • Matplotlib ドキュメントの参照: 使用しようとしているプロパティやメソッドが実際に CustomCell クラスに存在するか、その正しい使用方法を確認するために、Matplotlibの公式ドキュメントを参照します。
  • エラーメッセージの確認: Pythonのエラーメッセージ(トレースバック)は、問題の箇所と種類を示す重要な情報源です。注意深く読み、理解するように努めてください。
  • 最小限の再現可能な例 (MRE): エラーが発生した場合、問題のコードを最小限に絞り込んだ再現可能な例を作成してみてください。これにより、問題を特定しやすくなります。
  • バージョン確認: 使用しているMatplotlibのバージョンを確認し、最新版にアップデートすることを検討してください。古いバージョンでは問題が解決されている可能性があります。


例1: 基本的なテーブルの作成と特定のセルのカスタマイズ

この例では、基本的なテーブルを作成し、特定のセルの背景色、テキスト、および枠線を変更します。

import matplotlib.pyplot as plt
import matplotlib.table as mtable # mtable をインポートしておく

# FigureとAxesを作成
fig, ax = plt.subplots(figsize=(7, 3))

# グラフの軸を非表示にする(テーブルを見やすくするため)
ax.axis('off')
ax.set_title("Customizing Individual Cells")

# テーブルのデータ
data = [['Header A', 'Header B', 'Header C'],
        ['Row 1, Col 1', 'Row 1, Col 2', 'Row 1, Col 3'],
        ['Row 2, Col 1', 'Row 2, Col 2', 'Row 2, Col 3']]

# テーブルの作成
# cellLoc='center' はテキストをセルの中央に配置
# loc='center' はテーブル全体をAxesの中央に配置
table = ax.table(cellText=data,
                 cellLoc='center',
                 loc='center',
                 colLabels=None) # ヘッダー行をデータに含めたので、colLabelsはNone

# 各セルのオブジェクトを取得し、CustomCellであることを確認
# table[行インデックス, 列インデックス] でセルにアクセス
print(f"Type of cell at [0, 0]: {type(table[0, 0])}") # CustomCell であることを確認

# --- セルのカスタマイズ ---

# [0, 0] のセル(左上のヘッダー)をカスタマイズ
cell_0_0 = table[0, 0]
cell_0_0.set_facecolor('lightgray') # 背景色
cell_0_0.set_text_props(weight='bold') # テキストを太字に

# [1, 1] のセル(中央のデータセル)をカスタマイズ
cell_1_1 = table[1, 1]
cell_1_1.set_facecolor('lightblue')
cell_1_1.set_text_props(color='darkblue', fontsize=12)
cell_1_1.set_edgecolor('red') # 枠線の色
cell_1_1.set_linewidth(2) # 枠線の太さ

# [2, 0] のセルをカスタマイズ(テキスト変更と背景色)
cell_2_0 = table[2, 0]
cell_2_0.set_text('Modified Text')
cell_2_0.set_facecolor('lightgreen')

# テーブルの自動レイアウト(列幅の調整)
table.auto_set_column_width(ax)

# レイアウトの調整
fig.tight_layout()

plt.show()

例2: visible_edges を使用してセルの枠線を制御する

この例では、CustomCellvisible_edges プロパティを使って、セルの罫線を部分的に表示したり非表示にしたりする方法を示します。

import matplotlib.pyplot as plt
import matplotlib.table as mtable

fig, ax = plt.subplots(figsize=(8, 4))
ax.axis('off')
ax.set_title("Controlling Cell Edges with visible_edges")

data = [['Value 1,1', 'Value 1,2', 'Value 1,3'],
        ['Value 2,1', 'Value 2,2', 'Value 2,3'],
        ['Value 3,1', 'Value 3,2', 'Value 3,3']]

table = ax.table(cellText=data,
                 cellLoc='center',
                 loc='center')

# --- visible_edges のカスタマイズ ---

# [0, 0] のセル: 下と右の辺だけ表示 ('BR' = Bottom, Right)
cell_0_0 = table[0, 0]
cell_0_0.visible_edges = 'BR'
cell_0_0.set_facecolor('lightyellow')

# [0, 1] のセル: 上の辺だけ表示 ('T' = Top)
cell_0_1 = table[0, 1]
cell_0_1.visible_edges = 'T'
cell_0_1.set_facecolor('lightgray')

# [1, 0] のセル: すべての辺を非表示 ('open')
cell_1_0 = table[1, 0]
cell_1_0.visible_edges = 'open'
cell_1_0.set_facecolor('pink')

# [1, 2] のセル: 左右の辺だけ表示 ('vertical')
cell_1_2 = table[1, 2]
cell_1_2.visible_edges = 'vertical'
cell_1_2.set_facecolor('lavender')

# [2, 1] のセル: すべての辺を表示 (デフォルトと同じだが明示的に 'closed')
cell_2_1 = table[2, 1]
cell_2_1.visible_edges = 'closed'
cell_2_1.set_facecolor('lightcyan')
cell_2_1.set_edgecolor('blue') # 枠線の色も変更

# テーブルの自動レイアウト(列幅の調整)
table.auto_set_column_width(ax)
fig.tight_layout()

plt.show()

例3: 部分的に罫線がない表を作成する

visible_edges を応用して、結合セルがないものの、罫線が部分的にないような表を表現する例です。

import matplotlib.pyplot as plt
import matplotlib.table as mtable

fig, ax = plt.subplots(figsize=(8, 4))
ax.axis('off')
ax.set_title("Creating a Table with Partial Borders")

data = [['A', 'B', 'C'],
        ['1', '2', '3'],
        ['X', 'Y', 'Z']]

table = ax.table(cellText=data,
                 cellLoc='center',
                 loc='center',
                 edges='open') # まず全てのセルを枠線なしで作成

# --- 罫線を部分的に追加 ---

# 全てのセルに対して、下と右の線を引く (簡易的なグリッド)
num_rows, num_cols = len(data), len(data[0])
for r in range(num_rows):
    for c in range(num_cols):
        cell = table[r, c]
        cell.set_edgecolor('black') # 線の色
        cell.set_linewidth(1) # 線の太さ

        # 行の最後のセルでなければ右線を追加
        if c < num_cols - 1:
            cell.visible_edges += 'R'
        # 列の最後のセルでなければ下線を追加
        if r < num_rows - 1:
            cell.visible_edges += 'B'

# ヘッダー行の上の線を引く
for c in range(num_cols):
    cell = table[0, c]
    cell.visible_edges += 'T' # 上線を追加

# 左端列の左の線を引く
for r in range(num_rows):
    cell = table[r, 0]
    cell.visible_edges += 'L' # 左線を追加

# 特定のセル (例: [1,1]) の周りに太い枠線を引く
cell_1_1 = table[1, 1]
cell_1_1.visible_edges = 'closed' # 全ての辺を表示
cell_1_1.set_edgecolor('red')
cell_1_1.set_linewidth(2)

# テーブルの自動レイアウト(列幅の調整)
table.auto_set_column_width(ax)
fig.tight_layout()

plt.show()
  • ax.axis('off'): テーブル以外のグラフ要素(軸の目盛りやラベル)を非表示にして、テーブルをきれいに表示するためによく使用されます。
  • auto_set_column_width(ax): テーブル作成後にこのメソッドを呼び出すことで、各列の幅が内容に合わせて自動調整され、見栄えが良くなります。
  • visible_edges の値: 'B', 'R', 'T', 'L' の組み合わせ、または 'open', 'closed', 'horizontal', 'vertical' を指定します。これらを文字列として結合することで、複数の辺を指定できます(例: 'BL' で下と左の辺)。
  • プロパティの変更: 取得した CustomCell オブジェクトのプロパティ(facecolor, edgecolor, linewidth, visible_edges など)を直接設定するか、set_text_props() のようなセッターメソッドを使って変更します。
  • table[row_idx, col_idx] でセルにアクセス: ax.table() で作成されたテーブルオブジェクトから、インデックスを使って個々の CustomCell オブジェクトを取得します。


matplotlib.table.Cell (基底クラス)の直接操作

CustomCellCell クラスのサブクラスです。visible_edges のような CustomCell 特有の機能が必要ない場合、あるいは単に背景色やテキストなどを変更するだけであれば、Cell オブジェクトを直接操作しても問題ありません。ax.table() で生成されるセルは通常 CustomCell なので、意識せずに CustomCell の機能を使っていることが多いですが、概念的には Cell のレベルでのカスタマイズも可能です。

  • デメリット: visible_edges のような高度な罫線制御ができない。
  • メリット: コードがシンプルになる場合がある(CustomCell 特有のプロパティを意識する必要がないため)。
  • 用途: 基本的な背景色、枠線の色、テキスト内容、フォントサイズなどの変更。
import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(6, 2))
ax.axis('off')

data = [['A', 'B'], ['C', 'D']]
table = ax.table(cellText=data, loc='center')

# Cell オブジェクトのプロパティを変更
cell_0_0 = table[0, 0]
cell_0_0.set_facecolor('lightcoral') # 背景色
cell_0_0.set_text_props(color='white') # テキスト色
cell_0_0.set_edgecolor('darkred') # 枠線の色
cell_0_0.set_linewidth(2) # 枠線の太さ

plt.show()

matplotlib.axes.Axes.text と matplotlib.patches.Rectangle を組み合わせる

これは Matplotlib のプリミティブな要素を組み合わせてテーブルを「手動で」描画する方法です。各セルを個別のテキストオブジェクトと四角形(矩形)パッチで表現します。これは非常に柔軟ですが、コード量は多くなります。

  • デメリット: コードが冗長になり、位置計算が煩雑になる。自動的な列幅調整やフォントサイズ調整が難しい。
  • メリット: 究極の柔軟性。Matplotlibの描画機能を最大限に活用できる。
  • 用途: 非常に複雑なセルの結合、不規則なセル配置、グラフィカルな要素(アイコンなど)をセル内に配置したい場合。
import matplotlib.pyplot as plt
import matplotlib.patches as patches

fig, ax = plt.subplots(figsize=(7, 4))
ax.set_xlim(0, 10)
ax.set_ylim(0, 5)
ax.axis('off')

# セルのサイズと開始位置
cell_width = 2
cell_height = 1
start_x = 1
start_y = 3

data = [['Header', 'Col1', 'Col2'],
        ['Row1', 'Val1', 'Val2'],
        ['Row2', 'Val3', 'Val4']]

for r_idx, row_data in enumerate(data):
    for c_idx, cell_text in enumerate(row_data):
        x = start_x + c_idx * cell_width
        y = start_y - r_idx * cell_height

        # 矩形パッチでセルを描画
        rect = patches.Rectangle((x, y), cell_width, cell_height,
                                 facecolor='lightgray' if r_idx == 0 else 'white',
                                 edgecolor='black',
                                 linewidth=1)
        ax.add_patch(rect)

        # テキストを配置
        ax.text(x + cell_width / 2, y + cell_height / 2, cell_text,
                ha='center', va='center', fontsize=12)

# 特定のセル(例えば 'Val1')のスタイルを変更
val1_x = start_x + 1 * cell_width
val1_y = start_y - 1 * cell_height
rect_val1 = patches.Rectangle((val1_x, val1_y), cell_width, cell_height,
                              facecolor='lightblue',
                              edgecolor='red',
                              linewidth=2)
ax.add_patch(rect_val1)
ax.text(val1_x + cell_width / 2, val1_y + cell_height / 2, 'Val1',
        ha='center', va='center', fontsize=12, color='darkblue')

plt.show()

pandas.DataFrame と matplotlib.pyplot.table の連携

データが Pandas DataFrame の形式で存在する場合、matplotlib.pyplot.table() は DataFrame を直接受け取ることができます。この組み合わせは、データ整理と可視化を効率的に行いたい場合に非常に強力です。セルのカスタマイズは table.CustomCell を通して行います。

  • デメリット: CustomCell のカスタマイズ自体は別途行う必要がある。
  • メリット: Pandasのデータ操作能力を活用できる。データの整形が容易。
  • 用途: 表形式のデータを扱う際に、データ処理と可視化を統合したい場合。
import matplotlib.pyplot as plt
import pandas as pd

fig, ax = plt.subplots(figsize=(8, 4))
ax.axis('off')

# Pandas DataFrame を作成
data = {'Category A': ['Item 1', 'Item 2', 'Item 3'],
        'Category B': [100, 150, 120],
        'Category C': [True, False, True]}
df = pd.DataFrame(data)

# DataFrame からテーブルを作成
table = ax.table(cellText=df.values,
                 colLabels=df.columns, # DataFrameの列名をヘッダーに
                 rowLabels=df.index, # DataFrameのインデックスを行ラベルに
                 loc='center',
                 cellLoc='center')

# ヘッダー行をカスタマイズ
for (r, c), cell in table.cells.items():
    if r == 0: # ヘッダー行
        cell.set_facecolor('lightgray')
        cell.set_text_props(weight='bold', color='darkslategray')
    if c == -1: # 行ラベル(インデックス)
        cell.set_facecolor('lightsteelblue')
        cell.set_text_props(weight='bold')

# 特定のデータセルをカスタマイズ(例: 'Category B' の値が 150 のセル)
# データのインデックスと列のインデックスが重要
# df.values はヘッダーなしのデータなので、行インデックスはデータ行に合わせる
# 列インデックスは colLabels なので、0:Category A, 1:Category B, 2:Category C
cell_item_2_category_b = table[2, 1] # 2行目1列目 (0-indexed)
cell_item_2_category_b.set_facecolor('lightgreen')
cell_item_2_category_b.set_text_props(color='green', weight='bold')
cell_item_2_category_b.visible_edges = 'closed' # 念のため枠線を表示

table.auto_set_column_width(ax)
fig.tight_layout()
plt.show()

Matplotlib 以外にも、Python で表を作成・描画するためのライブラリはいくつか存在します。

  • ReportLab: PDF ドキュメントを生成するためのライブラリで、PDF 内に複雑なテーブルをレイアウトする機能があります。
  • Webフレームワーク (e.g., Dash, Streamlit): インタラクティブなWebアプリケーションでテーブルを表示する場合、これらのフレームワークが提供するテーブルコンポーネント(例: Dashの dash_table.DataTable)を使用する方が、動的なソート、フィルタリング、ページネーションなどが容易になります。これらは通常、HTML/CSS/JavaScriptベースで描画されます。
  • Pillow (PIL) + 描画ライブラリ: 画像ファイルとしてテーブルを生成したい場合、Pillow と組み合わせて直接ピクセルを操作したり、テキストを描画したりする方法があります。これは低レベルな制御が必要な場合に適しています。
  • PrettyTable: テキストベースのテーブル(コンソール出力用)に特化しており、簡単な表をターミナルに表示するのに便利です。グラフィカルな出力はできません。