初めに
値の関連付けが必要で高速に文字列を検索したい時に使えるらしいpyahocorasickを触ってみます
github.com
開発環境
| 項目 |
バージョン |
| OS |
macOS 26.0.1 |
| Python |
3.12.12 |
| uv |
0.9.5 |
| pyahocorasick |
2.3.0 |
環境構築
まずはプロジェクトの初期化を行います
# プロジェクトディレクトリで実行
uv init --no-readme
# Pythonバージョンを固定
uv python pin 3.12
開発用依存関係をインストールします
uv add --dev pytest twine setuptools wheel
本ライブラリをUnicode版でビルドします
AHOCORASICK_UNICODE=yes uv pip install -e .
実行
実際に以下のようなでもスクリプトを作成して実行してみます
import ahocorasick
PREFECTURES = [
"北海道", "青森県", "岩手県", "宮城県", "秋田県", "山形県", "福島県",
"茨城県", "栃木県", "群馬県", "埼玉県", "千葉県", "東京都", "神奈川県",
"新潟県", "富山県", "石川県", "福井県", "山梨県", "長野県", "岐阜県",
"静岡県", "愛知県", "三重県", "滋賀県", "京都府", "大阪府", "兵庫県",
"奈良県", "和歌山県", "鳥取県", "島根県", "岡山県", "広島県", "山口県",
"徳島県", "香川県", "愛媛県", "高知県", "福岡県", "佐賀県", "長崎県",
"熊本県", "大分県", "宮崎県", "鹿児島県", "沖縄県",
]
def main():
print("=" * 60)
print("pyahocorasick 基本デモ")
print("=" * 60)
print("\n【1. Automatonの作成】")
automaton = ahocorasick.Automaton()
print(f" Automaton created: {automaton}")
print("\n【2. キーワードの登録】")
for i, pref in enumerate(PREFECTURES):
automaton.add_word(pref, {"name": pref, "index": i})
print(f" 登録キーワード数: {len(automaton)}")
print("\n【3. オートマトンの構築】")
automaton.make_automaton()
print(f" 状態: {automaton}")
print("\n【4. テキスト検索】")
text = "東京都から京都府へ行き、大阪府と兵庫県を訪問した後、北海道と沖縄県にも足を延ばした"
print(f" 検索テキスト: 「{text}」")
print(f" テキスト長: {len(text)}文字")
print("\n【5. 検索結果】")
matches = []
for end_index, value in automaton.iter(text):
start_index = end_index - len(value["name"]) + 1
matches.append({
"start": start_index,
"end": end_index,
"keyword": value["name"],
"index": value["index"],
})
for match in matches:
print(f" 位置 {match['start']:2d}-{match['end']:2d}: {match['keyword']} (index={match['index']})")
print(f"\n マッチ数: {len(matches)}件")
print("\n" + "=" * 60)
print("追加機能")
print("=" * 60)
print("\n【キーワードの存在確認】")
print(f" '東京都' in automaton: {'東京都' in automaton}")
print(f" '東京' in automaton: {'東京' in automaton}")
print("\n【値の取得】")
print(f" automaton.get('大阪府'): {automaton.get('大阪府')}")
print(f" automaton.get('存在しない', 'default'): {automaton.get('存在しない', 'default')}")
print("\n【統計情報】")
print(f" キーワード数: {len(automaton)}")
print(f" Unicode mode: {ahocorasick.unicode}")
if __name__ == "__main__":
main()
結果は以下のとおりです
============================================================
pyahocorasick 基本デモ
============================================================
【1. Automatonの作成】
Automaton created: <ahocorasick.Automaton object at 0x10095d8b0>
【2. キーワードの登録】
登録キーワード数: 47
【3. オートマトンの構築】
状態: <ahocorasick.Automaton object at 0x10095d8b0>
【4. テキスト検索】
検索テキスト: 「東京都から京都府へ行き、大阪府と兵庫県を訪問した後、北海道と沖縄県にも足を延ばした」
テキスト長: 41文字
【5. 検索結果】
位置 0- 2: 東京都 (index=12)
位置 5- 7: 京都府 (index=25)
位置 12-14: 大阪府 (index=26)
位置 16-18: 兵庫県 (index=27)
位置 26-28: 北海道 (index=0)
位置 30-32: 沖縄県 (index=46)
マッチ数: 6件
============================================================
追加機能
============================================================
【キーワードの存在確認】
'東京都' in automaton: True
'東京' in automaton: False
【値の取得】
automaton.get('大阪府'): {'name': '大阪府', 'index': 26}
automaton.get('存在しない', 'default'): default
【統計情報】
キーワード数: 47
Unicode mode: 1
他の文字検索ライブラリとの比較
調査をすると以下のようなものが見つかりました
| ライブラリ |
実装言語 |
値関連付け |
部分一致 |
Unicode |
Pickle |
メンテナンス |
| pyahocorasick |
C |
✅ |
✅ |
✅ |
✅ |
活発 |
| ahocorasick_rs |
Rust |
❌ |
✅ |
✅ |
❌ |
活発 |
| flashtext |
Python |
✅ |
❌ |
✅ |
✅ |
低調 |
| ahocorapy |
Python |
✅ |
✅ |
✅ |
✅ |
低調 |
| regex |
C |
- |
✅ |
✅ |
- |
活発 |
また機能や性能を調べると以下のようになります
相対速度(pyahocorasick = 1.0として)
以下はWeb上の各種ベンチマーク結果に基づく概算値です。実際の性能は環境・データにより異なります。
| ライブラリ |
検索速度 |
構築速度 |
メモリ効率 |
| ahocorasick_rs |
1.5〜7× 高速 |
同等〜やや遅い |
同等 |
| pyahocorasick |
1.0(基準) |
1.0 |
1.0 |
| flashtext |
0.3〜1.0× |
遅い |
低い |
| ahocorapy |
0.1〜0.3× |
遅い |
低い |
| regex (1000+ keywords) |
0.01〜0.1× |
N/A |
N/A |
キーワード数と性能の関係
| キーワード数 |
regex |
pyahocorasick |
ahocorasick_rs |
| 10 |
良好 |
良好 |
良好 |
| 100 |
良好 |
良好 |
良好 |
| 500 |
低下開始 |
良好 |
良好 |
| 1,000+ |
急激に低下 |
安定 |
安定 |
| 10,000+ |
使用不可 |
安定 |
安定 |
技術選定について
これらがある中で以下のようになります
キーワード数
↓
┌───────────────┴───────────────┐
│ │
〜100個 100個以上
│ │
▼ ▼
regex/re 値の関連付けが必要?
↓
┌───────────┴───────────┐
│ │
必要 不要
│ │
▼ ▼
pyahocorasick ahocorasick_rs
(バランス良好) (最速)
ここで キーワードに値を関連付けたい : 値の関連付けがキーワードになってきます。これは以下のような用途・例の場合に適当することができそうです
以下のようにキーワードに任意のデータを紐付けできるため用途は以下の場合を想定できます
- 固有表現抽出(NER)
- 辞書ベース変換(読み仮名・翻訳)
- コンテンツフィルタリング
- ログ解析・アラート
automaton.add_word("東京", {"reading": "とうきょう", "en": "Tokyo"})