🐻‍❄️

【Python】Polarsからリンク付きのExcelを出力する

2024/01/23に公開

Rustではなく、Pythonの方のPolarsの話です。最近は機会があればPandasではなくPolarsで書いてみようと手を出していますが、なかなか慣れません。

今回は、リンク付きのExcelファイルをいい感じに出力しようとして、調べるのに結構時間がかかってしまいました。

polarsのwrite_excelメソッドで出力すると、=HYPERLINK("https://example.com", "リンクのサンプル") の様な文字列がそのまま出力されて、クリックできません。一旦xlsxwrite.Workbookを持ってきて、それをwrite_excleメソッドに渡して出力すると、求めていた挙動になります。

検証用コード全体

pip install polars xlsxwriteしてからpython3 example.pyすれば動くハズ。

example.py
import polars as pl
import xlsxwriter


def gen_hyperlink(query: str) -> str:
    """検索用のURLを生成"""
    return f'=HYPERLINK("https://example.com/search?q={query}", "検索用URL")'


def gen_dataframe() -> pl.DataFrame:
    """テスト用DataFrameを生成"""
    return pl.DataFrame(
        {
            "Fluits": ["apple", "banana", "orange"],
        },
    ).with_columns(pl.col("Fluits").map_elements(gen_hyperlink).alias("SearchURL"))


def export_directly() -> None:
    """pl.DataFrameから直接書き出し"""
    fluits = gen_dataframe()
    fluits.write_excel("test-direct.xlsx", autofit=True)


def export_with_xlsxwriter() -> None:
    """xlsxwriter.Workbookを使用"""
    fluits = gen_dataframe()
    with xlsxwriter.Workbook("test-xlsxwriter.xlsx") as wb:
        fluits.write_excel(wb, autofit=True)


def export_with_format() -> None:
    """xlsxwriter.Workbookを使用してフォーマットを適用"""
    fluits = gen_dataframe()
    with xlsxwriter.Workbook("test-format.xlsx") as wb:
        fluits.write_excel(
            wb,
            autofit=True,
            column_formats={"SearchURL": {"color": "blue", "underline": "TRUE"}},
        )

def main() -> None:
    export_directly()
    export_with_xlsxwriter()
    export_with_format()


if __name__ == "__main__":
    main()

検証

下記コードはdf変数にpolars.DataFrameが入っているものとします。また、URLを格納した列をSearchURLとします。

失敗例: polars.DataFrame.write_excel()

普通にやると、HYPERLINK関数がそのまま文字列として表示される。

df.write_excel("test.xlsx")


直接リンクとしてクリックできない

Excel上で編集すると変換される


これじゃない感

成功例: xlsxwriter.Workbookを使用

一旦xlsxwriter.Workbookを取得して、それを利用してwrite_excelメソッドを使うと、クリックできるリンクとして出力される。

with xlsxwriter.Workbook("test.xlsx") as wb:
    df.write_excel(wb)


ちゃんとクリックできる

さらに完璧に: リンクのある列にスタイルを当てる

見た目が他の文字列と同じままだとクリックできるリンクだと分からないので、該当の列に青文字と下線を適用する。

with xlsxwriter.Workbook("test.xlsx") as wb:
    df.write_excel(
        wb,
	column_formats={"SearchURL": {"color": "blue", "underline": "TRUE"}},
    )


求めていたもの

どうしてこうなるのか

原因は、write_excel関数にWorkbookではなくファイル名などが与えられた場合、_xl_setup_workbook関数内の ここで Workbookをインスタンス化する時に"strings_to_formulas": Falseを設定しているため。

自分でインスタンス化する際はこのオプションを指定していないため、デフォルト値の True が使用されます。Falseになっているのにはセキュリティ上の理由等があるんでしょうか。ご存知の方がいたら教えてください。

まとめ

Polarsの情報量はかなり増えてきましたが、Pandasにはまだまだ遠く及びません。未だに急ぎの時や迷った時はPandasに逃げてしまうときもあります。

パフォーマンスももちろんですが、書き方についてもpl.colで書けるのがPandasに比べてかなりスマートで気に入っているので、今後も機会があればどんどん使っていきたいです。

参考

https://docs.pola.rs/py-polars/html/reference/api/polars.DataFrame.write_excel.html

https://xlsxwriter.readthedocs.io/working_with_polars.html

https://xlsxwriter.readthedocs.io/workbook.html

Discussion