rapidfuzzを用いた遊戯王カードの名寄せ

rapidfuzzを用いた遊戯王カードの名寄せ

あいまいな遊戯王カード名を正式名称と紐づけたい

たとえば何らかのシステムに「青眼の白龍」のつもりで「青目の白龍」と入力したとする。
(「眼」と「目」が間違っている。)

このときに、


そんな名前のカードは存在しません

ではなく、


あなたが入力したのは青眼の白龍ですか?

こういった需要を満たす技術は「名寄せ」と呼ばれていて、
そこでは文字列間の類似度、あるいは距離を計算する手法がよく用いられる。
有名なアルゴリズムとして、

  • レーベンシュタイン距離
  • ジャロ距離
  • ジャロ・ウィンクラー距離

などいくつか挙げられるが、その辺のアルゴリズムを大体網羅して、かつ高速で使用方法も簡単な最強ライブラリが存在する。

今回はその最強ライブラリrapidfuzzを使って遊戯王カード名の名寄せを行う。

1. 実験

python3.11
インストールしたのはrapidfuzzpandasのみ。

1.1. シンプルな実装

rapidfuzzは非常に簡単。
下記の5行で簡単な実験ができる。

  • コード

    1
    2
    3
    4
    5
    6
    7
    from 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
    9
    rank,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
    13
    from 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

本当は「ブルーアイズ」で「青眼の白龍」を呼んできてほしい。
このパターンは単純な文字列の比較だと難しい。
マスタデータに読み仮名を追加するなど根本的な変更が必要。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×