Polarsの日本向けの拡張ライブラリ


Polarsで日本語処理や祝日判定など、日本の人が少し便利になる機能を詰め合わせたライブラリ、polars-japaneseを作成しました。

https://github.com/tanaka-jin/polars-japanese

たとえばこんなことができます。

- 和暦 ↔ datetime 変換
- 祝日判定・曜日取得
- 全角 ↔ 半角 変換
- 漢数字変換
- 都道府県 → 地方 変換
- Shift-JISでCSV出力

本記事では主な機能と背景技術について紹介します。

※この記事はPolars Data Crunch #4で発表した内容がベースですが、新たに追加した機能の説明も含んでいます[^crunch]

[^crunch]: Polars Data Crunch #4 の イベントページ: https://polars-ja.connpass.com/event/352216/ , LT資料: https://speakerdeck.com/jinta/polars-japanese-lt , 動画: https://youtube.com/live/h0tg7GQ2RNE?feature=share


# 概要
## 3行でわかる使い方
インストール
```
pip install polars-japanese
```

使用
```python
import polars_japanese

# 和暦（例: "令和2年2月2日"）で入っているデータをdatetimeに変換
df.select(pl.col("出生日").ja.to_datetime())
```

手順としては、インストール、インポートしたら `ja` アクセサが使えるようになります。`.str` や `.dt` と同じように、普通にExpressionに続けて使うことができます。

**注意事項**
- installとimportで名前が微妙に違うので（ハイフン `-` とアンスコ `_` ）、その点ご注意ください。japanize-matplotlib に倣ってます
- `import polars_japanese` は必要なものの、コード内で直接使用はしないので、Ruffなどに怒られる場合は `# noqa: F401` を書いておくことを推奨します

## 関連リンク
- Github: https://github.com/tanaka-jin/polars-japanese
- Docs: https://polars-japanese.readthedocs.io/ja/
- PyPI: https://pypi.org/project/polars-japanese/


# 主な機能

## 1. 全角・半角変換
- `.ja.to_half_width()`, `.ja.to_full_width()`
- `.ja.normalize()`

全角・半角変換は、数字、カタカナ、ascii（アルファベットなど）を変換します。Rustの[mojimoji-rs](https://github.com/europeanplaice/mojimoji-rs)をwrapしたものです。

`normalize`は、 `polars.str.normalize` によるNFKC正規化に加えて、[jaconv](https://github.com/ikegami-yukino/jaconv)に倣い、伸ばし棒（`―‐˗֊‐‑‒–⁃⁻₋−`）の統一などの処理を行っています。



## 2. 和暦変換
- `.ja.to_datetime(format='%-K%-y年%m月%d日', raise_error=True)`
- `.ja.to_wareki(format='%-K%-Y年%m月%d日', raise_error=True)`

文字列で持ってる和暦とDatetime型の変換です。
[japanera](https://github.com/nagataaaas/Japanera)を呼び出しているので、`format` を `%-K` と指定すると漢字（昭和、令和）、`%-E` にするとローマ字の和暦（Showa, Reiwa）へと変換するなど、フォーマットの指定が可能です。

平成50年など、存在しない和暦が指定されている場合にエラーを吐くか欠損にするかを `raise_error` で指定しています。

## 3. 漢数字変換
- `.ja.to_kanji(config=None)`
- `.ja.to_number()`

[kanjize](https://github.com/nagataaaas/Kanjize)をwrapしたものです。漢字表記の仕方はkanjizeのconfigをそのまま引数として渡しているので、たとえば二〇〇〇/二千/2千、〇/零など、細かく制御できるようになっています。

## 4. 祝日判定・曜日取得
- `.ja.is_holiday()`
- `.ja.is_business_day()`
- `.ja.to_weekday_name(format="%A")`

祝日判定は[JPHoliday](https://github.com/Lalcs/jpholiday)をwrapしたものです。`is_business_day` は土日と祝日の場合Falseを返します[^business_day]。

曜日取得は、デフォルトでは「月曜日」「火曜日」が返されますが、`format="%a"` とすると「月」「火」を取得できます。

[^business_day]: 現実点では、Polars 1.27で追加された `is_business_day` は使用していません。そのうち変更するかもです。参考: https://speakerdeck.com/ghibney/polars-1-dot-1-0yi-jiang-noshi-eruxin-ji-neng?slide=15


## 5. 都道府県変換
- `.ja_pref.to_code()`
- `.ja_pref.to_kanji()`
- `.ja_pref.to_hiragana()`
- `.ja_pref.to_katakana()`
- `.ja_pref.to_romaji()`
- `.ja_pref.to_region()`

都道府県名（漢字、かな、ローマ字）と都道府県コード間の変換ができます。名前は「青森県」のような書き方にも「青森」のような書き方にも対応しています。

また、`to_region` で地方への変換を行えます。地方の定義は複数ありますが、おそらく最も一般的であろう八地方区分を採用しています[^region]。

**注意事項**: この機能は `.ja` アクセサではなく `.ja_pref` アクセサから使用できます。

[^region]: 北海道、東北、関東、中部、近畿、中国、四国、九州・沖縄の8つの地方。https://makitani.net/shimauma/knowledge/regional-classification-of-japan ここはすぐに拡張するかもです。


## 6. CSV出力
- `.ja.write_csv(path, encoding="shift-jis")`

polarsの `write_csv` だと、文字コードの指定ができません[^enc]。

たまにShift-JISなどで出力したくなるときが発生するので、nkayさんの[こちらの記事](https://qiita.com/nkay/items/9cfb2776156dc7e054c8#%E3%83%87%E3%83%BC%E3%82%BF%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%81%AEcsv%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%87%BA%E5%8A%9B)をもとに実装しました。ありがとうございます！


[^enc]: https://docs.pola.rs/api/python/stable/reference/api/polars.DataFrame.write_csv.html

# 背景技術
## namespace
- `register_expr_namespace`, `register_dataframe_namespace`

PolarsのExpressionに続けて書ける処理を、 `.str` や `.dt` と同じように使える形で追加できる。これを用いてExpr -> Exprのメソッドを登録することで、`ja` アクセサで使えるようになる。

## plugin
- `register_plugin_function`

Rustで実装したものをpythonから呼び出せるようにするための機能がプラグイン。Polarsから呼び出せるようにRust側、Python側双方で設定が必要[^plugin]。

[^plugin]: LLMに書いてもらっていて正直どう働いているかはあまり理解してないので、詳細はごめんなさい。

## おまけ
小さなこだわりとして、このライブラリのなかでゼロから全て作るのではなく、既存のライブラリを流用する形を優先しています。作成手間を避けつつ、細かい制御が可能になっているかと思います。

その分 `map_elements` を多用する形になってしまっているので、スピードとの兼ね合いを考えて今後変更する可能性はあります。

**参考になったもの**
- [Polars plugins tutorial](https://marcogorelli.github.io/polars-plugins-tutorial/)
- [Plugins - Polars user guide](https://docs.pola.rs/user-guide/plugins/)
- [Polars Expression Plugins](https://zenn.dev/uchiiii/articles/2466719bfe2ec5)
- [polarsのudfとかpluginとか触ってみた - Speaker Deck](https://speakerdeck.com/t88/polarsnoudftokaplugintokahong-tutemita)



# 今後の展望
ニーズがあるかなどをあまり考えずに作ってしまったのですが、継続で開発していく気持ちはありますので、「こんな機能も欲しい」「ここが使いずらい」などあったらフィードバックいただけると飛んで喜びます。

また、一緒に開発することに興味ある方がいらっしゃったら[polars-jaのdiscord](https://github.com/polars-ja)より連絡ください！

----