|
1 | 1 | # -*- coding: utf-8 -*- |
2 | 2 |
|
3 | | -'''ตัวตัดคำภาษาไทยโดยใช้หลักการ maximal matching และ TCC |
| 3 | +"""ตัวตัดคำภาษาไทยโดยใช้หลักการ maximal matching และ Thai Character Cluster (TCC) |
4 | 4 | พัฒนาโดยคุณ 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 |
6 | 7 | https://colab.research.google.com/drive/14Ibg-ngZXj15RKwjNwoZlOT32fQBOrBx#scrollTo=MYZ7NzAR7Dmw |
7 | | -''' |
| 8 | +""" |
8 | 9 | from __future__ import absolute_import, unicode_literals |
| 10 | + |
9 | 11 | import re |
10 | 12 | from collections import defaultdict |
11 | | -from heapq import heappush, heappop # for priority queue |
| 13 | +from heapq import heappop, heappush # for priority queue |
| 14 | + |
12 | 15 | from pythainlp.tokenize import DEFAULT_DICT_TRIE |
13 | 16 |
|
| 17 | +from .tcc import tcc_gen |
| 18 | + |
14 | 19 | # ช่วยตัดพวกภาษาอังกฤษ เป็นต้น |
15 | | -pat_eng = re.compile(r'''(?x) |
| 20 | +PAT_ENG = re.compile( |
| 21 | + r"""(?x) |
16 | 22 | [-a-zA-Z]+| # english |
17 | 23 | \d[\d,\.]*| # number |
18 | 24 | [ \t]+| # space |
19 | 25 | \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}$") |
61 | 30 |
|
62 | 31 |
|
63 | 32 | def tcc_pos(text): |
64 | 33 | p_set = set() |
65 | 34 | p = 0 |
66 | | - for w in tcc(text): |
| 35 | + for w in tcc_gen(text): |
67 | 36 | p += len(w) |
68 | 37 | p_set.add(p) |
69 | 38 | return p_set |
70 | 39 |
|
| 40 | + |
71 | 41 | 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 | + |
80 | 51 |
|
81 | 52 | 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 |
126 | 55 |
|
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_) |
128 | 67 |
|
| 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] เอง |
129 | 76 |
|
| 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 | +# ช่วยให้ไม่ต้องพิมพ์ยาวๆ |
130 | 100 | def mmcut(text, trie=None): |
131 | 101 | if not trie: |
132 | 102 | trie = DEFAULT_DICT_TRIE |
|
0 commit comments