Source code for pythainlp.soundex.metasound
# SPDX-FileCopyrightText: 2016-2026 PyThaiNLP Project
# SPDX-FileType: SOURCE
# SPDX-License-Identifier: Apache-2.0
"""
Thai soundex, MetaSound system.
References:
Snae & Brückner. (2009). Novel Phonetic Name Matching Algorithm with
a Statistical Ontology for Analysing Names Given in Accordance
with Thai Astrology.
https://pdfs.semanticscholar.org/3983/963e87ddc6dfdbb291099aa3927a0e3e4ea6.pdf
"""
from __future__ import annotations
_CONS_THANTHAKHAT: str = "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรลวศษสหฬอฮ์"
_THANTHAKHAT: str = "์" # \u0e4c
_C1: str = "กขฃคฆฅ" # sound K -> coded letter 1
_C2: str = "จฉชฌซฐฏทฑฒถธดฎตสศษ" # D -> 2
_C3: str = "ฟฝพผภบป" # B -> 3
_C4: str = "ง" # NG -> 4
_C5: str = "ลฬรนณฦญ" # N -> 5
_C6: str = "ม" # M -> 6
_C7: str = "ย" # Y -> 7
_C8: str = "ว" # W -> 8
def _build_code_table() -> dict[str, str]:
"""Map each consonant to its code; the first matching group wins."""
table: dict[str, str] = {}
groups = (_C1, _C2, _C3, _C4, _C5, _C6, _C7, _C8)
for code, group in enumerate(groups, start=1):
for ch in group:
table.setdefault(ch, str(code))
return table
_CODES: dict[str, str] = _build_code_table()
def _remove_karan(chars: list[str]) -> list[str]:
"""Remove each thanthakhat and the character before it."""
if _THANTHAKHAT not in chars:
return chars
kept: list[str] = []
prev = ""
for ch in chars:
if ch != _THANTHAKHAT:
kept.append(ch)
elif kept and prev != _THANTHAKHAT:
kept.pop()
prev = ch
return kept
[docs]
def metasound(text: str, length: int = 4) -> str:
"""
Convert text into a MetaSound phonetic code.
MetaSound [#metasound]_ is a combination of the Soundex and
Metaphone algorithms.
The MetaSound algorithm was developed specifically for Thai.
:param str text: Thai word to be encoded
:param int length: preferred length of the MetaSound code (default is 4)
:return: MetaSound code
:rtype: str
:Example:
>>> from pythainlp.soundex.metasound import metasound
>>> metasound("ลัก")
'ล100'
>>> metasound("รัก")
'ร100'
>>> metasound("รักษ์")
'ร100'
>>> metasound("บูรณการ", 5)
'บ5515'
>>> metasound("บูรณการ", 6)
'บ55150'
>>> metasound("บูรณการ", 4)
'บ551'
"""
if not text or not isinstance(text, str):
return ""
# keep only consonants and thanthakhat
chars = [ch for ch in text if ch in _CONS_THANTHAKHAT]
chars = _remove_karan(chars)[:length]
# the first character stays; the rest are coded
coded = [chars[0]] if chars else []
coded.extend(_CODES.get(ch, "0") for ch in chars[1:])
if len(coded) < length:
coded.extend("0" * (length - len(coded)))
return "".join(coded)