あいまいな遊戯王カード名を正式名称と紐づけたい
たとえば何らかのシステムに「青眼の白龍」のつもりで「青目の白龍」と入力したとする。
(「眼」と「目」が間違っている。)
このときに、
そんな名前のカードは存在しません
ではなく、
あなたが入力したのは青眼の白龍ですか?
こういった需要を満たす技術は「名寄せ」と呼ばれていて、
そこでは文字列間の類似度、あるいは距離を計算する手法がよく用いられる。
有名なアルゴリズムとして、
- レーベンシュタイン距離
- ジャロ距離
- ジャロ・ウィンクラー距離
などいくつか挙げられるが、その辺のアルゴリズムを大体網羅して、かつ高速で使用方法も簡単な最強ライブラリが存在する。
今回はその最強ライブラリrapidfuzz
を使って遊戯王カード名の名寄せを行う。
1. 実験
python3.11
インストールしたのはrapidfuzz
とpandas
のみ。
1.1. シンプルな実装
rapidfuzz
は非常に簡単。
下記の5行で簡単な実験ができる。
コード
1
2
3
4
5
6
7from rapidfuzz.process import extract
input_name = "青目の白龍"
name_list = ["青眼の究極竜", "青眼の白龍", "青眼の銀ゾンビ"]
result = extract(input_name, name_list)
print(result)出力結果
1
[('青眼の白龍', 80.0, 1), ('青眼の究極竜', 36.36363636363637, 0), ('青眼の銀ゾンビ', 33.333333333333336, 2)]
上記実験では、”青目の白龍”という文字列と、リスト内の3つの文字列それぞれとの類似度の計算を行い、
その結果を返す。
デフォルトのアルゴリズムはレーベンシュタイン距離。
結果は下記のようなタプルのリストになっていて、
(文字列, 類似度, 元リストのインデックス)
リストの順番は類似度の高い順。
「元リストのインデックス」が返ってくるのがありがたい。
1.2. 実際にありそうな実装
続いて少し本格的な実験。
先ほどは手入力した名前リストの代わりに表データを使用。
事前に用意したcardinfo.csv
を読み込む。
cardinfo.csv
1
2
3
4
5
6
7
8
9rank,name_jp,card_id,atk,def
1,ブラック・マジシャン,46986414,2500,2100
2,青眼の白龍,89631139,3000,2500
3,ブラック・マジシャン・ガール,38033121,2000,1700
4,マスター・オブ・OZ,27134689,4200,3700
5,サイバー・ドラゴン,70095154,2100,1600
.
.
.
そして、rapidfuzz
により取得した、最も類似度の高いカード名の「元リストのインデックス」を使用。
表データの該当インデックスを参照して結果を返す。
- コード
1
2
3
4
5
6
7
8
9
10
11
12
13from rapidfuzz.process import extract
import pandas as pd
# カード情報の読み込み
df = pd.read_csv("cardinfo.csv", encoding="utf-8")
# 入力カード名
input_name = "青目の白龍"
result = extract(input_name, df["name_jp"])
print(result)
print("\n")
print(df.iloc[result[0][2]])
- 出力結果1
1
2
3
4
5
6
7
8[('青眼の白龍', 80.0, 1), ('青眼の亜白龍', 72.72727272727273, 67), ('青氷の白夜龍', 72.72727272727273, 390), ('Sin 青眼の白龍', 72.0, 641), ('炎龍', 60.00000000000001, 3476)]
rank 2
name_jp 青眼の白龍
card_id 89631139
atk 3000
def 2500
Name: 1, dtype: object
成功。
「青目の白龍」と入力したのに「青眼の白龍」のデータを取得することができた。
1.3. 他の実験結果
- 「ブラックマジシャン」で試したパターン
1
2
3
4
5
6
7
8
9[('ブラック・マジシャン', 94.73684210526316, 0), ('竜騎士ブラック・マジシャン', 81.81818181818181, 288), ('ブラック・マジシャン・ガール
', 80.0, 2), ('竜騎士ブラック・マジシャン・ガール', 80.0, 91), ('超魔導師-ブラック・マジシャンズ', 80.0, 99)]
rank 1
name_jp ブラック・マジシャン
card_id 46986414
atk 2500
def 2100
Name: 0, dtype: object
うまくいっている。
今回はやっていないが文字列に「・」などの記号が入るパターンは、前処理として記号を全部除去すると精度が上がる。
- 「ブルーアイズ」で試したパターン
1
2
3
4
5
6
7
8[('ブルーアイズ・トゥーン・ドラゴン', 90.0, 41), ('ブルーアイズ・カオス・MAX・ドラゴン', 90.0, 98), ('ブルーアイズ・ジェット・ドラゴン', 90.0, 202), ('ブルーアイズ・ソリッド・ドラゴン', 90.0, 745), ('ブルーアイズ・タイラント・ドラゴン', 90.0, 778)]
rank 42
name_jp ブルーアイズ・トゥーン・ドラゴン
card_id 53183600
atk 3000
def 2500
Name: 41, dtype: object
本当は「ブルーアイズ」で「青眼の白龍」を呼んできてほしい。
このパターンは単純な文字列の比較だと難しい。
マスタデータに読み仮名を追加するなど根本的な変更が必要。