姓名分離ライブラリ seimei-split を作った
プロジェクトでデータ移行をしていて、姓名が結合された状態のデータを姓と名に分ける必要がありました。 移行コード自体は JS/TS で書いていたので JS/TS のライブラリを探しましたが、見つかりませんでした。
探していく中で Python 実装の rskmoi/namedivider-python は見つけましたが、JS/TS はもちろん他の言語でも同等のライブラリはほとんど存在しませんでした。 必要そうな割に意外とライブラリが無かったので、AIをがっつり活用して自分で作ることにしました。
- GitHub: tk1024/seimei-split
- Playground: https://tk1024.github.io/seimei-split/
基本的な使い方
npm install seimei-split
import { split } from "seimei-split"; split("田中太郎"); // { sei: "田中", mei: "太郎" } split("田中 太郎"); // { sei: "田中", mei: "太郎" } split("たなかたろう"); // { sei: "たなか", mei: "たろう" }
スペース区切りがある場合はそのまま分割し、スペースがない場合は辞書マッチングとスコアリングで最適な分割位置を推定します。漢字・ひらがな・カタカナに対応しています。
名前辞書の話
名前分離の面倒なところは、実質的に名前辞書を作ってパターンマッチングするしかないところです。
しかし、オープンな名前データは意外と存在しませんでした。 国の機関がデータを公開しているわけでもなく、名前ランキング系のサイトも利用規約が不明確だったり、数万件規模のデータがほしかったので断念しました。
最終的には UniDic という日本語辞書データの中から人名データのみを抽出して利用しています。UniDic のライセンスはかなり自由で、とてもありがたいです。
これにより以下のデータが手に入りました。
| 種別 | 件数 |
|---|---|
| 姓(苗字) | 18,364件 |
| 名(名前) | 37,084件 |
日本には苗字が全部で約30万種類あると言われていますが、上位1,000件で約95%を網羅しているという統計があります。1.8万件あれば実用上かなりのカバー率になるはずです。
実際に作ってみるとかなり良いものができて、すぐに実用化できました。
パッケージサイズ
| サイズ | |
|---|---|
| コアロジック (gzip) | 1.8 KB |
| 辞書データ込み (gzip) | 216 KB |
辞書を内包しているためパッケージ全体では 216 KB になりますが、コアロジック自体は 1.8 KB と軽量です。辞書込みでも 216 KB なので、クライアントサイドだけでも十分使えるサイズです。
精度
| モード | 精度 |
|---|---|
| 標準モード | 94.7% |
allowLowConfidence 有効時 | 99.5% |
テストデータはエッジケースを多めに組み込んでいるため低めに出ていますが、一般的な日本人の実名であればほぼ間違いなく分割できると思っています。エッジケース込みでも標準モードで 94.7% あるので、実用性はかなりあると言えるはずです。
標準モードでは辞書にマッチしなかった場合、分割せず null を返します。allowLowConfidence を有効にすると、辞書外のヒューリスティクスも使って分割を試みます。
今後 rskmoi/namedivider-python のテストデータなども使って検証してみたいと思っています。
エッジケースとの戦い:VTuber の名前で検証してみた
翌日に思いついて、VTuber の名前など珍しいケースで検証してみました。 VTuber は個性的な名前が多く辞書に載っていないので、やはり辞書にないデータにはかなり弱かったです。
初期状態ではこんな結果になっていました。
| 入力 | 期待する分割 | 初期の分割結果 | 原因 |
|---|---|---|---|
| 夏色まつり | 夏色 / まつり | 夏 / 色まつり | 「夏」が苗字辞書にある |
| 宝鐘マリン | 宝鐘 / マリン | 宝 / 鐘マリン | 「宝」が苗字辞書にある |
| ジャガー横田 | ジャガー / 横田 | ジャガー横 / 田 | 「田」が名前辞書にあり、「横田」は苗字辞書にしかない |
スコアリングを見てみると、辞書に部分一致してしまう文字に引っ張られていることがわかりました。
そこで以下のような追加のヒューリスティクスを導入しました。
- 姓と名を分けた時に漢字とカタカナ・ひらがなが混在している場合は減点する
- 苗字がカタカナの場合は芸名の可能性が高いので、後方を苗字辞書でもマッチングする
- その他、文字種の境界を考慮したスコア調整
これらの調整により、VTuber のような個性的な名前も含めてかなり良い精度になりました。
なお、この辞書外の追加スコアリングは allowLowConfidence を有効にしないと適用されません。
import { split } from "seimei-split"; // 標準モード: 辞書にない名前は null を返す split("宝鐘マリン"); // => null // allowLowConfidence: ヒューリスティクスで分割を試みる split("宝鐘マリン", { allowLowConfidence: true }); // => { sei: "宝鐘", mei: "マリン" }
Web Playground
せっかく JS/TS で実装したので、ブラウザで動く Playground も作りました。
https://tk1024.github.io/seimei-split/
名前をコピペして姓名分離を試せるほか、結果を CSV で書き出すこともできます。コードに組み込まずにサクッと使いたい場合はこちらからどうぞ。