Pandasで表をHTML化:to_htmlの基本から応用、スタイリングまで

2025-05-27

このメソッドを使うと、Pythonで操作している表形式のデータを、Webページに表示したり、HTML形式のレポートとして出力したりする際に非常に便利です。

以下に主な特徴と使い方を説明します。

主な特徴

  • Web表示に最適
    Jupyter Notebookなどの環境では、このメソッドの結果が直接HTMLとしてレンダリングされるため、データの視覚的な確認に役立ちます。
  • 柔軟な出力
    • 結果を文字列として返すか、ファイルオブジェクトに直接書き込むことができます。
    • 特定の列のみをHTMLに含めることができます。
    • ヘッダーやインデックス(行名)を表示するかどうかを制御できます。
    • 欠損値(NaN)の表示方法を指定できます。
    • 数値の表示形式や、各列の要素に対するカスタムフォーマッター(書式設定関数)を適用できます。
    • テーブルのCSSクラスを指定して、スタイルを適用することも可能です。
  • HTMLテーブルへの変換
    DataFrameの行と列を、標準的なHTMLの<table>タグを使って表現します。

基本的な使い方

最もシンプルな使い方は、DataFrameオブジェクトに対してto_html()を呼び出すだけです。

import pandas as pd

# サンプルDataFrameを作成
data = {'Name': ['Alice', 'Bob', 'Charlie'],
        'Age': [25, 30, 35],
        'City': ['New York', 'London', 'Paris']}
df = pd.DataFrame(data)

# DataFrameをHTML文字列に変換
html_table = df.to_html()
print(html_table)

上記のコードを実行すると、以下のようなHTML文字列が出力されます。

<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>Name</th>
      <th>Age</th>
      <th>City</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>Alice</td>
      <td>25</td>
      <td>New York</td>
    </tr>
    <tr>
      <th>1</th>
      <td>Bob</td>
      <td>30</td>
      <td>London</td>
    </tr>
    <tr>
      <th>2</th>
      <td>Charlie</td>
      <td>35</td>
      <td>Paris</td>
    </tr>
  </tbody>
</table>

このHTML文字列をWebページに埋め込んだり、HTMLファイルとして保存したりすることで、ブラウザでDataFrameの内容をテーブルとして表示できます。

よく使われるオプション

  • classes: 生成される<table>タグに適用するCSSクラス名(文字列または文字列のリスト)。これにより、HTMLにスタイルを適用しやすくなります。
  • formatters: 各列の要素に適用するカスタムフォーマッター関数を辞書またはリストで指定します。
  • float_format: 浮動小数点数の書式を設定するPython書式文字列(例: %.2f)。
  • na_rep: 欠損値(NaN)を置き換える文字列。デフォルトは'NaN'です。
  • index: True(デフォルト)の場合、DataFrameのインデックス(行名)をテーブルの最初の列として表示します。Falseの場合、表示しません。
  • header: True(デフォルト)の場合、列名をヘッダーとして表示します。Falseの場合、表示しません。
  • columns: 表示したい列名のリスト。指定しない場合、すべての列が表示されます。
  • buf: 結果を書き込むファイルパスまたはファイルライクなオブジェクト。指定しない場合、文字列として返されます。
import pandas as pd

data = {'Product': ['A', 'B', 'C', 'D'],
        'Price': [10.5, 20.75, 15.0, 5.25],
        'Stock': [100, 50, 0, 200]}
df = pd.DataFrame(data)

# インデックスなし、価格を小数点以下2桁、在庫が0の場合は「SOLD OUT」と表示、CSSクラスを追加
html_output_custom = df.to_html(
    index=False,
    float_format='%.2f',
    formatters={'Stock': lambda x: 'SOLD OUT' if x == 0 else str(x)},
    classes='my-dataframe-table'
)
print(html_output_custom)


TypeError や AttributeError

これは、to_html() メソッドを呼び出そうとしているオブジェクトが実際にはDataFrameではない場合に発生することがあります。

考えられる原因

  • スペルミス
    to_html のスペルを間違えている場合。
  • オブジェクトがDataFrameではない
    変数に格納されているのが、Series、リスト、NumPy配列など、DataFrame以外のオブジェクトである場合。

トラブルシューティング

  • スペルを再確認する
    メソッド名が正しく to_html となっているか確認します。
  • 変数名を確認する
    正しいDataFrameオブジェクトに対してメソッドを呼び出しているか確認します。
  • オブジェクトの型を確認する
    type(your_variable) を実行して、それが pandas.DataFrame であることを確認します。

formatters オプションでの問題(特に日付/時刻型)

formatters オプションは強力ですが、特に日付/時刻型(datetime64[ns])のデータに対して適用しようとすると、期待通りに動作しないことがあります。これは、pandasが日付/時刻型のフォーマットを内部で特殊な方法で処理するためです。

考えられる原因

  • 日付/時刻型へのフォーマッターが無視される
    to_html の内部処理で、日付/時刻型の列に渡されたカスタムフォーマッターが適用されないことがあります。

トラブルシューティング

  • Styler オブジェクトの使用を検討する
    より複雑なフォーマットや条件付き書式設定が必要な場合は、DataFrame.style プロパティを使って Styler オブジェクトを生成し、そちらでapplyformatなどのメソッドを使ってフォーマットを適用し、最後に to_html() を呼び出すのがより柔軟で推奨される方法です。

    import pandas as pd
    from datetime import datetime
    
    df = pd.DataFrame({
        'Date': [datetime(2023, 1, 1), datetime(2023, 1, 15)],
        'Value': [100, 200]
    })
    
    # Styler を使った例
    html_output = df.style.format({'Date': lambda x: x.strftime('%Y/%m/%d')}).to_html()
    print(html_output)
    
  • 事前に型を文字列に変換する
    日付/時刻型の列をto_htmlに渡す前に、df['datetime_column'].dt.strftime('%Y-%m-%d') のようにして、目的の形式の文字列に変換します。こうすることで、通常の文字列としてフォーマッターが適用できるようになります。

特殊文字やHTMLタグのエスケープ

DataFrameのセルに <> などのHTML特殊文字や、意図的に埋め込みたいHTMLタグが含まれている場合、デフォルトではそれらがエスケープ(&lt;&gt; などに変換)されて表示されます。

考えられる原因

  • デフォルトのエスケープ挙動
    to_html() はセキュリティ上の理由から、デフォルトでHTMLをエスケープします。

トラブルシューティング

  • escape=False を使用する
    セル内の文字列がHTMLタグとして解釈されるようにしたい場合は、escape=False オプションを使用します。ただし、これにはXSS(クロスサイトスクリプティング)などのセキュリティリスクが伴うため、信頼できないソースからのデータには注意が必要です。

    import pandas as pd
    
    df = pd.DataFrame({'Description': ['<b>Bold Text</b>', '<a href="#">Link</a>']})
    
    # デフォルト(エスケープされる)
    print(df.to_html())
    
    # escape=False を使う(HTMLとして解釈される)
    print(df.to_html(escape=False))
    

大きなDataFrameでのパフォーマンス問題

非常に大きなDataFrameをHTMLに変換しようとすると、時間がかかったり、メモリを大量に消費したりすることがあります。

考えられる原因

  • 複雑なフォーマット
    formattersStyler を使って複雑なフォーマットを適用している場合、処理が重くなることがあります。
  • データ量が多い
    数十万行、数千列といった規模のDataFrameの場合、HTML生成に時間がかかります。

トラブルシューティング

  • Styler の最適化
    Styler を使用している場合、複雑なロジックはパフォーマンスに影響を与える可能性があります。可能な限りシンプルなフォーマットロジックを使用するか、事前にデータフレーム自体を整形しておきます。
  • メモリ効率の良い方法を検討する
    全体をHTMLに変換するのではなく、データをチャンクに分割して処理したり、他の出力形式(CSV、Parquetなど)を検討したりすることも有効です。
  • 必要なデータのみを変換する
    to_html() を呼び出す前に、必要な列のみを選択したり、行をサンプリングしたりして、DataFrameのサイズを小さくします。

DataFrame.style を使用してDataFrameの見た目をカスタマイズし、その後にto_html()を呼び出すのが推奨されるアプローチですが、予期せぬ結果になることもあります。

考えられる原因

  • CSSクラスやインラインスタイルの競合
    複数のスタイルが競合する場合。
  • Styler メソッドの適用順序
    Styler メソッドの適用順序が結果に影響を与えることがあります。

トラブルシューティング

  • 生成されたHTMLとCSSを確認する
    ブラウザの開発者ツールを使って、生成されたHTMLの構造と適用されているCSSを確認し、期待通りにスタイルが適用されているか、競合が発生していないかなどをデバッグします。
  • 簡単なスタイルから始める
    複雑なスタイルを一度に適用するのではなく、シンプルなスタイルから始めて、徐々に複雑なスタイルを追加していくことで、問題の特定がしやすくなります。
  • Styler のドキュメントを参照する
    Styler は非常に多機能であり、その使用方法には学習曲線があります。公式ドキュメントを参照し、適切なメソッドの適用順序と使用方法を確認してください。


基本的な変換

最も基本的な使い方は、DataFrameをHTML文字列に変換することです。

import pandas as pd

# サンプルDataFrameを作成
data = {'名前': ['アリス', 'ボブ', 'チャーリー'],
        '年齢': [25, 30, 35],
        '都市': ['東京', '大阪', '京都']}
df = pd.DataFrame(data)

# DataFrameをHTML文字列に変換して表示
html_output = df.to_html()
print(html_output)

# 出力例:
# <table border="1" class="dataframe">
#   <thead>
#     <tr style="text-align: right;">
#       <th></th>
#       <th>名前</th>
#       <th>年齢</th>
#       <th>都市</th>
#     </tr>
#   </thead>
#   <tbody>
#     <tr>
#       <th>0</th>
#       <td>アリス</td>
#       <td>25</td>
#       <td>東京</td>
#     </tr>
#     <tr>
#       <th>1</th>
#       <td>ボブ</td>
#       <td>30</td>
#       <td>大阪</td>
#     </tr>
#     <tr>
#       <th>2</th>
#       <td>チャーリー</td>
#       <td>35</td>
#       <td>京都</td>
#     </tr>
#   </tbody>
# </table>

このコードは、df の内容を標準的なHTMLテーブルとして出力します。Jupyter Notebookのような環境では、このHTMLが直接レンダリングされて表示されます。

HTMLファイルへの保存

buf オプションを使うことで、生成されたHTMLを直接ファイルに保存できます。

import pandas as pd

data = {'商品名': ['りんご', 'バナナ', 'オレンジ'],
        '価格': [150, 80, 120]}
df = pd.DataFrame(data)

# HTMLファイルを生成して保存
with open('products.html', 'w', encoding='utf-8') as f:
    df.to_html(f)

print("DataFrameが 'products.html' として保存されました。")

# products.html の内容例:
# <table border="1" class="dataframe">
#   <thead>
#     <tr style="text-align: right;">
#       <th></th>
#       <th>商品名</th>
#       <th>価格</th>
#     </tr>
#   </thead>
#   <tbody>
#     <tr>
#       <th>0</th>
#       <td>りんご</td>
#       <td>150</td>
#     </tr>
#     <tr>
#       <th>1</th>
#       <td>バナナ</td>
#       <td>80</td>
#     </tr>
#     <tr>
#       <th>2</th>
#       <td>オレンジ</td>
#       <td>120</td>
#     </tr>
#   </tbody>
# </table>

これにより、指定されたファイルパスにHTMLファイルが作成され、ブラウザで開いて内容を確認できます。

オプションによるカスタマイズ

to_html は、表示方法をカスタマイズするための多くのオプションを提供します。

a. インデックスとヘッダーの非表示、特定の列のみ表示

import pandas as pd

data = {'ID': [1, 2, 3],
        '氏名': ['田中', '佐藤', '鈴木'],
        '部署': ['営業', '開発', '人事'],
        '給与': [300000, 450000, 320000]}
df = pd.DataFrame(data)

# インデックスを非表示、ヘッダーを非表示、'氏名'と'部署'列のみ表示
html_custom = df.to_html(index=False, header=False, columns=['氏名', '部署'])
print(html_custom)

# 出力例:
# <table border="1" class="dataframe">
#   <tbody>
#     <tr>
#       <td>田中</td>
#       <td>営業</td>
#     </tr>
#     <tr>
#       <td>佐藤</td>
#       <td>開発</td>
#     </tr>
#     <tr>
#       <td>鈴木</td>
#       <td>人事</td>
#     </tr>
#   </tbody>
# </table>

b. 欠損値(NaN)の表示形式と浮動小数点数の書式設定

import pandas as pd
import numpy as np

data = {'項目': ['A', 'B', 'C'],
        '値1': [10.5, np.nan, 30.25],
        '値2': [5.123, 8.987, np.nan]}
df = pd.DataFrame(data)

# NaNを'N/A'と表示し、浮動小数点数を小数点以下2桁で表示
html_formatted = df.to_html(na_rep='N/A', float_format='%.2f')
print(html_formatted)

# 出力例:
# <table border="1" class="dataframe">
#   <thead>
#     <tr style="text-align: right;">
#       <th></th>
#       <th>項目</th>
#       <th>値1</th>
#       <th>値2</th>
#     </tr>
#   </thead>
#   <tbody>
#     <tr>
#       <th>0</th>
#       <td>A</td>
#       <td>10.50</td>
#       <td>5.12</td>
#     </tr>
#     <tr>
#       <th>1</th>
#       <td>B</td>
#       <td>N/A</td>
#       <td>8.99</td>
#     </tr>
#     <tr>
#       <th>2</th>
#       <td>C</td>
#       <td>30.25</td>
#       <td>N/A</td>
#     </tr>
#   </tbody>
# </table>

c. カスタムフォーマッターの適用

formatters オプションを使って、列ごとにカスタム関数を適用して表示形式を調整できます。

import pandas as pd

data = {'製品': ['PC', 'Monitor', 'Keyboard'],
        '価格': [1200, 300, 75],
        '在庫': [10, 0, 50]}
df = pd.DataFrame(data)

# 価格に通貨記号を追加、在庫が0の場合は「在庫切れ」と表示
html_formatters = df.to_html(
    formatters={
        '価格': lambda x: f'${x:,.2f}',  # カンマ区切りと小数点以下2桁
        '在庫': lambda x: '在庫切れ' if x == 0 else str(x)
    },
    escape=False # formatters で HTML タグを生成する場合は escape=False が必要
)
print(html_formatters)

# 出力例:
# <table border="1" class="dataframe">
#   <thead>
#     <tr style="text-align: right;">
#       <th></th>
#       <th>製品</th>
#       <th>価格</th>
#       <th>在庫</th>
#     </tr>
#   </thead>
#   <tbody>
#     <tr>
#       <th>0</th>
#       <td>PC</td>
#       <td>$1,200.00</td>
#       <td>10</td>
#     </tr>
#     <tr>
#       <th>1</th>
#       <td>Monitor</td>
#       <td>$300.00</td>
#       <td>在庫切れ</td>
#     </tr>
#     <tr>
#       <th>2</th>
#       <td>Keyboard</td>
#       <td>$75.00</td>
#       <td>50</td>
#     </tr>
#   </tbody>
# </table>

formatters の中でHTMLタグ(例: <b>)を使いたい場合は、escape=False を設定する必要があります。

d. CSSクラスの追加

生成される<table>タグにCSSクラスを追加することで、外部CSSファイルなどを使ってテーブルのスタイルを制御できます。

import pandas as pd

data = {'都市': ['札幌', '東京', '大阪'],
        '気温': [15, 25, 28]}
df = pd.DataFrame(data)

# 'table table-striped' というCSSクラスを追加
# これはBootstrapのようなCSSフレームワークでよく使われるクラスです
html_classes = df.to_html(classes='table table-striped')
print(html_classes)

# 出力例(一部抜粋):
# <table border="1" class="dataframe table table-striped">
#   <thead>
#     <tr style="text-align: right;">
#       <th></th>
#       <th>都市</th>
#       <th>気温</th>
#     </tr>
#   </thead>
#   <tbody>
#     ...
#   </tbody>
# </table>

このHTMLをWebページに埋め込み、対応するCSSを読み込むことで、テーブルの見た目を変更できます。

to_html は基本的な書式設定に適していますが、条件付き書式設定やより複雑なスタイリングには DataFrame.style プロパティで生成される Styler オブジェクトを使用するのが一般的です。Styler オブジェクトも最終的に to_html() メソッドを持っています。

import pandas as pd

data = {'月': ['Jan', 'Feb', 'Mar'],
        '売上': [1000, 1200, 800],
        '費用': [700, 900, 600]}
df = pd.DataFrame(data)

# '売上'が1000より大きい場合は緑色、そうでない場合は赤色にする
def highlight_sales(s):
    return ['background-color: lightgreen' if v > 1000 else 'background-color: lightcoral' for v in s]

# Stylerオブジェクトを使って書式設定し、HTMLに変換
html_styled = df.style.apply(highlight_sales, subset=['売上']).to_html()
print(html_styled)

# 出力例(スタイル部分の抜粋):
# <style type="text/css">
# #T_..._row0_col1 {
#     background-color: lightgreen;
# }
# #T_..._row1_col1 {
#     background-color: lightgreen;
# }
# #T_..._row2_col1 {
#     background-color: lightcoral;
# }
# </style>
# <table id="T_..." class="dataframe">
#   <thead>
#     <tr>
#       ...
#     </tr>
#   </thead>
#   <tbody>
#     <tr>
#       <th>0</th>
#       <td>Jan</td>
#       <td id="T_..._row0_col1" class="data row0 col1">1000</td>
#       <td>700</td>
#     </tr>
#     <tr>
#       <th>1</th>
#       <td>Feb</td>
#       <td id="T_..._row1_col1" class="data row1 col1">1200</td>
#       <td>900</td>
#     </tr>
#     <tr>
#       <th>2</th>
#       <td>Mar</td>
#       <td id="T_..._row2_col1" class="data row2 col1">800</td>
#       <td>600</td>
#     </tr>
#   </tbody>
# </table>

Styler オブジェクトを使用すると、より複雑なロジックに基づいてセルや行、列にCSSスタイルを適用でき、非常に見栄えの良いHTMLテーブルを生成できます。



pandas.DataFrame.style を使用した高度なHTMLスタイリング

これは to_html の直接的な代替というよりは、to_html の機能を拡張するための方法です。DataFrame.styleStyler オブジェクトを返し、これを使うことで条件付き書式設定、色付け、フォントサイズの変更など、より複雑なスタイリングをDataFrameに適用できます。最終的にこのStylerオブジェクトに対して.to_html()を呼び出すことで、スタイルが適用されたHTMLが生成されます。

なぜ使うのか?

  • Jupyter NotebookやWebレポートで、より視覚的に魅力的なテーブルを表示したい場合。
  • セルの値に基づいて色を変える、バーグラフを表示するなど、見た目を大幅に改善したい場合。


import pandas as pd
import numpy as np

df = pd.DataFrame(np.random.randn(5, 3), columns=list('ABC'))
df.iloc[0, 1] = np.nan # 欠損値を追加

# 'A' 列の負の数を赤く、'B' 列のNaNをハイライト
html_styled = df.style \
    .highlight_null(null_color='lightyellow') \
    .applymap(lambda x: 'color: red' if x < 0 else None, subset=['A']) \
    .to_html()

print(html_styled)

to_csv() や to_excel() など、他のファイル形式への出力

HTMLはWeb表示には適していますが、データ分析や共有の目的で必ずしも最適ではありません。他のツールで開いたり、分析に利用したりする場合、より汎用的な形式への出力が考えられます。

なぜ使うのか?

  • 大容量のデータを効率的に保存・共有したい場合(Parquet, HDF5など)。
  • データをCSV形式で他のプログラムに渡したい場合 (to_csv)。
  • データをExcelで開いて編集・分析したい場合 (to_excel)。


import pandas as pd

data = {'Name': ['Alice', 'Bob'], 'Age': [25, 30]}
df = pd.DataFrame(data)

# CSVファイルとして保存
df.to_csv('output.csv', index=False)
print("CSVファイルに保存しました: output.csv")

# Excelファイルとして保存
df.to_excel('output.xlsx', index=False, sheet_name='UserData')
print("Excelファイルに保存しました: output.xlsx")

Markdown形式への変換 (to_markdown())

HTMLテーブルではなく、Markdown形式のテーブルとして出力したい場合、Pandas 1.0.0以降では to_markdown() メソッドが利用できます。これは、GitHubのREADMEやMarkdownベースのドキュメントでテーブルを表示するのに便利です。

なぜ使うのか?

  • テキストベースで視覚的に整理されたテーブルが必要な場合。
  • Markdown形式のドキュメント(GitHubのREADMEなど)にテーブルを埋め込みたい場合。


import pandas as pd

data = {'フルーツ': ['りんご', 'バナナ'], '色': ['赤', '黄']}
df = pd.DataFrame(data)

markdown_table = df.to_markdown(index=False)
print(markdown_table)

# 出力例:
# | フルーツ | 色 |
# |:---|:---|
# | りんご | 赤 |
# | バナナ | 黄 |

インタラクティブなHTMLテーブル(外部ライブラリ)

静的なHTMLテーブルではなく、ソート、検索、ページネーションなどの機能を備えたインタラクティブなテーブルをWebページに表示したい場合、外部ライブラリの利用が非常に有効です。

なぜ使うのか?

  • Webアプリケーションでリッチなデータ表示を提供したい場合。
  • 大量のデータをユーザーが操作できるようにしたい場合。

代表的なライブラリ

  • Dash / Streamlit / Panel など
    これらはPythonでWebアプリケーションを構築するためのフレームワークです。DataFrameをこれらのフレームワークのコンポーネントとして表示することで、インタラクティブなダッシュボードやWebアプリケーションの一部として組み込むことができます。これらは単なるHTML出力以上のものを提供し、ユーザーとのインタラクションに基づいたデータの更新なども可能です。

    例(Streamlitの場合)

    # app.py として保存し、`streamlit run app.py` で実行
    import streamlit as st
    import pandas as pd
    
    st.title("DataFrameの表示")
    
    data = {'Product': ['A', 'B', 'C'], 'Sales': [100, 150, 80]}
    df = pd.DataFrame(data)
    
    st.dataframe(df) # Streamlitが自動的にインタラクティブなテーブルとして表示
    
  • ITables (旧 Pandas-DataFrames-as-Interactive-DataTables)
    Pandas DataFrameをDataTables.jsライブラリを使ったインタラクティブなHTMLテーブルとしてレンダリングします。Jupyter NotebookやStreamlitなどで特に便利です。

    import pandas as pd
    import itables
    
    # itablesを有効にする(Jupyter Notebookなどで)
    # itables.init_notebook_mode(all_interactive=True)
    
    df = pd.DataFrame({'名前': ['Alice', 'Bob', 'Charlie', 'David'],
                       '年齢': [25, 30, 35, 40],
                       '都市': ['New York', 'London', 'Paris', 'Tokyo']})
    
    # インタラクティブなテーブルとして表示
    # Jupyter 環境なら直接インタラクティブな表示になる
    # それ以外の環境ではHTML文字列が返される
    html_interactive = itables.to_html_datatable(df)
    print(html_interactive)
    

より包括的なレポートを生成する場合、HTMLテーブルを単独で出力するのではなく、レポート生成ライブラリと連携させることも考えられます。

なぜ使うのか?

  • PDF形式などで最終的なレポートを配布したい場合。
  • グラフ、テキスト、テーブルなどを組み合わせて、洗練されたレポートを作成したい場合。

代表的なライブラリ

  • WeasyPrint
    HTMLとCSSからPDFを生成できるため、to_html()で生成したHTMLを元にPDFレポートを作成することも可能です。
  • ReportLab (PDF生成)
    DataFrameの内容を直接PDFドキュメントに描画できます。
  • Jinja2 と組み合わせる
    pandasで生成したHTML文字列をJinja2のようなテンプレートエンジンに渡し、HTMLテンプレートに埋め込んで、より複雑なWebページを構築できます。