Skip to content

Commit 1bfe60a

Browse files
authored
Merge pull request #124 from bact/dev
Precompile frequently-used pattern/regexes (PAT_ENG, PAT_TCC, PAT_TWOCHARS)
2 parents aaf027a + de13dc1 commit 1bfe60a

File tree

2 files changed

+110
-124
lines changed

2 files changed

+110
-124
lines changed

pythainlp/tokenize/newmm.py

Lines changed: 70 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,132 +1,102 @@
11
# -*- coding: utf-8 -*-
22

3-
'''ตัวตัดคำภาษาไทยโดยใช้หลักการ maximal matching และ TCC
3+
"""ตัวตัดคำภาษาไทยโดยใช้หลักการ maximal matching และ Thai Character Cluster (TCC)
44
พัฒนาโดยคุณ Korakot Chaovavanich
5-
Notebook : https://colab.research.google.com/notebook#fileId=1V1Z657_5eSWPo8rLfVRwA0A5E4vkg7SI
5+
Notebooks:
6+
https://colab.research.google.com/notebook#fileId=1V1Z657_5eSWPo8rLfVRwA0A5E4vkg7SI
67
https://colab.research.google.com/drive/14Ibg-ngZXj15RKwjNwoZlOT32fQBOrBx#scrollTo=MYZ7NzAR7Dmw
7-
'''
8+
"""
89
from __future__ import absolute_import, unicode_literals
10+
911
import re
1012
from collections import defaultdict
11-
from heapq import heappush, heappop # for priority queue
13+
from heapq import heappop, heappush # for priority queue
14+
1215
from pythainlp.tokenize import DEFAULT_DICT_TRIE
1316

17+
from .tcc import tcc_gen
18+
1419
# ช่วยตัดพวกภาษาอังกฤษ เป็นต้น
15-
pat_eng = re.compile(r'''(?x)
20+
PAT_ENG = re.compile(
21+
r"""(?x)
1622
[-a-zA-Z]+| # english
1723
\d[\d,\.]*| # number
1824
[ \t]+| # space
1925
\r?\n # newline
20-
''')
21-
# TCC
22-
pat_tcc = """\
23-
เc็c
24-
เcctาะ
25-
เccีtยะ
26-
เccีtย(?=[เ-ไก-ฮ]|$)
27-
เcc็c
28-
เcิc์c
29-
เcิtc
30-
เcีtยะ?
31-
เcืtอะ?
32-
เc[ิีุู]tย(?=[เ-ไก-ฮ]|$)
33-
เctา?ะ?
34-
cัtวะ
35-
c[ัื]tc[ุิะ]?
36-
c[ิุู]์
37-
c[ะ-ู]t
38-
c็
39-
ct[ะาำ]?
40-
แc็c
41-
แcc์
42-
แctะ
43-
แcc็c
44-
แccc์
45-
โctะ
46-
[เ-ไ]ct
47-
""".replace('c', '[ก-ฮ]').replace('t', '[่-๋]?').split()
48-
49-
50-
def tcc(w):
51-
p = 0
52-
pat = re.compile("|".join(pat_tcc))
53-
while p < len(w):
54-
m = pat.match(w[p:])
55-
if m:
56-
n = m.span()[1]
57-
else:
58-
n = 1
59-
yield w[p:p + n]
60-
p += n
26+
"""
27+
)
28+
29+
PAT_TWOCHARS = re.compile("[ก-ฮ]{,2}$")
6130

6231

6332
def tcc_pos(text):
6433
p_set = set()
6534
p = 0
66-
for w in tcc(text):
35+
for w in tcc_gen(text):
6736
p += len(w)
6837
p_set.add(p)
6938
return p_set
7039

40+
7141
def bfs_paths_graph(graph, start, goal):
72-
queue = [(start, [start])]
73-
while queue:
74-
(vertex, path) = queue.pop(0)
75-
for next in graph[vertex]:
76-
if next == goal:
77-
yield path + [next]
78-
else:
79-
queue.append((next, path+[next]))
42+
queue = [(start, [start])]
43+
while queue:
44+
(vertex, path) = queue.pop(0)
45+
for next in graph[vertex]:
46+
if next == goal:
47+
yield path + [next]
48+
else:
49+
queue.append((next, path + [next]))
50+
8051

8152
def onecut(text, trie):
82-
graph = defaultdict(list) # main data structure
83-
allow_pos = tcc_pos(text) # ตำแหน่งที่ตัด ต้องตรงกับ tcc
84-
85-
q = [0] # min-heap queue
86-
last_p = 0 # last position for yield
87-
while q[0] < len(text):
88-
p = heappop(q)
89-
90-
for w in trie.prefixes(text[p:]):
91-
p_ = p + len(w)
92-
if p_ in allow_pos: # เลือกที่สอดคล้อง tcc
93-
graph[p].append(p_)
94-
if p_ not in q:
95-
heappush(q, p_)
96-
97-
# กรณี length 1 คือ ไม่กำกวมแล้ว ส่งผลลัพธ์ก่อนนี้คืนได้
98-
if len(q)==1:
99-
pp = next(bfs_paths_graph(graph, last_p, q[0]))
100-
# เริ่มต้น last_p = pp[0] เอง
101-
for p in pp[1:]:
102-
yield text[last_p:p]
103-
last_p = p
104-
# สุดท้าย last_p == q[0] เอง
105-
106-
# กรณี length 0 คือ ไม่มีใน dict
107-
if len(q)==0:
108-
m = pat_eng.match(text[p:])
109-
if m: # อังกฤษ, เลข, ว่าง
110-
i = p + m.end()
111-
else: # skip น้อยที่สุด ที่เป็นไปได้
112-
for i in range(p+1, len(text)):
113-
if i in allow_pos: # ใช้ tcc ด้วย
114-
ww = [w for w in trie.prefixes(text[i:]) if (i+len(w) in allow_pos)]
115-
ww = [w for w in ww if not re.match('[ก-ฮ]{,2}$', w)]
116-
m = pat_eng.match(text[i:])
117-
if ww or m:
118-
break
119-
else:
120-
i = len(text)
121-
w = text[p:i]
122-
graph[p].append(i)
123-
yield w
124-
last_p = i
125-
heappush(q, i)
53+
graph = defaultdict(list) # main data structure
54+
allow_pos = tcc_pos(text) # ตำแหน่งที่ตัด ต้องตรงกับ tcc
12655

127-
# ช่วยให้ไม่ต้องพิมพ์ยาวๆ
56+
q = [0] # min-heap queue
57+
last_p = 0 # last position for yield
58+
while q[0] < len(text):
59+
p = heappop(q)
60+
61+
for w in trie.prefixes(text[p:]):
62+
p_ = p + len(w)
63+
if p_ in allow_pos: # เลือกที่สอดคล้อง tcc
64+
graph[p].append(p_)
65+
if p_ not in q:
66+
heappush(q, p_)
12867

68+
# กรณี length 1 คือ ไม่กำกวมแล้ว ส่งผลลัพธ์ก่อนนี้คืนได้
69+
if len(q) == 1:
70+
pp = next(bfs_paths_graph(graph, last_p, q[0]))
71+
# เริ่มต้น last_p = pp[0] เอง
72+
for p in pp[1:]:
73+
yield text[last_p:p]
74+
last_p = p
75+
# สุดท้าย last_p == q[0] เอง
12976

77+
# กรณี length 0 คือ ไม่มีใน dict
78+
if len(q) == 0:
79+
m = PAT_ENG.match(text[p:])
80+
if m: # อังกฤษ, เลข, ว่าง
81+
i = p + m.end()
82+
else: # skip น้อยที่สุด ที่เป็นไปได้
83+
for i in range(p + 1, len(text)):
84+
if i in allow_pos: # ใช้ tcc ด้วย
85+
ww = [w for w in trie.prefixes(text[i:]) if (i + len(w) in allow_pos)]
86+
ww = [w for w in ww if not PAT_TWOCHARS.match(w)]
87+
m = PAT_ENG.match(text[i:])
88+
if ww or m:
89+
break
90+
else:
91+
i = len(text)
92+
w = text[p:i]
93+
graph[p].append(i)
94+
yield w
95+
last_p = i
96+
heappush(q, i)
97+
98+
99+
# ช่วยให้ไม่ต้องพิมพ์ยาวๆ
130100
def mmcut(text, trie=None):
131101
if not trie:
132102
trie = DEFAULT_DICT_TRIE

pythainlp/tokenize/tcc.py

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
# -*- coding: utf-8 -*-
2-
from __future__ import absolute_import,division,unicode_literals,print_function
2+
from __future__ import absolute_import, division, print_function, unicode_literals
3+
4+
import re
5+
36
"""
4-
โปรแกรม TCC ภาษาไทย
5-
เดติด
6-
TCC : Mr.Jakkrit TeCho
7-
grammar : คุณ Wittawat Jitkrittum (https://github.com/wittawatj/jtcc/blob/master/TCC.g)
8-
โค้ด : คุณ Korakot Chaovavanich
7+
Separate Thai text into Thai Character Cluster (TCC).
8+
Based on "Character cluster based Thai information retrieval" (Theeramunkong et al. 2002)
9+
http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.59.2548
10+
11+
Credits:
12+
- TCC: Jakkrit TeCho
13+
- Grammar: Wittawat Jitkrittum https://github.com/wittawatj/jtcc/blob/master/TCC.g
14+
- Python code: Korakot Chaovavanich
915
"""
10-
import re
11-
pat_list = """\
16+
17+
RE_TCC = (
18+
"""\
1219
เc็c
1320
เcctาะ
1421
เccีtยะ
@@ -33,25 +40,34 @@
3340
แccc์
3441
โctะ
3542
[เ-ไ]ct
36-
37-
ฯลฯ
38-
39-
""".replace('c','[ก-ฮ]').replace('t', '[่-๋]?').split()
40-
def tcc1(w):
43+
""".replace(
44+
"c", "[ก-ฮ]"
45+
)
46+
.replace("t", "[่-๋]?")
47+
.split()
48+
)
49+
50+
PAT_TCC = re.compile("|".join(RE_TCC))
51+
52+
53+
def tcc_gen(w):
4154
p = 0
42-
pat = re.compile("|".join(pat_list))
43-
while p<len(w):
44-
m = pat.match(w[p:])
55+
while p < len(w):
56+
m = PAT_TCC.match(w[p:])
4557
if m:
4658
n = m.span()[1]
4759
else:
4860
n = 1
49-
yield w[p:p+n]
61+
yield w[p : p + n]
5062
p += n
51-
def tcc(w, sep='/'):
52-
return sep.join(tcc1(w))
53-
if __name__ == '__main__':
54-
print(tcc('แมวกิน'))
55-
print(tcc('ประชาชน'))
56-
print(tcc('ขุดหลุม'))
57-
print(tcc('ยินดี'))
63+
64+
65+
def tcc(w, sep="/"):
66+
return sep.join(tcc_gen(w))
67+
68+
69+
if __name__ == "__main__":
70+
print(tcc("แมวกิน"))
71+
print(tcc("ประชาชน"))
72+
print(tcc("ขุดหลุม"))
73+
print(tcc("ยินดี"))

0 commit comments

Comments
 (0)