Skip to content

Commit 82d4302

Browse files
authored
Merge pull request #1363 from bact/nerentity
Define EntitySpan class for cleaner NER interface
2 parents e9a80ad + 83d8e67 commit 82d4302

3 files changed

Lines changed: 29 additions & 21 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ basepython = "pypy3.11"
414414

415415
[tool.tox.env.ruff]
416416
basepython = "python"
417-
deps = ["ruff"]
417+
deps = "ruff"
418418
commands = [["ruff", "check", "pythainlp"], ["ruff", "format", "--check", "pythainlp"]]
419419
skip_install = true
420420

pythainlp/tag/named_entity.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
from __future__ import annotations
77

8-
from typing import TYPE_CHECKING, Any, Union
8+
from typing import TYPE_CHECKING, TypedDict, Union
99

1010
if TYPE_CHECKING:
1111
from types import ModuleType
@@ -20,6 +20,14 @@
2020
ThaiNameTagger as WangchanbertaThaiNameTagger,
2121
)
2222

23+
24+
class EntitySpan(TypedDict):
25+
"""Entity span dictionary"""
26+
entity_type: str
27+
text: list[str]
28+
span: list[int]
29+
30+
2331
# Type alias for NER engine types
2432
NEREngineType = Union[
2533
"ThaiNNER",
@@ -164,7 +172,7 @@ def load_engine(self, engine: str = "thai_nner") -> None:
164172

165173
def tag(
166174
self, text: str, top_level_only: bool = False
167-
) -> tuple[list[str], list[dict[str, Any]]]:
175+
) -> tuple[list[str], list[EntitySpan]]:
168176
"""This function tags nested named entities.
169177
170178
:param str text: text in Thai to be tagged
@@ -175,7 +183,7 @@ def tag(
175183
:return: a tuple of (tokens, entities) where tokens is a list of
176184
tokenized strings and entities is a list of dictionaries
177185
containing 'text', 'span', and 'entity_type' keys.
178-
:rtype: tuple[list[str], list[dict[str, Any]]]
186+
:rtype: tuple[list[str], list[EntitySpan]]
179187
180188
.. note::
181189
The tokenized output may include empty strings as part of the

pythainlp/tag/thai_nner.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,23 @@
99

1010
from __future__ import annotations
1111

12-
from typing import TYPE_CHECKING, Any, Optional, Union
12+
from typing import TYPE_CHECKING, Optional, Union
1313

1414
from pythainlp.corpus import get_corpus_path
15+
from pythainlp.tag.named_entity import EntitySpan
1516

1617
if TYPE_CHECKING:
1718
from thai_nner import NNER # noqa: F401
1819

20+
1921
__all__: list[str] = ["ThaiNNER"]
2022

2123

22-
def _is_contained_in(
23-
entity: dict[str, Any], container: dict[str, Any]
24-
) -> bool:
24+
def _is_contained_in(entity: EntitySpan, container: EntitySpan) -> bool:
2525
"""Check if an entity is strictly contained within a container entity.
2626
27-
:param dict[str, Any] entity: Entity to check
28-
:param dict[str, Any] container: Potential container entity
27+
:param EntitySpan entity: Entity to check
28+
:param EntitySpan container: Potential container entity
2929
:return: True if entity is strictly contained in container
3030
:rtype: bool
3131
"""
@@ -42,19 +42,19 @@ def _is_contained_in(
4242

4343

4444
def get_top_level_entities(
45-
entities: list[dict[str, Any]],
46-
) -> list[dict[str, Any]]:
45+
entities: list[EntitySpan],
46+
) -> list[EntitySpan]:
4747
"""Extract only top-level (outermost) entities from nested NER results.
4848
4949
In nested NER, entities can contain other entities. This function filters
5050
the results to return only the outermost entities that are not contained
5151
within other entity.
5252
53-
:param list[dict[str, Any]] entities: List of entity dictionaries with
53+
:param list[EntitySpan] entities: List of entity dictionaries with
5454
'span', 'text', and 'entity_type'
5555
keys
5656
:return: List of top-level entities only
57-
:rtype: list[dict[str, Any]]
57+
:rtype: list[EntitySpan]
5858
5959
:Example:
6060
::
@@ -81,7 +81,7 @@ def get_top_level_entities(
8181
entities, key=lambda x: (x["span"][0], -x["span"][1])
8282
)
8383

84-
top_level: list[dict[str, Any]] = []
84+
top_level: list[EntitySpan] = []
8585
for ent in sorted_entities:
8686
is_contained = False
8787
# Only check against entities already in top_level
@@ -149,7 +149,7 @@ def __init__(self, path_model: Optional[str] = None) -> None:
149149

150150
def tag(
151151
self, text: str, top_level_only: bool = False
152-
) -> tuple[list[str], list[dict]]:
152+
) -> tuple[list[str], list[EntitySpan]]:
153153
"""Tag Thai text with nested named entities.
154154
155155
:param str text: Thai text to tag
@@ -159,7 +159,7 @@ def tag(
159159
:return: Tuple of (tokens, entities) where tokens is a list of
160160
tokenized strings and entities is a list of dictionaries
161161
containing 'text', 'span', and 'entity_type' keys.
162-
:rtype: tuple[list[str], list[dict]]
162+
:rtype: tuple[list[str], list[EntitySpan]]
163163
164164
:Example:
165165
::
@@ -228,7 +228,7 @@ def get_ner(
228228

229229

230230
def _entities_to_iob(
231-
tokens: list[str], entities: list[dict]
231+
tokens: list[str], entities: list[EntitySpan]
232232
) -> list[tuple[str, str]]:
233233
"""Convert Thai-NNER entity format to IOB format.
234234
@@ -238,7 +238,7 @@ def _entities_to_iob(
238238
will overwrite the IOB tags of earlier entities.
239239
240240
:param list[str] tokens: List of tokens
241-
:param list[dict] entities: List of entity dictionaries (should be non-overlapping)
241+
:param list[EntitySpan] entities: List of entity dictionaries (should be non-overlapping)
242242
:return: List of (token, tag) tuples in IOB format
243243
:rtype: list[tuple[str, str]]
244244
"""
@@ -263,15 +263,15 @@ def _entities_to_iob(
263263
return result
264264

265265

266-
def _entities_to_html(tokens: list[str], entities: list[dict]) -> str:
266+
def _entities_to_html(tokens: list[str], entities: list[EntitySpan]) -> str:
267267
"""Convert Thai-NNER entity format to HTML-like tags.
268268
269269
This function assumes entities do not overlap. If entities overlap,
270270
tokens between overlapping entities may be skipped. For best results,
271271
use only top-level entities (use get_top_level_entities() to filter).
272272
273273
:param list[str] tokens: List of tokens
274-
:param list[dict] entities: List of entity dictionaries
274+
:param list[EntitySpan] entities: List of entity dictionaries
275275
:return: String with HTML-like entity tags
276276
:rtype: str
277277
"""

0 commit comments

Comments
 (0)