From 61f787f53223c274898140f15a067795cb3d6d66 Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Sat, 20 Apr 2019 13:28:39 +0200 Subject: [PATCH 1/9] merge and refactor word_tokenize and dict_word_tokenize --- pythainlp/tokenize/__init__.py | 124 ++++++++++++++------------------- 1 file changed, 54 insertions(+), 70 deletions(-) diff --git a/pythainlp/tokenize/__init__.py b/pythainlp/tokenize/__init__.py index ce3672f4a..c210df754 100644 --- a/pythainlp/tokenize/__init__.py +++ b/pythainlp/tokenize/__init__.py @@ -3,6 +3,7 @@ Thai tokenizers """ import re +import sys from typing import Iterable, List, Union from pythainlp.corpus import get_corpus, thai_syllables, thai_words @@ -14,20 +15,21 @@ def word_tokenize( - text: str, engine: str = "newmm", whitespaces: bool = True + text: str, custom_dict: Trie = None, engine: str = "newmm", whitespaces: bool = True ) -> List[str]: """ :param str text: text to be tokenized :param str engine: tokenizer to be used - :param bool whitespaces: True to output no whitespace, a common mark of end of phrase in Thai + :param dict custom_dict: a dictionary trie + :param bool whitespaces: True to output no whitespace, a common mark for end of phrase in Thai :Parameters for engine: * newmm (default) - dictionary-based, Maximum Matching + Thai Character Cluster * longest - dictionary-based, Longest Matching - * icu - wrapper for ICU (International Components for Unicode, using PyICU), dictionary-based * deepcut - wrapper for deepcut, language-model-based https://github.com/rkcosmos/deepcut - * ulmfit - use newmm engine with a specific dictionary for use with thai2vec + * icu - wrapper for ICU (International Components for Unicode, using PyICU), dictionary-based + * ulmfit - for thai2fit + * a custom_dict can be provided for newmm, longest, and deepcut :return: list of words, tokenized from the text - **Example**:: >>> from pythainlp.tokenize import word_tokenize >>> text = "โอเคบ่พวกเรารักภาษาบ้านเกิด" @@ -36,88 +38,72 @@ def word_tokenize( >>> word_tokenize(text, engine="icu") ['โอ', 'เค', 'บ่', 'พวก', 'เรา', 'รัก', 'ภาษา', 'บ้าน', 'เกิด'] """ - if not text: + if not text or not isinstance(text, str): return [] + segments = [] if engine == "newmm" or engine == "onecut": from .newmm import segment - elif engine == "longest" or engine == "longest-matching": + + segments = segment(text, custom_dict) + elif engine == "longest": from .longest import segment - elif engine == "ulmfit": - from .newmm import segment as segment_ - def segment(text): - return segment_(text, custom_dict=FROZEN_DICT_TRIE) + segments = segment(text, custom_dict) + elif engine == "mm" or engine == "multi_cut": + from .multi_cut import segment + + segments = segment(text, custom_dict) + elif engine == "deepcut": # deepcut can optionally use dictionary + from .deepcut import segment + + if custom_dict: + custom_dict = list(custom_dict) + segments = segment(text, custom_dict) + else: + segments = segment(text) + elif engine == "ulmfit": # ulmfit has its own specific dictionary + from .newmm import segment + segments = segment(text, custom_dict=FROZEN_DICT_TRIE) elif engine == "icu": from .pyicu import segment - elif engine == "deepcut": - from .deepcut import segment - elif engine == "mm" or engine == "multi_cut": - from .multi_cut import segment + + segments = segment(text) else: # default, use "newmm" engine from .newmm import segment - segments = segment(text) + if custom_dict: + custom_dict = dict_trie(custom_dict) + segments = segment(text, custom_dict) if whitespaces: - return segments + segments = [token.strip(" ") for token in segments if token.strip(" ")] - return [token.strip(" ") for token in segments if token.strip(" ")] + return segments def dict_word_tokenize( text: str, - custom_dict: Union[Trie, Iterable[str], str] = DEFAULT_DICT_TRIE, + custom_dict: Trie = DEFAULT_DICT_TRIE, engine: str = "newmm", whitespaces: bool = True, ) -> List[str]: """ - :meth:`dict_word_tokenize` tokenizes word based on the dictionary you provide. The format has to be in trie data structure. + :meth: Deprecated. Please use `word_tokenize()` with `custom_dict` argument instead. :param str text: text to be tokenized :param dict custom_dict: a dictionary trie, or an iterable of words, or a string of dictionary path :param str engine: choose between different options of engine to token (newmm [default], mm, longest, and deepcut) :param bool whitespaces: True to output no whitespace, a common mark of end of phrase in Thai :return: list of words - **Example**:: - >>> from pythainlp.tokenize import dict_word_tokenize, dict_trie - >>> words = ["แมว", "ดี"] - >>> trie = dict_trie(words) - >>> dict_word_tokenize("แมวดีดีแมว", trie) - ['แมว', 'ดี', 'ดี', 'แมว'] """ - - if not text: - return [] - - if engine == "newmm" or engine == "onecut": - from .newmm import segment - - custom_dict = dict_trie(custom_dict) - elif engine == "longest" or engine == "longest-matching": - from .longest import segment - - custom_dict = dict_trie(custom_dict) - elif engine == "mm" or engine == "multi_cut": - from .multi_cut import segment - - custom_dict = dict_trie(custom_dict) - elif engine == "deepcut": - from .deepcut import segment - - if not isinstance(custom_dict, List) and not isinstance(custom_dict, str): - custom_dict = list(custom_dict) - else: # default, use "newmm" engine - from .newmm import segment - - custom_dict = dict_trie(custom_dict) - - segments = segment(text, custom_dict) - - if whitespaces: - return segments - - return [token.strip(" ") for token in segments if token.strip(" ")] + print( + "Deprecated. Use `word_tokenize()` with `custom_dict` argument instead.", + file=sys.stderr, + ) + return word_tokenize( + text=text, custom_dict=custom_dict, engine=engine, whitespaces=whitespaces + ) def sent_tokenize(text: str, engine: str = "whitespace+newline") -> List[str]: @@ -178,7 +164,7 @@ def syllable_tokenize(text: str) -> List[str]: words = word_tokenize(text) trie = dict_trie(dict_source=thai_syllables()) for word in words: - tokens.extend(dict_word_tokenize(text=word, custom_dict=trie)) + tokens.extend(word_tokenize(text=word, custom_dict=trie)) return tokens @@ -214,20 +200,20 @@ def dict_trie(dict_source: Union[str, Iterable[str], Trie]) -> Trie: class Tokenizer: def __init__( - self, custom_dict: Union[str, Iterable] = None, tokenize_engine: str = "newmm" + self, custom_dict: Union[Trie, Iterable[str], str] = None, engine: str = "newmm" ): """ Initialize tokenizer object - :param str custom_dict: a file path or a list of vocaburaies to be used to create a trie (default - original lexitron) - :param str tokenize_engine: choose between different options of engine to token (newmm, mm, longest) + :param str custom_dict: a file path or a list of vocaburaies to be used to create a trie + :param str engine: choose between different options of engine to token (newmm, mm, longest) """ self.__trie_dict = None - self.word_engine = tokenize_engine + self.__engine = engine if custom_dict: self.__trie_dict = dict_trie(custom_dict) else: - self.__trie_dict = dict_trie(thai_words()) + self.__trie_dict = DEFAULT_DICT_TRIE def word_tokenize(self, text: str) -> List[str]: """ @@ -235,12 +221,10 @@ def word_tokenize(self, text: str) -> List[str]: :return: list of words, tokenized from the text """ - return dict_word_tokenize( - text, custom_dict=self.__trie_dict, engine=self.word_engine - ) + return word_tokenize(text, custom_dict=self.__trie_dict, engine=self.__engine) - def set_tokenize_engine(self, name_engine: str) -> None: + def set_tokenize_engine(self, engine: str) -> None: """ - :param str name_engine: choose between different options of engine to token (newmm, mm, longest) + :param str engine: choose between different options of engine to token (newmm, mm, longest) """ - self.word_engine = name_engine + self.__engine = engine From e6d3fb00bf82c5a82c664eca9afece9155ec996a Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Sat, 20 Apr 2019 13:44:25 +0200 Subject: [PATCH 2/9] update test for dict_word_tokenize --- tests/__init__.py | 67 +++++++++++++++++------------------------------ 1 file changed, 24 insertions(+), 43 deletions(-) diff --git a/tests/__init__.py b/tests/__init__.py index 067d6975f..06677a556 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -292,34 +292,7 @@ def test_ner(self): # ### pythainlp.tokenize def test_dict_word_tokenize(self): - self.assertEqual(dict_word_tokenize("", custom_dict=FROZEN_DICT_TRIE), []) - self.assertIsNotNone( - dict_word_tokenize("รถไฟฟ้ากรุงเทพBTSหูว์ค์", custom_dict=FROZEN_DICT_TRIE) - ) - self.assertIsNotNone(dict_trie(())) - self.assertIsNotNone( - dict_word_tokenize( - "รถไฟฟ้ากรุงเทพBTSหูว์ค์", custom_dict=FROZEN_DICT_TRIE, engine="newmm" - ) - ) - self.assertIsNotNone( - dict_word_tokenize( - "รถไฟฟ้ากรุงเทพBTSหูว์ค์", - custom_dict=FROZEN_DICT_TRIE, - engine="longest", - whitespaces=False, - ) - ) - self.assertIsNotNone( - dict_word_tokenize( - "รถไฟฟ้ากรุงเทพBTSหูว์ค์", custom_dict=FROZEN_DICT_TRIE, engine="mm" - ) - ) - self.assertIsNotNone( - dict_word_tokenize( - "รถไฟฟ้ากรุงเทพBTSหูว์ค์", custom_dict=FROZEN_DICT_TRIE, engine="XX" - ) - ) + self.assertEqual(dict_word_tokenize(""), []) def test_etcc(self): self.assertEqual(etcc.segment(""), "") @@ -337,10 +310,21 @@ def test_word_tokenize(self): word_tokenize("ฉันรักภาษาไทยเพราะฉันเป็นคนไทย"), ["ฉัน", "รัก", "ภาษาไทย", "เพราะ", "ฉัน", "เป็น", "คนไทย"], ) - self.assertIsNotNone(word_tokenize("ทดสอบ", engine="ulmfit")) - self.assertIsNotNone(word_tokenize("ทดสอบ", engine="XX")) - self.assertIsNotNone(word_tokenize("ทดสอบ", engine="deepcut")) - self.assertIsNotNone(word_tokenize("", engine="deepcut")) + + self.assertIsNotNone(word_tokenize("หมอนทองตากลมหูว์", engine="newmm")) + self.assertIsNotNone(word_tokenize("หมอนทองตากลมหูว์", engine="mm")) + self.assertIsNotNone(word_tokenize("หมอนทองตากลมหูว์", engine="longest")) + self.assertIsNotNone(word_tokenize("หมอนทองตากลมหูว์", engine="ulmfit")) + self.assertIsNotNone(word_tokenize("หมอนทองตากลมหูว์", engine="icu")) + self.assertIsNotNone(word_tokenize("หมอนทองตากลมหูว์", engine="deepcut")) + self.assertIsNotNone(word_tokenize("หมอนทองตากลมหูว์", engine="XX")) + + self.assertIsNotNone(dict_trie(())) + self.assertIsNotNone(dict_trie(("ทดสอบ", "สร้าง", "Trie"))) + self.assertIsNotNone(dict_trie(["ทดสอบ", "สร้าง", "Trie"])) + self.assertIsNotNone(dict_trie(thai_words())) + + self.assertIsNotNone(word_tokenize("รถไฟฟ้าBTS", custom_dict=DEFAULT_DICT_TRIE)) def test_Tokenizer(self): t_test = Tokenizer() @@ -359,12 +343,9 @@ def test_word_tokenize_deepcut(self): self.assertEqual(tokenize_deepcut.segment(""), []) self.assertIsNotNone(tokenize_deepcut.segment("ทดสอบ", DEFAULT_DICT_TRIE)) self.assertIsNotNone(tokenize_deepcut.segment("ทดสอบ", ["ทด", "สอบ"])) - self.assertIsNotNone(dict_word_tokenize("ทดสอบ", engine="deepcut")) - self.assertIsNotNone( - dict_word_tokenize("ทดสอบ", engine="deepcut", custom_dict=["ทด", "สอบ"]) - ) + self.assertIsNotNone(word_tokenize("ทดสอบ", engine="deepcut")) - def test_word_tokenize_longest_matching(self): + def test_word_tokenize_longest(self): self.assertEqual(longest.segment(None), []) self.assertEqual(longest.segment(""), []) self.assertEqual( @@ -444,12 +425,12 @@ def test_romanize(self): self.assertEqual(romanize_royin(""), "") self.assertEqual(romanize_royin("หาย"), "hai") self.assertEqual(romanize_royin("หมอก"), "mok") - #self.assertEqual(romanize_royin("มหา"), "maha") # not pass - #self.assertEqual(romanize_royin("หยาก"), "yak") # not pass - #self.assertEqual(romanize_royin("อยาก"), "yak") # not pass - #self.assertEqual(romanize_royin("ยมก"), "yamok") # not pass - #self.assertEqual(romanize_royin("กลัว"), "klua") # not pass - #self.assertEqual(romanize_royin("กลัว"), "klua") # not pass + # self.assertEqual(romanize_royin("มหา"), "maha") # not pass + # self.assertEqual(romanize_royin("หยาก"), "yak") # not pass + # self.assertEqual(romanize_royin("อยาก"), "yak") # not pass + # self.assertEqual(romanize_royin("ยมก"), "yamok") # not pass + # self.assertEqual(romanize_royin("กลัว"), "klua") # not pass + # self.assertEqual(romanize_royin("กลัว"), "klua") # not pass self.assertEqual(romanize("แมว", engine="royin"), "maeo") self.assertEqual(romanize("เดือน", engine="royin"), "duean") From efcbdae0e0a51903127b7a9062794a8edf2337d0 Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Sat, 20 Apr 2019 14:03:08 +0200 Subject: [PATCH 3/9] `word_tokenize()' argument `whitespaces` is now `keep_whitespace` --- pythainlp/tokenize/__init__.py | 8 ++++---- tests/__init__.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pythainlp/tokenize/__init__.py b/pythainlp/tokenize/__init__.py index c210df754..4c33ea0fb 100644 --- a/pythainlp/tokenize/__init__.py +++ b/pythainlp/tokenize/__init__.py @@ -15,13 +15,13 @@ def word_tokenize( - text: str, custom_dict: Trie = None, engine: str = "newmm", whitespaces: bool = True + text: str, custom_dict: Trie = None, engine: str = "newmm", keep_whitespace: bool = True ) -> List[str]: """ :param str text: text to be tokenized :param str engine: tokenizer to be used :param dict custom_dict: a dictionary trie - :param bool whitespaces: True to output no whitespace, a common mark for end of phrase in Thai + :param bool keep_whitespace: True to keep whitespaces, a common mark for end of phrase in Thai :Parameters for engine: * newmm (default) - dictionary-based, Maximum Matching + Thai Character Cluster * longest - dictionary-based, Longest Matching @@ -77,7 +77,7 @@ def word_tokenize( custom_dict = dict_trie(custom_dict) segments = segment(text, custom_dict) - if whitespaces: + if not keep_whitespace: segments = [token.strip(" ") for token in segments if token.strip(" ")] return segments @@ -102,7 +102,7 @@ def dict_word_tokenize( file=sys.stderr, ) return word_tokenize( - text=text, custom_dict=custom_dict, engine=engine, whitespaces=whitespaces + text=text, custom_dict=custom_dict, engine=engine, keep_whitespace=whitespaces ) diff --git a/tests/__init__.py b/tests/__init__.py index 06677a556..b477a51c6 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -372,7 +372,7 @@ def test_word_tokenize_newmm(self): ["ฉัน", "รัก", "ภาษาไทย", "เพราะ", "ฉัน", "เป็น", "คนไทย"], ) self.assertEqual( - word_tokenize("สวัสดีครับ สบายดีไหมครับ", engine="newmm"), + word_tokenize("สวัสดีครับ สบายดีไหมครับ", engine="newmm", keep_whitespace=True), ["สวัสดี", "ครับ", " ", "สบายดี", "ไหม", "ครับ"], ) self.assertEqual( @@ -380,7 +380,7 @@ def test_word_tokenize_newmm(self): ) self.assertEqual(word_tokenize("จุ๋มง่วง", engine="newmm"), ["จุ๋ม", "ง่วง"]) self.assertEqual( - word_tokenize("จุ๋ม ง่วง", engine="newmm", whitespaces=False), + word_tokenize("จุ๋ม ง่วง", engine="newmm", keep_whitespace=False), ["จุ๋ม", "ง่วง"], ) From 18d0b8058be03170a9ba0408da8adbdb905cf9ac Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Sat, 20 Apr 2019 18:34:55 +0200 Subject: [PATCH 4/9] fix word_tokenize() call in FrequencySummarizer --- pythainlp/summarize/freq.py | 4 ++-- pythainlp/tokenize/newmm.py | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pythainlp/summarize/freq.py b/pythainlp/summarize/freq.py index f39998e53..bc43ef437 100644 --- a/pythainlp/summarize/freq.py +++ b/pythainlp/summarize/freq.py @@ -37,9 +37,9 @@ def __compute_frequencies(self, word_tokenized_sents): def __rank(self, ranking, n: int): return nlargest(n, ranking, key=ranking.get) - def summarize(self, text: str, n: int, tokenizer: str) -> List[str]: + def summarize(self, text: str, n: int, tokenizer: str = "newmm") -> List[str]: sents = sent_tokenize(text) - word_tokenized_sents = [word_tokenize(sent, tokenizer) for sent in sents] + word_tokenized_sents = [word_tokenize(sent, engine=tokenizer) for sent in sents] self.__freq = self.__compute_frequencies(word_tokenized_sents) ranking = defaultdict(int) diff --git a/pythainlp/tokenize/newmm.py b/pythainlp/tokenize/newmm.py index 88b766eea..b7437a631 100644 --- a/pythainlp/tokenize/newmm.py +++ b/pythainlp/tokenize/newmm.py @@ -30,7 +30,7 @@ _PAT_TWOCHARS = re.compile("[ก-ฮ]{,2}$") -def bfs_paths_graph(graph, start, goal): +def _bfs_paths_graph(graph, start, goal): queue = [(start, [start])] while queue: (vertex, path) = queue.pop(0) @@ -41,9 +41,9 @@ def bfs_paths_graph(graph, start, goal): queue.append((next, path + [next])) -def onecut(text: str, custom_dict: Trie): +def _onecut(text: str, custom_dict: Trie): graph = defaultdict(list) # main data structure - allow_pos = tcc_pos(text) # ตำแหน่งที่ตัด ต้องตรงกับ tcc + allow_pos = tcc_pos(text) # separating position should aligned with TCC q = [0] # min-heap queue last_p = 0 # last position for yield @@ -59,7 +59,7 @@ def onecut(text: str, custom_dict: Trie): # กรณี length 1 คือ ไม่กำกวมแล้ว ส่งผลลัพธ์ก่อนนี้คืนได้ if len(q) == 1: - pp = next(bfs_paths_graph(graph, last_p, q[0])) + pp = next(_bfs_paths_graph(graph, last_p, q[0])) # เริ่มต้น last_p = pp[0] เอง for p in pp[1:]: yield text[last_p:p] @@ -99,4 +99,4 @@ def segment(text: str, custom_dict: Trie = None) -> List[str]: if not custom_dict: custom_dict = DEFAULT_DICT_TRIE - return list(onecut(text, custom_dict)) + return list(_onecut(text, custom_dict)) From 3161c38453cdb22e6f8bf5f0b891d945b9ff9685 Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Sat, 20 Apr 2019 19:15:51 +0200 Subject: [PATCH 5/9] more test cases for word_tokenize with custom_dict --- notebooks/pythainlp-get-started.ipynb | 73 ++++++++++++++------------- tests/__init__.py | 11 +++- 2 files changed, 49 insertions(+), 35 deletions(-) diff --git a/notebooks/pythainlp-get-started.ipynb b/notebooks/pythainlp-get-started.ipynb index 8e5577ab9..8d5b1c363 100644 --- a/notebooks/pythainlp-get-started.ipynb +++ b/notebooks/pythainlp-get-started.ipynb @@ -308,7 +308,7 @@ "\n", "print(\"sent_tokenize:\", sent_tokenize(text))\n", "print(\"word_tokenize:\", word_tokenize(text))\n", - "print(\"word_tokenize, without whitespace:\", word_tokenize(text, whitespaces=False))" + "print(\"word_tokenize, without whitespace:\", word_tokenize(text, keep_whitespace=False))" ] }, { @@ -729,15 +729,15 @@ { "data": { "text/plain": [ - "[('ลุ่น', 4),\n", - " ('คั่น', 53),\n", - " ('ไก่ป่า', 29),\n", - " ('ปริพาชก', 4),\n", - " ('สิกขาบท', 4),\n", - " ('คัดลายมือ', 2),\n", - " ('เลียบ', 53),\n", - " ('เกือบๆ', 6),\n", - " ('จันทรคติ', 6)]" + "[('สะใภ้', 69),\n", + " ('เบ็ด', 120),\n", + " ('น้ำตก', 64),\n", + " ('กร', 170),\n", + " ('จีบ', 37),\n", + " ('อั้งยี่', 4),\n", + " ('พิจิตร', 12),\n", + " ('นักมวย', 30),\n", + " ('มด', 133)]" ] }, "execution_count": 28, @@ -880,9 +880,7 @@ { "data": { "text/plain": [ - "[[('ราชกิจจานุเบกษา', 'NCMN'),\n", - " ('เผยแพร่', 'VACT'),\n", - " ('ประกาศสำนักนายกฯ', 'NCMN'),\n", + "[[('ประกาศสำนักนายกฯ', 'NCMN'),\n", " (' ', 'PUNC'),\n", " ('ให้', 'JSBR'),\n", " (' ', 'PUNC'),\n", @@ -898,11 +896,6 @@ " ('แต่งตั้ง', 'VACT'),\n", " ('ให้', 'JSBR'),\n", " ('เป็น', 'VSTA'),\n", - " ('ข้าราชการ', 'NCMN'),\n", - " ('พลเรือน', 'NCMN'),\n", - " ('สามัญ', 'NCMN'),\n", - " ('ตำแหน่ง', 'NCMN'),\n", - " (' ', 'PUNC'),\n", " (\"'อธิบดีกรมประชาสัมพันธ์'\", 'NCMN')]]" ] }, @@ -912,11 +905,10 @@ } ], "source": [ - "sents = [[\"ราชกิจจานุเบกษา\", \"เผยแพร่\", \"ประกาศสำนักนายกฯ\", \" \", \"ให้\",\n", + "sents = [[\"ประกาศสำนักนายกฯ\", \" \", \"ให้\",\n", " \" \", \"'พล.ท.สรรเสริญ แก้วกำเนิด'\", \" \", \"พ้นจากตำแหน่ง\",\n", " \" \", \"ผู้ทรงคุณวุฒิพิเศษ\", \"กองทัพบก\", \" \", \"กระทรวงกลาโหม\"],\n", - " [\"และ\",\"แต่งตั้ง\",\"ให้\", \"เป็น\", \"ข้าราชการ\", \"พลเรือน\", \"สามัญ\",\n", - " \"ตำแหน่ง\", \" \", \"'อธิบดีกรมประชาสัมพันธ์'\"]]\n", + " [\"และ\", \"แต่งตั้ง\", \"ให้\", \"เป็น\", \"'อธิบดีกรมประชาสัมพันธ์'\"]]\n", "\n", "pos_tag_sents(sents)" ] @@ -941,23 +933,36 @@ { "data": { "text/plain": [ - "[('วันที่', 'NOUN', 'O'),\n", - " (' ', 'PUNCT', 'O'),\n", - " ('15', 'NUM', 'B-DATE'),\n", - " (' ', 'PUNCT', 'I-DATE'),\n", - " ('ก.ย.', 'NOUN', 'I-DATE'),\n", - " (' ', 'PUNCT', 'I-DATE'),\n", - " ('61', 'NUM', 'I-DATE'),\n", - " (' ', 'PUNCT', 'O'),\n", - " ('ทดสอบ', 'VERB', 'O'),\n", - " ('ระบบ', 'NOUN', 'O'),\n", - " ('เวลา', 'NOUN', 'O'),\n", + "[('วันที่', 'NOUN', 'I-MONEY'),\n", + " (' ', 'PUNCT', 'I-MONEY'),\n", + " ('15', 'NUM', 'I-MONEY'),\n", + " (' ', 'PUNCT', 'I-MONEY'),\n", + " ('ก.ย.', 'NOUN', 'I-MONEY'),\n", + " (' ', 'PUNCT', 'I-MONEY'),\n", + " ('61', 'NUM', 'I-MONEY'),\n", + " (' ', 'PUNCT', 'I-MONEY'),\n", + " ('ทดสอบ', 'VERB', 'I-MONEY'),\n", + " ('ระบบ', 'NOUN', 'I-MONEY'),\n", " (' ', 'PUNCT', 'O'),\n", " ('14', 'NOUN', 'B-TIME'),\n", " (':', 'PUNCT', 'I-TIME'),\n", " ('49', 'NUM', 'I-TIME'),\n", " (' ', 'PUNCT', 'I-TIME'),\n", - " ('น.', 'NOUN', 'I-TIME')]" + " ('น.', 'NOUN', 'I-TIME'),\n", + " (' ', 'PUNCT', 'O'),\n", + " ('เดินทาง', 'VERB', 'O'),\n", + " ('จาก', 'ADP', 'O'),\n", + " ('กทม.', 'NOUN', 'B-LOCATION'),\n", + " ('ไป', 'AUX', 'O'),\n", + " ('จังหวัด', 'NOUN', 'B-LOCATION'),\n", + " ('กำแพงเพชร', 'NOUN', 'I-LOCATION'),\n", + " (' ', 'PUNCT', 'I-MONEY'),\n", + " ('ตั๋ว', 'NOUN', 'I-MONEY'),\n", + " ('ราคา', 'NOUN', 'I-MONEY'),\n", + " (' ', 'PUNCT', 'I-MONEY'),\n", + " ('297', 'NUM', 'I-MONEY'),\n", + " (' ', 'PUNCT', 'I-MONEY'),\n", + " ('บาท', 'NOUN', 'I-MONEY')]" ] }, "execution_count": 35, @@ -969,7 +974,7 @@ "from pythainlp.tag.named_entity import ThaiNameTagger\n", "\n", "ner = ThaiNameTagger()\n", - "ner.get_ner(\"วันที่ 15 ก.ย. 61 ทดสอบระบบเวลา 14:49 น.\")" + "ner.get_ner(\"วันที่ 15 ก.ย. 61 ทดสอบระบบ 14:49 น. เดินทางจากกทม.ไปจังหวัดกำแพงเพชร ตั๋วราคา 297 บาท\")" ] }, { diff --git a/tests/__init__.py b/tests/__init__.py index b477a51c6..d448e23cb 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -323,8 +323,15 @@ def test_word_tokenize(self): self.assertIsNotNone(dict_trie(("ทดสอบ", "สร้าง", "Trie"))) self.assertIsNotNone(dict_trie(["ทดสอบ", "สร้าง", "Trie"])) self.assertIsNotNone(dict_trie(thai_words())) + self.assertIsNotNone(dict_trie(FROZEN_DICT_TRIE)) self.assertIsNotNone(word_tokenize("รถไฟฟ้าBTS", custom_dict=DEFAULT_DICT_TRIE)) + self.assertIsNotNone( + word_tokenize("ทดสอบ", engine="deepcut", custom_dict=FROZEN_DICT_TRIE) + ) + self.assertIsNotNone( + word_tokenize("ทดสอบ", engine="XX", custom_dict=FROZEN_DICT_TRIE) + ) def test_Tokenizer(self): t_test = Tokenizer() @@ -372,7 +379,9 @@ def test_word_tokenize_newmm(self): ["ฉัน", "รัก", "ภาษาไทย", "เพราะ", "ฉัน", "เป็น", "คนไทย"], ) self.assertEqual( - word_tokenize("สวัสดีครับ สบายดีไหมครับ", engine="newmm", keep_whitespace=True), + word_tokenize( + "สวัสดีครับ สบายดีไหมครับ", engine="newmm", keep_whitespace=True + ), ["สวัสดี", "ครับ", " ", "สบายดี", "ไหม", "ครับ"], ) self.assertEqual( From 90d1ac6d307041e72aa76b48f907486ee235085a Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Sat, 20 Apr 2019 23:22:03 +0200 Subject: [PATCH 6/9] - more test cases for "longest" tokenizer - check if instance is str before tokenizing --- docs/archive/pythainlp-dev-thai.md | 2 +- notebooks/pythainlp-get-started.ipynb | 165 +++++++++++++++---------- notebooks/wongnai_classification.ipynb | 2 +- pythainlp/soundex/lk82.py | 2 +- pythainlp/soundex/metasound.py | 2 +- pythainlp/soundex/udom83.py | 2 +- pythainlp/tokenize/__init__.py | 16 +-- pythainlp/tokenize/deepcut.py | 2 +- pythainlp/tokenize/etcc.py | 2 +- pythainlp/tokenize/longest.py | 2 +- pythainlp/tokenize/multi_cut.py | 9 +- pythainlp/tokenize/newmm.py | 2 +- pythainlp/tokenize/pyicu.py | 3 +- pythainlp/tokenize/tcc.py | 12 +- pythainlp/transliterate/__init__.py | 4 +- pythainlp/util/digitconv.py | 6 +- pythainlp/util/thai.py | 2 +- tests/__init__.py | 21 ++-- 18 files changed, 150 insertions(+), 106 deletions(-) diff --git a/docs/archive/pythainlp-dev-thai.md b/docs/archive/pythainlp-dev-thai.md index 943b51d7f..ec7c575d1 100644 --- a/docs/archive/pythainlp-dev-thai.md +++ b/docs/archive/pythainlp-dev-thai.md @@ -387,7 +387,7 @@ rank(["แมง", "แมง", "คน"]) # Counter({'แมง': 2, 'คน': กฎที่รองรับ - lk82 - กฎการเข้ารหัสซาวน์เด็กซ์ของ วิชิตหล่อจีระชุณห์กุล และ เจริญ คุวินทร์พันธุ์ - udom83 - กฎการเข้ารหัสซาวน์เด็กซ์ของ วรรณี อุดมพาณิชย์ -- metasound - กฎการเข้ารหัส MetaSoound ของ Snae & Brückner (2009) +- metasound - กฎการเข้ารหัส MetaSound ของ Snae & Brückner (2009) เครดิต - โค้ด lk82 และ udom83 - Korakot Chaovavanich https://gist.github.com/korakot/0b772e09340cac2f493868da035597e8 diff --git a/notebooks/pythainlp-get-started.ipynb b/notebooks/pythainlp-get-started.ipynb index 8d5b1c363..bc131bd4f 100644 --- a/notebooks/pythainlp-get-started.ipynb +++ b/notebooks/pythainlp-get-started.ipynb @@ -382,6 +382,42 @@ "print(\"custom:\", custom_tokenizer.word_tokenize(text))" ] }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['มี|ความ|เป็น|ไป|ได้|อย่าง|ไร|บ้าง|',\n", + " 'มี|ความ|เป็นไป|ได้|อย่าง|ไร|บ้าง|',\n", + " 'มี|ความ|เป็นไปได้|อย่าง|ไร|บ้าง|',\n", + " 'มี|ความเป็นไป|ได้|อย่าง|ไร|บ้าง|',\n", + " 'มี|ความเป็นไปได้|อย่าง|ไร|บ้าง|',\n", + " 'มี|ความ|เป็น|ไป|ได้|อย่างไร|บ้าง|',\n", + " 'มี|ความ|เป็นไป|ได้|อย่างไร|บ้าง|',\n", + " 'มี|ความ|เป็นไปได้|อย่างไร|บ้าง|',\n", + " 'มี|ความเป็นไป|ได้|อย่างไร|บ้าง|',\n", + " 'มี|ความเป็นไปได้|อย่างไร|บ้าง|',\n", + " 'มี|ความ|เป็น|ไป|ได้|อย่างไรบ้าง|',\n", + " 'มี|ความ|เป็นไป|ได้|อย่างไรบ้าง|',\n", + " 'มี|ความ|เป็นไปได้|อย่างไรบ้าง|',\n", + " 'มี|ความเป็นไป|ได้|อย่างไรบ้าง|',\n", + " 'มี|ความเป็นไปได้|อย่างไรบ้าง|']" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from pythainlp.tokenize.multi_cut import find_all_segment, mmcut, segment\n", + "\n", + "find_all_segment(\"มีความเป็นไปได้อย่างไรบ้าง\")\n" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -393,7 +429,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -402,7 +438,7 @@ "['ป', 'ระ', 'เท', 'ศ', 'ไท', 'ย']" ] }, - "execution_count": 15, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } @@ -422,7 +458,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -431,7 +467,7 @@ "['ป', 'ระ', 'เท', 'ศ', 'ไท', 'ย']" ] }, - "execution_count": 16, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } @@ -444,7 +480,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -453,7 +489,7 @@ "{1, 3, 5, 6, 8, 9}" ] }, - "execution_count": 17, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } @@ -464,7 +500,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 19, "metadata": {}, "outputs": [ { @@ -489,7 +525,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -498,7 +534,7 @@ "'maeo'" ] }, - "execution_count": 19, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } @@ -511,7 +547,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -520,7 +556,7 @@ "'mɛːw'" ] }, - "execution_count": 20, + "execution_count": 21, "metadata": {}, "output_type": "execute_result" } @@ -533,7 +569,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ @@ -550,7 +586,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 23, "metadata": {}, "outputs": [ { @@ -559,7 +595,7 @@ "True" ] }, - "execution_count": 22, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" } @@ -581,7 +617,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 24, "metadata": {}, "outputs": [ { @@ -605,7 +641,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 25, "metadata": {}, "outputs": [ { @@ -645,7 +681,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 26, "metadata": {}, "outputs": [ { @@ -654,7 +690,7 @@ "['เหลียม', 'เหลือม']" ] }, - "execution_count": 25, + "execution_count": 26, "metadata": {}, "output_type": "execute_result" } @@ -668,7 +704,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 27, "metadata": {}, "outputs": [ { @@ -677,7 +713,7 @@ "'เหลียม'" ] }, - "execution_count": 26, + "execution_count": 27, "metadata": {}, "output_type": "execute_result" } @@ -700,7 +736,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 28, "metadata": {}, "outputs": [ { @@ -723,24 +759,24 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "[('สะใภ้', 69),\n", - " ('เบ็ด', 120),\n", - " ('น้ำตก', 64),\n", - " ('กร', 170),\n", - " ('จีบ', 37),\n", - " ('อั้งยี่', 4),\n", - " ('พิจิตร', 12),\n", - " ('นักมวย', 30),\n", - " ('มด', 133)]" + "[('แสดงทรรศนะ', 2),\n", + " ('เจ้าอธิการ', 4),\n", + " ('วินิจฉัย', 133),\n", + " ('อ่อนหวาน', 90),\n", + " ('ไตรตรา', 3),\n", + " ('คำๆ', 15),\n", + " ('ปริ่ม', 13),\n", + " ('มนุ', 3),\n", + " ('ส้าง', 5)]" ] }, - "execution_count": 28, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } @@ -758,7 +794,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 30, "metadata": {}, "outputs": [ { @@ -767,7 +803,7 @@ "39977" ] }, - "execution_count": 29, + "execution_count": 30, "metadata": {}, "output_type": "execute_result" } @@ -779,7 +815,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 31, "metadata": {}, "outputs": [ { @@ -788,7 +824,7 @@ "30379" ] }, - "execution_count": 30, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" } @@ -800,7 +836,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 32, "metadata": {}, "outputs": [ { @@ -809,7 +845,7 @@ "76706" ] }, - "execution_count": 31, + "execution_count": 32, "metadata": {}, "output_type": "execute_result" } @@ -821,7 +857,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 33, "metadata": {}, "outputs": [ { @@ -830,7 +866,7 @@ "76700" ] }, - "execution_count": 32, + "execution_count": 33, "metadata": {}, "output_type": "execute_result" } @@ -852,7 +888,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 34, "metadata": {}, "outputs": [ { @@ -861,7 +897,7 @@ "[('การ', 'FIXN'), ('เดินทาง', 'VACT')]" ] }, - "execution_count": 33, + "execution_count": 34, "metadata": {}, "output_type": "execute_result" } @@ -874,7 +910,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 35, "metadata": {}, "outputs": [ { @@ -899,7 +935,7 @@ " (\"'อธิบดีกรมประชาสัมพันธ์'\", 'NCMN')]]" ] }, - "execution_count": 34, + "execution_count": 35, "metadata": {}, "output_type": "execute_result" } @@ -927,22 +963,21 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "[('วันที่', 'NOUN', 'I-MONEY'),\n", - " (' ', 'PUNCT', 'I-MONEY'),\n", - " ('15', 'NUM', 'I-MONEY'),\n", - " (' ', 'PUNCT', 'I-MONEY'),\n", - " ('ก.ย.', 'NOUN', 'I-MONEY'),\n", - " (' ', 'PUNCT', 'I-MONEY'),\n", - " ('61', 'NUM', 'I-MONEY'),\n", - " (' ', 'PUNCT', 'I-MONEY'),\n", - " ('ทดสอบ', 'VERB', 'I-MONEY'),\n", - " ('ระบบ', 'NOUN', 'I-MONEY'),\n", + "[('15', 'NUM', 'B-DATE'),\n", + " (' ', 'PUNCT', 'I-DATE'),\n", + " ('ก.ย.', 'NOUN', 'I-DATE'),\n", + " (' ', 'PUNCT', 'I-DATE'),\n", + " ('61', 'NUM', 'I-DATE'),\n", + " (' ', 'PUNCT', 'O'),\n", + " ('ทดสอบ', 'VERB', 'O'),\n", + " ('ระบบ', 'NOUN', 'O'),\n", + " ('เวลา', 'NOUN', 'O'),\n", " (' ', 'PUNCT', 'O'),\n", " ('14', 'NOUN', 'B-TIME'),\n", " (':', 'PUNCT', 'I-TIME'),\n", @@ -965,7 +1000,7 @@ " ('บาท', 'NOUN', 'I-MONEY')]" ] }, - "execution_count": 35, + "execution_count": 36, "metadata": {}, "output_type": "execute_result" } @@ -974,7 +1009,7 @@ "from pythainlp.tag.named_entity import ThaiNameTagger\n", "\n", "ner = ThaiNameTagger()\n", - "ner.get_ner(\"วันที่ 15 ก.ย. 61 ทดสอบระบบ 14:49 น. เดินทางจากกทม.ไปจังหวัดกำแพงเพชร ตั๋วราคา 297 บาท\")" + "ner.get_ner(\"15 ก.ย. 61 ทดสอบระบบเวลา 14:49 น. เดินทางจากกทม.ไปจังหวัดกำแพงเพชร ตั๋วราคา 297 บาท\")" ] }, { @@ -986,7 +1021,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 37, "metadata": {}, "outputs": [ { @@ -1006,7 +1041,7 @@ "0.99259853" ] }, - "execution_count": 36, + "execution_count": 37, "metadata": {}, "output_type": "execute_result" } @@ -1019,7 +1054,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 38, "metadata": {}, "outputs": [ { @@ -1035,7 +1070,7 @@ "'แมว'" ] }, - "execution_count": 37, + "execution_count": 38, "metadata": {}, "output_type": "execute_result" } @@ -1053,7 +1088,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 39, "metadata": {}, "outputs": [ { @@ -1062,7 +1097,7 @@ "'หนึ่งล้านสองแสนสามหมื่นสี่พันห้าร้อยหกสิบเจ็ดล้านแปดแสนเก้าหมื่นหนึ่งร้อยยี่สิบสามบาทสี่สิบห้าสตางค์'" ] }, - "execution_count": 38, + "execution_count": 39, "metadata": {}, "output_type": "execute_result" } @@ -1075,7 +1110,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 40, "metadata": {}, "outputs": [ { @@ -1084,7 +1119,7 @@ "'หนึ่งบาทเก้าสิบเอ็ดสตางค์'" ] }, - "execution_count": 39, + "execution_count": 40, "metadata": {}, "output_type": "execute_result" } diff --git a/notebooks/wongnai_classification.ipynb b/notebooks/wongnai_classification.ipynb index 70345b9e1..59d1ae760 100644 --- a/notebooks/wongnai_classification.ipynb +++ b/notebooks/wongnai_classification.ipynb @@ -355,7 +355,7 @@ "def process_text(text):\n", " nopunc = [char for char in text if char not in string.punctuation]\n", " nopunc = ''.join(nopunc)\n", - " return [word for word in word_tokenize(nopunc,'ulmfit') if word and not re.search(pattern=r\"\\s+\", string=word)]\n", + " return [word for word in word_tokenize(nopunc, engine='ulmfit') if word and not re.search(pattern=r\"\\s+\", string=word)]\n", "def split_text(text):\n", " return text.split()\n", "\n", diff --git a/pythainlp/soundex/lk82.py b/pythainlp/soundex/lk82.py index e0dee6d6b..a173cae5f 100644 --- a/pythainlp/soundex/lk82.py +++ b/pythainlp/soundex/lk82.py @@ -28,7 +28,7 @@ def lk82(text: str) -> str: :param str text: Thai word :return: LK82 soundex """ - if not text: + if not text or not isinstance(text, str): return "" text = _RE_1.sub("", text) # 4.ลบวรรณยุกต์ diff --git a/pythainlp/soundex/metasound.py b/pythainlp/soundex/metasound.py index 6998f81a9..d594c950e 100644 --- a/pythainlp/soundex/metasound.py +++ b/pythainlp/soundex/metasound.py @@ -38,7 +38,7 @@ def metasound(text: str, length: int = 4) -> str: >>> metasound("บูรณการ", 5)) 'บ5515' """ - if not text: + if not text or not isinstance(text, str): return "" # keep only consonants and thanthakhat diff --git a/pythainlp/soundex/udom83.py b/pythainlp/soundex/udom83.py index dce60feaa..1c1cd5149 100644 --- a/pythainlp/soundex/udom83.py +++ b/pythainlp/soundex/udom83.py @@ -37,7 +37,7 @@ def udom83(text: str) -> str: :return: Udom83 soundex """ - if not text: + if not text or not isinstance(text, str): return "" text = _RE_1.sub("ัน\\1", text) diff --git a/pythainlp/tokenize/__init__.py b/pythainlp/tokenize/__init__.py index 4c33ea0fb..1709c9c3f 100644 --- a/pythainlp/tokenize/__init__.py +++ b/pythainlp/tokenize/__init__.py @@ -87,22 +87,22 @@ def dict_word_tokenize( text: str, custom_dict: Trie = DEFAULT_DICT_TRIE, engine: str = "newmm", - whitespaces: bool = True, + keep_whitespace: bool = True, ) -> List[str]: """ - :meth: Deprecated. Please use `word_tokenize()` with `custom_dict` argument instead. + :meth: DEPRECATED: Please use `word_tokenize()` with a `custom_dict` argument instead :param str text: text to be tokenized :param dict custom_dict: a dictionary trie, or an iterable of words, or a string of dictionary path :param str engine: choose between different options of engine to token (newmm [default], mm, longest, and deepcut) - :param bool whitespaces: True to output no whitespace, a common mark of end of phrase in Thai + :param bool keep_whitespace: True to keep whitespaces, a common mark for end of phrase in Thai :return: list of words """ print( - "Deprecated. Use `word_tokenize()` with `custom_dict` argument instead.", + "Deprecated. Use word_tokenize() with a custom_dict argument instead.", file=sys.stderr, ) return word_tokenize( - text=text, custom_dict=custom_dict, engine=engine, keep_whitespace=whitespaces + text=text, custom_dict=custom_dict, engine=engine, keep_whitespace=keep_whitespace ) @@ -116,7 +116,7 @@ def sent_tokenize(text: str, engine: str = "whitespace+newline") -> List[str]: :return: a list of text, split by whitespace or new line. """ - if not text: + if not text or not isinstance(text, str): return [] sentences = [] @@ -138,7 +138,7 @@ def subword_tokenize(text: str, engine: str = "tcc") -> List[str]: * etcc - Enhanced Thai Character Cluster (Inrut et al. 2001) [In development] :return: a list of tokenized strings. """ - if not text: + if not text or not isinstance(text, str): return [] if engine == "etcc": @@ -156,7 +156,7 @@ def syllable_tokenize(text: str) -> List[str]: :return: returns list of strings of syllables """ - if not text: + if not text or not isinstance(text, str): return [] tokens = [] diff --git a/pythainlp/tokenize/deepcut.py b/pythainlp/tokenize/deepcut.py index f3ec1efb4..39ae78209 100644 --- a/pythainlp/tokenize/deepcut.py +++ b/pythainlp/tokenize/deepcut.py @@ -11,7 +11,7 @@ def segment(text: str, custom_dict: Union[Trie, List[str], str] = None) -> List[str]: - if not text: + if not text or not isinstance(text, str): return [] if custom_dict: diff --git a/pythainlp/tokenize/etcc.py b/pythainlp/tokenize/etcc.py index 727e903ec..efb0d5c45 100644 --- a/pythainlp/tokenize/etcc.py +++ b/pythainlp/tokenize/etcc.py @@ -29,7 +29,7 @@ def segment(text: str) -> str: :return: etcc """ - if not text: + if not text or not isinstance(text, str): return "" if re.search(r"[เแ]" + _C + r"[" + "".join(_UV) + r"]" + r"\w", text): diff --git a/pythainlp/tokenize/longest.py b/pythainlp/tokenize/longest.py index db0bf889c..d248213e4 100644 --- a/pythainlp/tokenize/longest.py +++ b/pythainlp/tokenize/longest.py @@ -140,7 +140,7 @@ def tokenize(self, text: str) -> List[str]: def segment(text: str, custom_dict: Trie = None) -> List[str]: """ตัดคำภาษาไทยด้วยวิธี longest matching""" - if not text: + if not text or not isinstance(text, str): return [] if not custom_dict: diff --git a/pythainlp/tokenize/multi_cut.py b/pythainlp/tokenize/multi_cut.py index 5d1238336..20fddd7bd 100644 --- a/pythainlp/tokenize/multi_cut.py +++ b/pythainlp/tokenize/multi_cut.py @@ -122,7 +122,7 @@ def segment(text: str, custom_dict: Trie = None) -> List[str]: """ ใช้ในการหา list ที่สามารถตัดคำได้ทั้งหมด """ - if not text: + if not text or not isinstance(text, str): return [] return list(_multicut(text, custom_dict=custom_dict)) @@ -130,9 +130,12 @@ def segment(text: str, custom_dict: Trie = None) -> List[str]: def find_all_segment(text: str, custom_dict: Trie = None) -> List[str]: """ - ใช้ในการหา list ที่สามารถตัดคำได้ทั้งหมด + Get all possible segment variations + + :param str text: input string to be tokenized + :return: returns list of segment variations """ - if not text: + if not text or not isinstance(text, str): return [] ww = list(_multicut(text, custom_dict=custom_dict)) diff --git a/pythainlp/tokenize/newmm.py b/pythainlp/tokenize/newmm.py index b7437a631..cd246fb8f 100644 --- a/pythainlp/tokenize/newmm.py +++ b/pythainlp/tokenize/newmm.py @@ -93,7 +93,7 @@ def _onecut(text: str, custom_dict: Trie): def segment(text: str, custom_dict: Trie = None) -> List[str]: - if not text: + if not text or not isinstance(text, str): return [] if not custom_dict: diff --git a/pythainlp/tokenize/pyicu.py b/pythainlp/tokenize/pyicu.py index 869965e2c..9d1d37a6d 100644 --- a/pythainlp/tokenize/pyicu.py +++ b/pythainlp/tokenize/pyicu.py @@ -19,8 +19,9 @@ def _gen_words(text: str) -> str: def segment(text: str) -> List[str]: - if not text: + if not text or not isinstance(text, str): return [] text = re.sub("([^\u0E00-\u0E7F\n ]+)", " \\1 ", text) + return list(_gen_words(text)) diff --git a/pythainlp/tokenize/tcc.py b/pythainlp/tokenize/tcc.py index 47547ad4e..8949a04a8 100644 --- a/pythainlp/tokenize/tcc.py +++ b/pythainlp/tokenize/tcc.py @@ -49,23 +49,23 @@ PAT_TCC = re.compile("|".join(RE_TCC)) -def tcc(w: str) -> str: - if not w: +def tcc(text: str) -> str: + if not text or not isinstance(text, str): return "" p = 0 - while p < len(w): - m = PAT_TCC.match(w[p:]) + while p < len(text): + m = PAT_TCC.match(text[p:]) if m: n = m.span()[1] else: n = 1 - yield w[p : p + n] + yield text[p : p + n] p += n def tcc_pos(text: str) -> Set[int]: - if not text: + if not text or not isinstance(text, str): return set() p_set = set() diff --git a/pythainlp/transliterate/__init__.py b/pythainlp/transliterate/__init__.py index 735650345..f18af4afd 100644 --- a/pythainlp/transliterate/__init__.py +++ b/pythainlp/transliterate/__init__.py @@ -12,7 +12,7 @@ def romanize(text: str, engine: str = "royin") -> str: :return: A string of Thai words rendered in the Latin alphabet. """ - if not isinstance(text, str) or not text: + if not text or not isinstance(text, str): return "" if engine == "thai2rom": @@ -31,7 +31,7 @@ def transliterate(text: str, engine: str = "ipa") -> str: :return: A string of Internaitonal Phonetic Alphabets indicating how the text should be pronounced. """ - if not isinstance(text, str) or not text: + if not text or not isinstance(text, str): return "" if engine == "icu" or engine == "pyicu": diff --git a/pythainlp/util/digitconv.py b/pythainlp/util/digitconv.py index 3982168d6..75717431a 100644 --- a/pythainlp/util/digitconv.py +++ b/pythainlp/util/digitconv.py @@ -61,7 +61,7 @@ def thai_digit_to_arabic_digit(text: str) -> str: :param str text: Text with Thai digits such as '๑', '๒', '๓' :return: Text with Thai digits being converted to Arabic digits such as '1', '2', '3' """ - if not text: + if not text or not isinstance(text, str): return "" newtext = [] @@ -79,7 +79,7 @@ def arabic_digit_to_thai_digit(text: str) -> str: :param str text: Text with Arabic digits such as '1', '2', '3' :return: Text with Arabic digits being converted to Thai digits such as '๑', '๒', '๓' """ - if not text: + if not text or not isinstance(text, str): return "" newtext = [] @@ -97,7 +97,7 @@ def digit_to_text(text: str) -> str: :param str text: Text with digits such as '1', '2', '๓', '๔' :return: Text with digits being spelled out in Thai """ - if not text: + if not text or not isinstance(text, str): return "" newtext = [] diff --git a/pythainlp/util/thai.py b/pythainlp/util/thai.py index 70e5a9d15..53a8f4676 100644 --- a/pythainlp/util/thai.py +++ b/pythainlp/util/thai.py @@ -44,7 +44,7 @@ def countthai(text: str, ignore_chars: str = _DEFAULT_IGNORE_CHARS) -> float: :param str text: input text :return: float, proportion of characters in the text that is Thai character """ - if not text: + if not text or not isinstance(text, str): return 0 if not ignore_chars: diff --git a/tests/__init__.py b/tests/__init__.py index d448e23cb..11aa934fe 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -311,13 +311,13 @@ def test_word_tokenize(self): ["ฉัน", "รัก", "ภาษาไทย", "เพราะ", "ฉัน", "เป็น", "คนไทย"], ) - self.assertIsNotNone(word_tokenize("หมอนทองตากลมหูว์", engine="newmm")) - self.assertIsNotNone(word_tokenize("หมอนทองตากลมหูว์", engine="mm")) - self.assertIsNotNone(word_tokenize("หมอนทองตากลมหูว์", engine="longest")) - self.assertIsNotNone(word_tokenize("หมอนทองตากลมหูว์", engine="ulmfit")) - self.assertIsNotNone(word_tokenize("หมอนทองตากลมหูว์", engine="icu")) - self.assertIsNotNone(word_tokenize("หมอนทองตากลมหูว์", engine="deepcut")) - self.assertIsNotNone(word_tokenize("หมอนทองตากลมหูว์", engine="XX")) + self.assertIsNotNone(word_tokenize("หมอนทองตากลมหูว์MBK39", engine="newmm")) + self.assertIsNotNone(word_tokenize("หมอนทองตากลมหูว์MBK39", engine="mm")) + self.assertIsNotNone(word_tokenize("หมอนทองตากลมหูว์MBK39", engine="longest")) + self.assertIsNotNone(word_tokenize("หมอนทองตากลมหูว์MBK39", engine="ulmfit")) + self.assertIsNotNone(word_tokenize("หมอนทองตากลมหูว์MBK39", engine="icu")) + self.assertIsNotNone(word_tokenize("หมอนทองตากลมหูว์MBK39", engine="deepcut")) + self.assertIsNotNone(word_tokenize("หมอนทองตากลมหูว์MBK39", engine="XX")) self.assertIsNotNone(dict_trie(())) self.assertIsNotNone(dict_trie(("ทดสอบ", "สร้าง", "Trie"))) @@ -334,8 +334,10 @@ def test_word_tokenize(self): ) def test_Tokenizer(self): - t_test = Tokenizer() + t_test = Tokenizer(FROZEN_DICT_TRIE) self.assertEqual(t_test.word_tokenize(""), []) + t_test.set_tokenize_engine("longest") + self.assertEqual(t_test.word_tokenize(None), []) def test_word_tokenize_icu(self): self.assertEqual(tokenize_pyicu.segment(None), []) @@ -369,7 +371,10 @@ def test_word_tokenize_mm(self): ["ฉัน", "รัก", "ภาษาไทย", "เพราะ", "ฉัน", "เป็น", "คนไทย"], ) + self.assertIsNone(multi_cut.mmcut("ทดสอบ")) + self.assertIsNotNone(multi_cut.find_all_segment("รถไฟฟ้ากรุงเทพมหานครBTS")) + self.assertEqual(multi_cut.find_all_segment(None), []) def test_word_tokenize_newmm(self): self.assertEqual(newmm.segment(None), []) From 56ed57505283c67318101214eebac8484cd2c9b3 Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Sat, 20 Apr 2019 23:30:32 +0200 Subject: [PATCH 7/9] - fix test case of "mm" tokenize engine - remove old docs --- docs/archive/pythainlp-1-3-thai.md | 215 --------- docs/archive/pythainlp-1-4-eng.md | 311 ------------- docs/archive/pythainlp-1-4-eng.pdf | Bin 93022 -> 0 bytes docs/archive/pythainlp-1-4-thai.md | 375 --------------- docs/archive/pythainlp-1-4-thai.pdf | Bin 126942 -> 0 bytes docs/archive/pythainlp-1-5-eng.md | 471 ------------------- docs/archive/pythainlp-1-5-thai.md | 609 ------------------------- docs/archive/pythainlp-1-6-eng.md | 502 -------------------- docs/archive/pythainlp-1-6-thai.md | 683 ---------------------------- docs/archive/pythainlp-1-7.md | 1 - docs/archive/pythainlp-dev-thai.md | 599 ------------------------ tests/__init__.py | 2 +- 12 files changed, 1 insertion(+), 3767 deletions(-) delete mode 100644 docs/archive/pythainlp-1-3-thai.md delete mode 100644 docs/archive/pythainlp-1-4-eng.md delete mode 100644 docs/archive/pythainlp-1-4-eng.pdf delete mode 100644 docs/archive/pythainlp-1-4-thai.md delete mode 100644 docs/archive/pythainlp-1-4-thai.pdf delete mode 100644 docs/archive/pythainlp-1-5-eng.md delete mode 100644 docs/archive/pythainlp-1-5-thai.md delete mode 100644 docs/archive/pythainlp-1-6-eng.md delete mode 100644 docs/archive/pythainlp-1-6-thai.md delete mode 100644 docs/archive/pythainlp-1-7.md delete mode 100644 docs/archive/pythainlp-dev-thai.md diff --git a/docs/archive/pythainlp-1-3-thai.md b/docs/archive/pythainlp-1-3-thai.md deleted file mode 100644 index cd694c64f..000000000 --- a/docs/archive/pythainlp-1-3-thai.md +++ /dev/null @@ -1,215 +0,0 @@ -# คู่มือการใช้งาน PyThaiNLP 1.3 - -รองรับเฉพาะ Python 3.4 ขึ้นไปเท่านั้น - -ติดตั้งใช้คำสั่ง - -``` -pip install pythainlp -``` - -**วิธีติดตั้งสำหรับ Windows** - -ให้ทำการติดตั้ง pyicu โดยใช้ไฟล์ .whl จาก [http://www.lfd.uci.edu/~gohlke/pythonlibs/#pyicu](http://www.lfd.uci.edu/~gohlke/pythonlibs/#pyicu) - -หากใช้ python 3.5 64 bit ให้โหลด PyICU‑1.9.7‑cp35‑cp35m‑win_amd64.whl แล้วเปิด cmd ใช้คำสั่ง - -``` -pip install PyICU‑1.9.7‑cp35‑cp35m‑win_amd64.whl -``` - -แล้วจึงใช้ - -``` -pip install pythainlp -``` - -**ติดตั้งบน Mac** - -```sh -$ brew install icu4c --force -$ brew link --force icu4c -$ CFLAGS=-I/usr/local/opt/icu4c/include LDFLAGS=-L/usr/local/opt/icu4c/lib pip install pythainlp -``` - -ข้อมูลเพิ่มเติม [คลิกที่นี้](https://medium.com/data-science-cafe/install-polyglot-on-mac-3c90445abc1f#.rdfrorxjx) - -## API - -### ตัดคำไทย - -สำหรับการตัดคำไทยนั้น ใน PyThaiNLP 1.3 ได้ทำเปลี่ยน API ใหม่ ยกเลิก pythainlp.segment ให้ทำการเปลี่ยนไปใช้ API ชุดใหม่ - -```python -from pythainlp.tokenize import word_tokenize -word_tokenize(text,engine) -``` -text คือ ข้อความในรูปแบบสตริง str เท่านั้น - -engine คือ ระบบตัดคำไทย ปัจจุบันนี้ PyThaiNLP ได้พัฒนามี 3 engine ให้ใช้งานกันดังนี้ - -1. icu - engine ตัวดั้งเดิมของ PyThaiNLP (ความแม่นยำต่ำ) และเป็นค่าเริ่มต้น -2. dict - เป็นการตัดคำโดยใช้พจานุกรมจาก thaiword.txt ใน corpus (ความแม่นยำปานกลาง) -3. mm - ใช้ Maximum Matching algorithm ในการตัดคำภาษาไทย - -คืนค่าเป็น ''list'' เช่น ['แมว','กิน'] - -**ตัวอย่าง** - -```python -from pythainlp.tokenize import word_tokenize -text='ผมรักคุณนะครับโอเคบ่พวกเราเป็นคนไทยรักภาษาไทยภาษาบ้านเกิด' -a=word_tokenize(text,engine='icu') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอ', 'เค', 'บ่', 'พวก', 'เรา', 'เป็น', 'คน', 'ไทย', 'รัก', 'ภาษา', 'ไทย', 'ภาษา', 'บ้าน', 'เกิด'] -b=word_tokenize(text,engine='dict') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คนไทย', 'รัก', 'ภาษาไทย', 'ภาษา', 'บ้านเกิด'] -c=word_tokenize(text,engine='mm') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คนไทย', 'รัก', 'ภาษาไทย', 'ภาษา', 'บ้านเกิด'] -``` - -### Postaggers ภาษาไทย - -ตั้งแต่ PyThaiNLP 1.3 เป็นต้นไป ได้ทำการยกเลิก pythainlp.postaggers เดิม เปลี่ยนไปใช้ API ชุดใหม่ดังนี้ - -```python -from pythainlp.tag import pos_tag -pos_tag(list,engine='old') -``` - -list คือ list ที่เก็บข้อความหลังผ่านการตัดคำแล้ว - -engine คือ ชุดเครื่องมือในการ postaggers มี 2 ตัวดังนี้ - -1. old เป็น UnigramTagger (ค่าเริ่มต้น) -2. artagger เป็น RDR POS Tagger ละเอียดยิ่งกว่าเดิม รองรับเฉพาะ Python 3 เท่านั้น - -### แปลงข้อความเป็น Latin - -```python -from pythainlp.romanization import romanization -romanization(str) -``` -**ตัวอย่าง** - -```python -from pythainlp.romanization import romanization -romanization("แมว") # 'mæw' -``` - -### เช็คคำผิด * - -*ความสามารถนี้รองรับเฉพาะ Python 3 - -ก่อนใช้งานความสามารถนี้ ให้ทำการติดตั้ง hunspell และ hunspell-th ก่อน - -**วิธีติดตั้ง** สำหรับบน Debian , Ubuntu - -``` -sudo apt-get install hunspell hunspell-th -``` - -บน Mac OS ติดตั้งตามนี้ [http://pankdm.github.io/hunspell.html](http://pankdm.github.io/hunspell.html) - -ให้ใช้ pythainlp.spell ตามตัวอย่างนี้ - -```python -from pythainlp.spell import * -a=spell("สี่เหลียม") -print(a) # ['สี่เหลี่ยม', 'เสียเหลี่ยม', 'เหลี่ยม'] -``` -### pythainlp.number - -```python -from pythainlp.number import * -``` -จัดการกับตัวเลข โดยมีดังนี้ - -- nttn(str) - เป็นการแปลงเลขไทยสู่เลข -- nttt(str) - เลขไทยสู่ข้อความ -- ntnt(str) - เลขสู่เลขไทย -- ntt(str) - เลขสู่ข้อความ -- ttn(str) - ข้อความสู่เลข -- numtowords(float) - อ่านจำนวนตัวเลขภาษาไทย (บาท) รับค่าเป็น ''float'' คืนค่าเป็น 'str' - -### เรียงลำดับข้อมูลภาษาไทยใน List - -```python -from pythainlp.collation import collation -print(collation(['ไก่','ไข่','ก','ฮา'])) # ['ก', 'ไก่', 'ไข่', 'ฮา'] -``` - -รับ list คืนค่า list - -### รับเวลาปัจจุบันเป็นภาษาไทย - -```python -from pythainlp.date import now -now() # '30 พฤษภาคม 2560 18:45:24' -``` -### WordNet ภาษาไทย - -เรียกใช้งาน - -```python -from pythainlp.corpus import wordnet -``` - -**รับ Synset** - -```python -wordnet.getSynset(คำ) -``` - -เป็นคำสั่ง ใช้รับ Synset รับค่า str ส่งออกเป็น tuple ('Synset', 'synset li') - -**รับคำจาก id** - -```python -wordnet.getWords() -``` - -เป็นคำสั่ง ใช้รับคำจาก ID รับค่า str ส่งออกเป็น tuple ('Word', 'synsetid li') - -### stopword ภาษาไทย - -```python -from pythainlp.corpus import stopwords -stopwords = stopwords.words('thai') -``` - -### หาคำที่มีจำนวนการใช้งานมากที่สุด - -```python -from pythainlp.rank import rank -rank(list) -``` - -คืนค่าออกมาเป็น dict - -**ตัวอย่างการใช้งาน** - -```python ->>> rank(['แมง','แมง','คน']) -Counter({'แมง': 2, 'คน': 1}) -``` - -### แก้ไขปัญหาการพิมพ์ลืมเปลี่ยนภาษา - -```python -from pythainlp.change import * -``` - -มีคำสั่งดังนี้ - -- texttothai(str) แปลงแป้นตัวอักษรภาษาอังกฤษเป็นภาษาไทย -- texttoeng(str) แปลงแป้นตัวอักษรภาษาไทยเป็นภาษาอังกฤษ - -คืนค่าออกมาเป็น str - -### Sentiment analysis ภาษาไทย - -ใช้ข้อมูลจาก https://github.com/wannaphongcom/lexicon-thai/tree/master/ข้อความ/ - -```python -from pythainlp.sentiment import sentiment -sentiment(str) -``` - -รับค่า str ส่งออกเป็น pos , neg หรือ neutral \ No newline at end of file diff --git a/docs/archive/pythainlp-1-4-eng.md b/docs/archive/pythainlp-1-4-eng.md deleted file mode 100644 index 6955dc8e6..000000000 --- a/docs/archive/pythainlp-1-4-eng.md +++ /dev/null @@ -1,311 +0,0 @@ -# User manual PyThaiNLP 1.4 - -[TOC] - -## API - -### Thai segment - -```python -from pythainlp.tokenize import word_tokenize -word_tokenize(text,engine) -``` -**text** refers to an input text string in Thai. - -**engine** refers to a thai word segmentation system; There are 6 systems to choose from. - -1. icu (default) - pyicu has a very poor performance. -2. dict - dictionary-based tokenizer. It returns False if the message can not be wrapped. -3. mm - Maximum Matching algorithm for Thai word segmentation. -4. newmm - Maximum Matching algorithm for Thai word segmatation. Developed by Korakot Chaovavanich (https://www.facebook.com/groups/408004796247683/permalink/431283740586455/) -5. pylexto - LexTo. -6. deepcut - Deep Learning based Thai word segmentation (https://github.com/rkcosmos/deepcut) - - -Output: ''list'' ex. ['แมว','กิน'] - -**Example** - - -```python -from pythainlp.tokenize import word_tokenize -text='ผมรักคุณนะครับโอเคบ่พวกเราเป็นคนไทยรักภาษาไทยภาษาบ้านเกิด' -a=word_tokenize(text,engine='icu') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอ', 'เค', 'บ่', 'พวก', 'เรา', 'เป็น', 'คน', 'ไทย', 'รัก', 'ภาษา', 'ไทย', 'ภาษา', 'บ้าน', 'เกิด'] -b=word_tokenize(text,engine='dict') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คนไทย', 'รัก', 'ภาษาไทย', 'ภาษา', 'บ้านเกิด'] -c=word_tokenize(text,engine='mm') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คนไทย', 'รัก', 'ภาษาไทย', 'ภาษา', 'บ้านเกิด'] -d=word_tokenize(text,engine='pylexto') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คนไทย', 'รัก', 'ภาษาไทย', 'ภาษา', 'บ้านเกิด'] -e=word_tokenize(text,engine='newmm') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คนไทย', 'รัก', 'ภาษาไทย', 'ภาษา', 'บ้านเกิด'] -``` - -### Thai postaggers - -```python -from pythainlp.tag import pos_tag -pos_tag(list,engine='old') -``` - -engine - -1. old is the UnigramTagger (default) -2. artagger is the RDR POS Tagger. - -### Thai romanization - -```python -from pythainlp.romanization import romanization -romanization(str,engine='pyicu') -``` -There are 2 engines - -- pyicu -- royin - -data : - -input ''str'' - -returns ''str'' - -**Example** - -```python -from pythainlp.romanization import romanization -romanization("แมว") # 'mæw' -``` - -### Spell Check - -Before using this module, please install hunspell and hunspell-th. - -```python -from pythainlp.spell import * -a=spell("สี่เหลียม") -print(a) # ['สี่เหลี่ยม', 'เสียเหลี่ยม', 'เหลี่ยม'] -``` -### pythainlp.number - -```python -from pythainlp.number import * -``` -- nttn(str) - convert thai numbers to numbers. -- nttt(str) - Thai Numbers to text. -- ntnt(str) - numbers to thai numbers. -- ntt(str) - numbers to text. -- ttn(str) - text to numbers. -- numtowords(float) - Read thai numbers (Baht) input ''float'' returns 'str' - -### Sort Thai text into List - -```python -from pythainlp.collation import collation -print(collation(['ไก่','ไข่','ก','ฮา'])) # ['ก', 'ไก่', 'ไข่', 'ฮา'] -``` - -input list - -returns list - -### Get current time in Thai - -```python -from pythainlp.date import now -now() # '30 พฤษภาคม 2560 18:45:24' -``` -### Thai WordNet - -import - -```python -from pythainlp.corpus import wordnet -``` - -**Use** - -It's like nltk. - -- wordnet.synsets(word) -- wordnet.synset(name_synsets) -- wordnet.all_lemma_names(pos=None, lang="tha") -- wordnet.all_synsets(pos=None) -- wordnet.langs() -- wordnet.lemmas(word,pos=None,lang="tha") -- wordnet.lemma(name_synsets) -- wordnet.lemma_from_key(key) -- wordnet.path_similarity(synsets1,synsets2) -- wordnet.lch_similarity(synsets1,synsets2) -- wordnet.wup_similarity(synsets1,synsets2) -- wordnet.morphy(form, pos=None) -- wordnet.custom_lemmas(tab_file, lang) - -**Example** - -```python ->>> from pythainlp.corpus import wordnet ->>> print(wordnet.synsets('หนึ่ง')) -[Synset('one.s.05'), Synset('one.s.04'), Synset('one.s.01'), Synset('one.n.01')] ->>> print(wordnet.synsets('หนึ่ง')[0].lemma_names('tha')) -[] ->>> print(wordnet.synset('one.s.05')) -Synset('one.s.05') ->>> print(wordnet.synset('spy.n.01').lemmas()) -[Lemma('spy.n.01.spy'), Lemma('spy.n.01.undercover_agent')] ->>> print(wordnet.synset('spy.n.01').lemma_names('tha')) -['สปาย', 'สายลับ'] -``` - -### Find the most frequent words. - -```python -from pythainlp.rank import rank -rank(list) -``` - -returns dict - -**Example** - -```python ->>> rank(['แมง','แมง','คน']) -Counter({'แมง': 2, 'คน': 1}) -``` - -### Incorrect input language correction - -```python -from pythainlp.change import * -``` - -- texttothai(str) - eng to thai. -- texttoeng(str) - thai to eng. - -### Thai Character Clusters (TCC) - -TCC : Mr.Jakkrit TeCho - -grammar : Wittawat Jitkrittum (https://github.com/wittawatj/jtcc/blob/master/TCC.g) - -Code : Korakot Chaovavanich - -**Example** - -```python ->>> from pythainlp.tokenize import tcc ->>> tcc.tcc('ประเทศไทย') -'ป/ระ/เท/ศ/ไท/ย' -``` - -### Enhanced Thai Character Cluster (ETCC) - -**Example** - -```python ->>> from pythainlp.tokenize import etcc ->>> etcc.etcc('คืนความสุข') -'/คืน/ความสุข' -``` - -### Thai Soundex - -credit Korakot Chaovavanich (from https://gist.github.com/korakot/0b772e09340cac2f493868da035597e8) - -- LK82 -- Udom83 - -**Example** - -```python ->>> from pythainlp.soundex import LK82 ->>> print(LK82('รถ')) -ร3000 ->>> print(LK82('รด')) -ร3000 ->>> print(LK82('จัน')) -จ4000 ->>> print(LK82('จันทร์')) -จ4000 ->>> print(Udom83('รถ')) -ร800000 -``` - -### Thai meta sound - -``` -Snae & Brückner. (2009). Novel Phonetic Name Matching Algorithm with a Statistical Ontology for Analysing Names Given in Accordance with Thai Astrology. Retrieved from https://pdfs.semanticscholar.org/3983/963e87ddc6dfdbb291099aa3927a0e3e4ea6.pdf -``` - -**Example** - -```python ->>> from pythainlp.MetaSound import * ->>> MetaSound('คน') -'15' -``` - -### Thai sentiment analysis - -using data from [https://github.com/wannaphongcom/lexicon-thai/tree/master/ข้อความ/](https://github.com/wannaphongcom/lexicon-thai/tree/master/ข้อความ/) - -```python -from pythainlp.sentiment import sentiment -sentiment(str) -``` - -input str returns pos , neg or neutral - -### Util - -using - -```python -from pythainlp.util import * -``` - -#### ngrams - -for building ngrams - -```python -ngrams(token,num) -``` - -- token - list -- num - ngrams - -### Corpus - -#### Thai stopword - -```python -from pythainlp.corpus import stopwords -stopwords = stopwords.words('thai') -``` - -#### Thai country name - -```python -from pythainlp.corpus import country -country.get_data() -``` - -#### Tone in Thai - -```python -from pythainlp.corpus import tone -tone.get_data() -``` - -#### Consonant in thai - -```python -from pythainlp.corpus import alphabet -alphabet.get_data() -``` - -#### Word list in thai - -```python -from pythainlp.corpus.thaiword import get_data # old data -get_data() -from pythainlp.corpus.newthaiword import get_data # new data -get_data() -``` diff --git a/docs/archive/pythainlp-1-4-eng.pdf b/docs/archive/pythainlp-1-4-eng.pdf deleted file mode 100644 index 9af1abbc08c1face0ba948f0c304b85187cd5d92..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93022 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 GT*wp=wOz>OU{eVnv)Eb#SEq0Do+ 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
  • ocw$$D3p>Bk`SjasWrq@}n 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 x58OKiF8X7DyPTkfehesTfGR;d0MNod@B เพราะโลกขับเคลื่อนต่อไปด้วยการแบ่งปัน - -รองรับเฉพาะ Python 3.4 ขึ้นไปเท่านั้น - -ติดตั้งใช้คำสั่ง - -``` -pip install pythainlp -``` - -**วิธีติดตั้งสำหรับ Windows** - -ให้ทำการติดตั้ง pyicu โดยใช้ไฟล์ .whl จาก [http://www.lfd.uci.edu/~gohlke/pythonlibs/#pyicu](http://www.lfd.uci.edu/~gohlke/pythonlibs/#pyicu) - -หากใช้ python 3.5 64 bit ให้โหลด PyICU‑1.9.7‑cp35‑cp35m‑win_amd64.whl แล้วเปิด cmd ใช้คำสั่ง - -``` -pip install PyICU‑1.9.7‑cp35‑cp35m‑win_amd64.whl -``` - -แล้วจึงใช้ - -``` -pip install pythainlp -``` - -**ติดตั้งบน Mac** - -```sh -$ brew install icu4c --force -$ brew link --force icu4c -$ CFLAGS=-I/usr/local/opt/icu4c/include LDFLAGS=-L/usr/local/opt/icu4c/lib pip install pythainlp -``` - -ข้อมูลเพิ่มเติม [คลิกที่นี้](https://medium.com/data-science-cafe/install-polyglot-on-mac-3c90445abc1f#.rdfrorxjx) - -## API - -### ตัดคำไทย - -สำหรับการตัดคำไทยนั้น ใช้ API ดังต่อไปนี้ - -```python -from pythainlp.tokenize import word_tokenize -word_tokenize(text,engine) -``` -text คือ ข้อความในรูปแบบสตริง str เท่านั้น - -engine คือ ระบบตัดคำไทย ปัจจุบันนี้ PyThaiNLP ได้พัฒนามี 6 engine ให้ใช้งานกันดังนี้ - -1. icu - engine ตัวดั้งเดิมของ PyThaiNLP (ความแม่นยำต่ำ) และเป็นค่าเริ่มต้น -2. dict - เป็นการตัดคำโดยใช้พจานุกรมจาก thaiword.txt ใน corpus (ความแม่นยำปานกลาง) จะคืนค่า False หากข้อความนั้นไม่สามารถตัดคำได้ -3. mm - ใช้ Maximum Matching algorithm ในการตัดคำภาษาไทย - API ชุดเก่า -4. newmm - ใช้ Maximum Matching algorithm ในการตัดคำภาษาไทย โค้ดชุดใหม่ โดยใช้โค้ดคุณ Korakot Chaovavanich จาก https://www.facebook.com/groups/408004796247683/permalink/431283740586455/ มาพัฒนาต่อ -5. pylexto ใช้ LexTo ในการตัดคำ -6. deepcut ใช้ deepcut จาก https://github.com/rkcosmos/deepcut ในการตัดคำภาษาไทย - -คืนค่าเป็น ''list'' เช่น ['แมว','กิน'] - -**ตัวอย่าง** - -```python -from pythainlp.tokenize import word_tokenize -text='ผมรักคุณนะครับโอเคบ่พวกเราเป็นคนไทยรักภาษาไทยภาษาบ้านเกิด' -a=word_tokenize(text,engine='icu') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอ', 'เค', 'บ่', 'พวก', 'เรา', 'เป็น', 'คน', 'ไทย', 'รัก', 'ภาษา', 'ไทย', 'ภาษา', 'บ้าน', 'เกิด'] -b=word_tokenize(text,engine='dict') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คนไทย', 'รัก', 'ภาษาไทย', 'ภาษา', 'บ้านเกิด'] -c=word_tokenize(text,engine='mm') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คนไทย', 'รัก', 'ภาษาไทย', 'ภาษา', 'บ้านเกิด'] -d=word_tokenize(text,engine='pylexto') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คนไทย', 'รัก', 'ภาษาไทย', 'ภาษา', 'บ้านเกิด'] -e=word_tokenize(text,engine='newmm') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คนไทย', 'รัก', 'ภาษาไทย', 'ภาษา', 'บ้านเกิด'] -``` - -### Postaggers ภาษาไทย - -```python -from pythainlp.tag import pos_tag -pos_tag(list,engine='old') -``` - -list คือ list ที่เก็บข้อความหลังผ่านการตัดคำแล้ว - -engine คือ ชุดเครื่องมือในการ postaggers มี 2 ตัวดังนี้ - -1. old เป็น UnigramTagger (ค่าเริ่มต้น) -2. artagger เป็น RDR POS Tagger ละเอียดยิ่งกว่าเดิม รองรับเฉพาะ Python 3 เท่านั้น - -### แปลงข้อความเป็น Latin - -```python -from pythainlp.romanization import romanization -romanization(str,engine='pyicu') -``` -มี 2 engine ดังนี้ - -- pyicu ส่งค่า Latin -- royin ใช้หลักเกณฑ์การถอดอักษรไทยเป็นอักษรโรมัน ฉบับราชบัณฑิตยสถาน (**หากมีข้อผิดพลาด ให้ใช้คำอ่าน เนื่องจากตัว royin ไม่มีตัวแปลงคำเป็นคำอ่าน**) - -data : - -รับค่า ''str'' ข้อความ - -คืนค่าเป็น ''str'' ข้อความ - -**ตัวอย่าง** - -```python -from pythainlp.romanization import romanization -romanization("แมว") # 'mæw' -``` - -### เช็คคำผิด - -ก่อนใช้งานความสามารถนี้ ให้ทำการติดตั้ง hunspell และ hunspell-th ก่อน - -**วิธีติดตั้ง** สำหรับบน Debian , Ubuntu - -``` -sudo apt-get install hunspell hunspell-th -``` - -บน Mac OS ติดตั้งตามนี้ [http://pankdm.github.io/hunspell.html](http://pankdm.github.io/hunspell.html) - -ให้ใช้ pythainlp.spell ตามตัวอย่างนี้ - -```python -from pythainlp.spell import * -a=spell("สี่เหลียม") -print(a) # ['สี่เหลี่ยม', 'เสียเหลี่ยม', 'เหลี่ยม'] -``` -### pythainlp.number - -```python -from pythainlp.number import * -``` -จัดการกับตัวเลข โดยมีดังนี้ - -- nttn(str) - เป็นการแปลงเลขไทยสู่เลข -- nttt(str) - เลขไทยสู่ข้อความ -- ntnt(str) - เลขสู่เลขไทย -- ntt(str) - เลขสู่ข้อความ -- ttn(str) - ข้อความสู่เลข -- numtowords(float) - อ่านจำนวนตัวเลขภาษาไทย (บาท) รับค่าเป็น ''float'' คืนค่าเป็น 'str' - -### เรียงลำดับข้อมูลภาษาไทยใน List - -```python -from pythainlp.collation import collation -print(collation(['ไก่','ไข่','ก','ฮา'])) # ['ก', 'ไก่', 'ไข่', 'ฮา'] -``` - -รับ list คืนค่า list - -### รับเวลาปัจจุบันเป็นภาษาไทย - -```python -from pythainlp.date import now -now() # '30 พฤษภาคม 2560 18:45:24' -``` -### WordNet ภาษาไทย - -เรียกใช้งาน - -```python -from pythainlp.corpus import wordnet -``` - -**การใช้งาน** - -API เหมือนกับ NLTK โดยรองรับ API ดังนี้ - -- wordnet.synsets(word) -- wordnet.synset(name_synsets) -- wordnet.all_lemma_names(pos=None, lang="tha") -- wordnet.all_synsets(pos=None) -- wordnet.langs() -- wordnet.lemmas(word,pos=None,lang="tha") -- wordnet.lemma(name_synsets) -- wordnet.lemma_from_key(key) -- wordnet.path_similarity(synsets1,synsets2) -- wordnet.lch_similarity(synsets1,synsets2) -- wordnet.wup_similarity(synsets1,synsets2) -- wordnet.morphy(form, pos=None) -- wordnet.custom_lemmas(tab_file, lang) - -**ตัวอย่าง** - -```python ->>> from pythainlp.corpus import wordnet ->>> print(wordnet.synsets('หนึ่ง')) -[Synset('one.s.05'), Synset('one.s.04'), Synset('one.s.01'), Synset('one.n.01')] ->>> print(wordnet.synsets('หนึ่ง')[0].lemma_names('tha')) -[] ->>> print(wordnet.synset('one.s.05')) -Synset('one.s.05') ->>> print(wordnet.synset('spy.n.01').lemmas()) -[Lemma('spy.n.01.spy'), Lemma('spy.n.01.undercover_agent')] ->>> print(wordnet.synset('spy.n.01').lemma_names('tha')) -['สปาย', 'สายลับ'] -``` - -### หาคำที่มีจำนวนการใช้งานมากที่สุด - -```python -from pythainlp.rank import rank -rank(list) -``` - -คืนค่าออกมาเป็น dict - -**ตัวอย่างการใช้งาน** - -```python ->>> rank(['แมง','แมง','คน']) -Counter({'แมง': 2, 'คน': 1}) -``` - -### แก้ไขปัญหาการพิมพ์ลืมเปลี่ยนภาษา - -```python -from pythainlp.change import * -``` - -มีคำสั่งดังนี้ - -- texttothai(str) แปลงแป้นตัวอักษรภาษาอังกฤษเป็นภาษาไทย -- texttoeng(str) แปลงแป้นตัวอักษรภาษาไทยเป็นภาษาอังกฤษ - -คืนค่าออกมาเป็น str - -### Thai Character Clusters (TCC) - -PyThaiNLP 1.4 รองรับ Thai Character Clusters (TCC) โดยจะแบ่งกลุ่มด้วย / - -**เดติด** - -TCC : Mr.Jakkrit TeCho - -grammar : คุณ Wittawat Jitkrittum (https://github.com/wittawatj/jtcc/blob/master/TCC.g) - -โค้ด : คุณ Korakot Chaovavanich - -**การใช้งาน** - -```python ->>> from pythainlp.tokenize import tcc ->>> tcc.tcc('ประเทศไทย') -'ป/ระ/เท/ศ/ไท/ย' -``` - -### Enhanced Thai Character Cluster (ETCC) - -นอกจาก TCC แล้ว PyThaiNLP 1.4 ยังรองรับ Enhanced Thai Character Cluster (ETCC) โดยแบ่งกลุ่มด้วย / - -**การใช้งาน** - -```python ->>> from pythainlp.tokenize import etcc ->>> etcc.etcc('คืนความสุข') -'/คืน/ความสุข' -``` - -### Thai Soundex ภาษาไทย - -เดติด คุณ Korakot Chaovavanich (จาก https://gist.github.com/korakot/0b772e09340cac2f493868da035597e8) - -กฎที่รองรับในเวชั่น 1.4 - -- กฎการเข้ารหัสซาวน์เด็กซ์ของ วิชิตหล่อจีระชุณห์กุล และ เจริญ คุวินทร์พันธุ์ - LK82 -- กฎการเข้ารหัสซาวน์เด็กซ์ของ วรรณี อุดมพาณิชย์ - Udom83 - -**การใช้งาน** - -```python ->>> from pythainlp.soundex import LK82 ->>> print(LK82('รถ')) -ร3000 ->>> print(LK82('รด')) -ร3000 ->>> print(LK82('จัน')) -จ4000 ->>> print(LK82('จันทร์')) -จ4000 ->>> print(Udom83('รถ')) -ร800000 -``` - -### Meta Sound ภาษาไทย - -``` -Snae & Brückner. (2009). Novel Phonetic Name Matching Algorithm with a Statistical Ontology for Analysing Names Given in Accordance with Thai Astrology. Retrieved from https://pdfs.semanticscholar.org/3983/963e87ddc6dfdbb291099aa3927a0e3e4ea6.pdf -``` - -**การใช้งาน** - -```python ->>> from pythainlp.MetaSound import * ->>> MetaSound('คน') -'15' -``` - -### Sentiment analysis ภาษาไทย - -ใช้ข้อมูลจาก [https://github.com/wannaphongcom/lexicon-thai/tree/master/ข้อความ/](https://github.com/wannaphongcom/lexicon-thai/tree/master/ข้อความ/) - -```python -from pythainlp.sentiment import sentiment -sentiment(str) -``` - -รับค่า str ส่งออกเป็น pos , neg หรือ neutral - -### Util - -การใช้งาน - -```python -from pythainlp.util import * -``` - -#### ngrams - -สำหรับสร้าง ngrams - -```python -ngrams(token,num) -``` - -- token คือ list -- num คือ จำนวน ngrams - -### Corpus - -#### stopword ภาษาไทย - -```python -from pythainlp.corpus import stopwords -stopwords = stopwords.words('thai') -``` - -#### ชื่อประเทศ ภาษาไทย - -```python -from pythainlp.corpus import country -country.get_data() -``` - -#### ตัววรรณยุกต์ในภาษาไทย - -```python -from pythainlp.corpus import tone -tone.get_data() -``` - -#### ตัวพยัญชนะในภาษาไทย - -```python -from pythainlp.corpus import alphabet -alphabet.get_data() -``` - -#### รายการคำในภาษาไทย - -```python -from pythainlp.corpus.thaiword import get_data # ข้อมูลเก่า -get_data() -from pythainlp.corpus.newthaiword import get_data # ข้อมูลใหม่ -get_data() -``` - -เขียนโดย นาย วรรณพงษ์ ภัททิยไพบูลย์ \ No newline at end of file diff --git a/docs/archive/pythainlp-1-4-thai.pdf b/docs/archive/pythainlp-1-4-thai.pdf deleted file mode 100644 index 6af963a3cbf4c545feed100943a8caa24eac4c18..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 126942 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%0A3S)^ks}Y@`^aJP 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 RHg*&@dvFHMl5O^={U7{Dc5(m! diff --git a/docs/archive/pythainlp-1-5-eng.md b/docs/archive/pythainlp-1-5-eng.md deleted file mode 100644 index 227bcbb73..000000000 --- a/docs/archive/pythainlp-1-5-eng.md +++ /dev/null @@ -1,471 +0,0 @@ -# User manual PyThaiNLP 1.5 - -[TOC] - -## API - -### tokenize - -#### word_tokenize - -word_tokenize is thai word segmatation. - -```python -from pythainlp.tokenize import word_tokenize -word_tokenize(text,engine) -``` -**text** refers to an input text string in Thai. - -**engine** refers to a thai word segmentation system; There are 6 systems to choose from. - -1. icu (default) - pyicu has a very poor performance. -2. dict - dictionary-based tokenizer. It returns False if the message can not be wrapped. -3. mm - Maximum Matching algorithm for Thai word segmentation. -4. newmm - Maximum Matching algorithm for Thai word segmatation. Developed by Korakot Chaovavanich (https://www.facebook.com/groups/408004796247683/permalink/431283740586455/) -5. pylexto - LexTo. -6. deepcut - Deep Learning based Thai word segmentation (https://github.com/rkcosmos/deepcut) - - -Output: ''list'' ex. ['แมว','กิน'] - -**Example** - - -```python -from pythainlp.tokenize import word_tokenize -text='ผมรักคุณนะครับโอเคบ่พวกเราเป็นคนไทยรักภาษาไทยภาษาบ้านเกิด' -a=word_tokenize(text,engine='icu') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอ', 'เค', 'บ่', 'พวก', 'เรา', 'เป็น', 'คน', 'ไทย', 'รัก', 'ภาษา', 'ไทย', 'ภาษา', 'บ้าน', 'เกิด'] -b=word_tokenize(text,engine='dict') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คนไทย', 'รัก', 'ภาษาไทย', 'ภาษา', 'บ้านเกิด'] -c=word_tokenize(text,engine='mm') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คนไทย', 'รัก', 'ภาษาไทย', 'ภาษา', 'บ้านเกิด'] -d=word_tokenize(text,engine='pylexto') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คนไทย', 'รัก', 'ภาษาไทย', 'ภาษา', 'บ้านเกิด'] -e=word_tokenize(text,engine='newmm') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คนไทย', 'รัก', 'ภาษาไทย', 'ภาษา', 'บ้านเกิด'] -``` - -#### dict_word_tokenize - -```python -from pythainlp.tokenize import dict_word_tokenize -dict_word_tokenize(text,file,engine) -``` - -A command for tokenize by using user-defined information. - -text : str - -file : name file data using in tokenize. - -engine - -- newmm -- wordcutpy : using wordcutpy (https://github.com/veer66/wordcutpy) -- mm -- longest-matching - -Example https://gist.github.com/wannaphongcom/1e862583051bf0464b6ef4ed592f739c - -#### sent_tokenize - -Thai Sentence Tokenizer - -```python -sent_tokenize(text,engine='whitespace+newline') -``` - -engine : - -- whitespace - tokenizer from whitespace -- whitespace+newline - tokenizer from whitespace and newline. - -#### Thai Character Clusters (TCC) - -TCC : Mr.Jakkrit TeCho - -grammar : Wittawat Jitkrittum (https://github.com/wittawatj/jtcc/blob/master/TCC.g) - -Code : Korakot Chaovavanich - -**Example** - -```python ->>> from pythainlp.tokenize import tcc ->>> tcc.tcc('ประเทศไทย') -'ป/ระ/เท/ศ/ไท/ย' -``` - -#### Enhanced Thai Character Cluster (ETCC) - -**Example** - -```python ->>> from pythainlp.tokenize import etcc ->>> etcc.etcc('คืนความสุข') -'/คืน/ความสุข' -``` - -#### WhitespaceTokenizer - -Tokenizer by using spaces - -```python ->>> from pythainlp.tokenize import WhitespaceTokenizer ->>> WhitespaceTokenizer("ทดสอบ ตัดคำช่องว่าง") -['ทดสอบ', 'ตัดคำช่องว่าง'] -``` -#### isthai - -check - -### Thai postaggers - -```python -from pythainlp.tag import pos_tag -pos_tag(list,engine='old') -``` - -engine - -1. old is the UnigramTagger (default) -2. artagger is the RDR POS Tagger. - -### romanization - -```python -from pythainlp.romanization import romanization -romanization(str,engine='pyicu') -``` -There are 2 engines - -- pyicu -- royin - -data : - -input ''str'' - -returns ''str'' - -**Example** - -```python -from pythainlp.romanization import romanization -romanization("แมว") # 'mæw' -``` - -### keywords - -#### find_keyword - -find keywords from thai text in list. - -```python -find_keyword(list,lentext=3) -``` -lentext is minimum number of keywords words. - -return dict {words: number of keywords} - -### Spell Check - -```python -spell(word,engine='pn') -``` -engine - -- 'pn' code from Peter Norvig -- 'hunspell' using hunspell - -Before using this module, please install hunspell and hunspell-th. - -```python -from pythainlp.spell import * -a=spell("สี่เหลียม") -print(a) # ['สี่เหลี่ยม'] -``` -#### pn - -```python -correction(word) -``` - -Show word possible - -**Sample usage** - -```python -from pythainlp.spell.pn import correction -a=correction("สี่เหลียม") -print(a) # ['สี่เหลี่ยม'] -``` -### number - -```python -from pythainlp.number import * -``` -- nttn(str) - convert thai numbers to numbers. -- nttt(str) - Thai Numbers to text. -- ntnt(str) - numbers to thai numbers. -- ntt(str) - numbers to text. -- ttn(str) - text to numbers. -- numtowords(float) - Read thai numbers (Baht) input ''float'' returns 'str' - -### Sort Thai text into List - -```python -from pythainlp.collation import collation -print(collation(['ไก่','ไข่','ก','ฮา'])) # ['ก', 'ไก่', 'ไข่', 'ฮา'] -``` - -input list - -returns list - -### Get current time in Thai - -```python -from pythainlp.date import now -now() # '30 พฤษภาคม 2560 18:45:24' -``` -### Find the most frequent words. - -```python -from pythainlp.rank import rank -rank(list) -``` - -returns dict - -**Example** - -```python ->>> rank(['แมง','แมง','คน']) -Counter({'แมง': 2, 'คน': 1}) -``` - -### Incorrect input language correction - -```python -from pythainlp.change import * -``` - -- texttothai(str) - eng to thai. -- texttoeng(str) - thai to eng. - -### Thai Soundex - -credit Korakot Chaovavanich (from https://gist.github.com/korakot/0b772e09340cac2f493868da035597e8) - -- LK82 -- Udom83 - -**Example** - -```python ->>> from pythainlp.soundex import LK82,Udom83 ->>> print(LK82('รถ')) -ร3000 ->>> print(LK82('รด')) -ร3000 ->>> print(LK82('จัน')) -จ4000 ->>> print(LK82('จันทร์')) -จ4000 ->>> print(Udom83('รถ')) -ร800000 -``` - -### Thai meta sound - -``` -Snae & Brückner. (2009). Novel Phonetic Name Matching Algorithm with a Statistical Ontology for Analysing Names Given in Accordance with Thai Astrology. Retrieved from https://pdfs.semanticscholar.org/3983/963e87ddc6dfdbb291099aa3927a0e3e4ea6.pdf -``` - -**Example** - -```python ->>> from pythainlp.MetaSound import * ->>> MetaSound('คน') -'15' -``` - -### Thai sentiment analysis - -using data from [https://github.com/wannaphongcom/lexicon-thai/tree/master/ข้อความ/](https://github.com/wannaphongcom/lexicon-thai/tree/master/ข้อความ/) - -```python -from pythainlp.sentiment import sentiment -sentiment(str) -``` - -input str returns pos , neg or neutral - -### Util - -using - -```python -from pythainlp.util import * -``` - -#### ngrams - -for building ngrams - -```python -ngrams(token,num) -``` - -- token - list -- num - ngrams - -#### bigrams - -for building bigrams - -```python -bigrams(token) -``` - -- token - list - -#### trigram - -for building trigram - -```python -trigram(token) -``` - -- token - list - -#### normalize - -fix thai text - -```python -normalize(text) -``` - -**Example** - -```python ->>> print(normalize("เเปลก")=="แปลก") # เ เ ป ล ก กับ แปลก -True -``` - -### Corpus - -#### Thai stopword - -```python -from pythainlp.corpus import stopwords -stopwords = stopwords.words('thai') -``` - -#### Thai country name - -```python -from pythainlp.corpus import country -country.get_data() -``` - -#### Tone in Thai - -```python -from pythainlp.corpus import tone -tone.get_data() -``` - -#### Consonant in thai - -```python -from pythainlp.corpus import alphabet -alphabet.get_data() -``` - -#### Word list in thai - -```python -from pythainlp.corpus.thaiword import get_data # old data -get_data() -from pythainlp.corpus.newthaiword import get_data # new data -get_data() -``` - -#### ConceptNet - -Thai tool for ConceptNet. - -**find edges** - -```python -edges(word,lang='th') -``` - -return dict - -#### Thai WordNet - -import - -```python -from pythainlp.corpus import wordnet -``` - -**Use** - -It's like nltk. - -- wordnet.synsets(word) -- wordnet.synset(name_synsets) -- wordnet.all_lemma_names(pos=None, lang="tha") -- wordnet.all_synsets(pos=None) -- wordnet.langs() -- wordnet.lemmas(word,pos=None,lang="tha") -- wordnet.lemma(name_synsets) -- wordnet.lemma_from_key(key) -- wordnet.path_similarity(synsets1,synsets2) -- wordnet.lch_similarity(synsets1,synsets2) -- wordnet.wup_similarity(synsets1,synsets2) -- wordnet.morphy(form, pos=None) -- wordnet.custom_lemmas(tab_file, lang) - -**Example** - -```python ->>> from pythainlp.corpus import wordnet ->>> print(wordnet.synsets('หนึ่ง')) -[Synset('one.s.05'), Synset('one.s.04'), Synset('one.s.01'), Synset('one.n.01')] ->>> print(wordnet.synsets('หนึ่ง')[0].lemma_names('tha')) -[] ->>> print(wordnet.synset('one.s.05')) -Synset('one.s.05') ->>> print(wordnet.synset('spy.n.01').lemmas()) -[Lemma('spy.n.01.spy'), Lemma('spy.n.01.undercover_agent')] ->>> print(wordnet.synset('spy.n.01').lemma_names('tha')) -['สปาย', 'สายลับ'] -``` - -#### TNC - -Tool for Thai National Corpus (http://www.arts.chula.ac.th/~ling/TNC/index.php) - -##### word_frequency - -find word frequency - -```python -word_frequency(word,domain='all') -``` -domain - -- all -- imaginative -- natural-pure-science -- applied-science -- social-science -- world-affairs-history -- commerce-finance -- arts -- belief-thought -- leisure -- others \ No newline at end of file diff --git a/docs/archive/pythainlp-1-5-thai.md b/docs/archive/pythainlp-1-5-thai.md deleted file mode 100644 index ee03c26dd..000000000 --- a/docs/archive/pythainlp-1-5-thai.md +++ /dev/null @@ -1,609 +0,0 @@ -# คู่มือการใช้งาน PyThaiNLP 1.5 - -[TOC] - -Natural language processing หรือ การประมวลภาษาธรรมชาติ โมดูล PyThaiNLP เป็นโมดูลที่ถูกพัฒนาขึ้นเพื่อพัฒนาการประมวลภาษาธรรมชาติภาษาไทยในภาษา Python และ**มันฟรี (ตลอดไป) เพื่อคนไทยและชาวโลกทุกคน !** - -> เพราะโลกขับเคลื่อนต่อไปด้วยการแบ่งปัน - -รองรับ Python 2.7 และ Python 3.4 ขึ้นไปเท่านั้น - -ติดตั้งใช้คำสั่ง - -``` -pip install pythainlp -``` - -**วิธีติดตั้งสำหรับ Windows** - -การติดตั้ง pythainlp บน windows ต้องติดตั้ง pyicu ซึ่งทำได้ยากมาก -วิธีที่ง่ายที่สุดคือใช้ wheel - -1. [http://www.lfd.uci.edu/~gohlke/pythonlibs/#pyicu](http://www.lfd.uci.edu/~gohlke/pythonlibs/#pyicu) แล้ว download wheel ตาม python ตัวเองเช่น - ผมใช้ python x64 3.6.1 บน Windows ก็ให้ใช้ PyICU‑1.9.7‑cp36‑cp36m‑win_amd64.whl - -2. `pip install PyICU‑1.9.7‑cp36‑cp36m‑win_amd64.whl` - -3. `pip install pythainlp` - -**ติดตั้งบน Mac** - -** แนะนำให้ใช้ icu 58.2 เนื่องจาก icu 59.1 มาปัญหากับ PyICU ** - -```sh -$ brew install icu4c --force -$ brew link --force icu4c -$ CFLAGS=-I/usr/local/opt/icu4c/include LDFLAGS=-L/usr/local/opt/icu4c/lib pip install pythainlp -``` - -ข้อมูลเพิ่มเติม [คลิกที่นี้](https://medium.com/data-science-cafe/install-polyglot-on-mac-3c90445abc1f#.rdfrorxjx) - -## API - -### tokenize - -#### word_tokenize - -สำหรับการตัดคำไทยนั้น ใช้ API ดังต่อไปนี้ - -```python -from pythainlp.tokenize import word_tokenize -word_tokenize(text,engine) -``` -text คือ ข้อความในรูปแบบสตริง str เท่านั้น - -engine คือ ระบบตัดคำไทย ปัจจุบันนี้ PyThaiNLP ได้พัฒนามี 6 engine ให้ใช้งานกันดังนี้ - -1. icu - engine ตัวดั้งเดิมของ PyThaiNLP (ความแม่นยำต่ำ) และเป็นค่าเริ่มต้น -2. dict - เป็นการตัดคำโดยใช้พจานุกรมจาก thaiword.txt ใน corpus (ความแม่นยำปานกลาง) **จะคืนค่า False หากข้อความนั้นไม่สามารถตัดคำได้** -3. longest-matching ใช้ Longest matching ในการตัดคำ -4. mm - ใช้ Maximum Matching algorithm ในการตัดคำภาษาไทย - API ชุดเก่า -5. newmm - ใช้ Maximum Matching algorithm ในการตัดคำภาษาไทย โค้ดชุดใหม่ โดยใช้โค้ดคุณ Korakot Chaovavanich จาก https://www.facebook.com/groups/408004796247683/permalink/431283740586455/ มาพัฒนาต่อ -6. pylexto ใช้ LexTo ในการตัดคำ โดยเป็น Longest matching -7. deepcut ใช้ deepcut จาก https://github.com/rkcosmos/deepcut ในการตัดคำภาษาไทย -8. wordcutpy ใช้ wordcutpy (https://github.com/veer66/wordcutpy) ในการตัดคำ - -คืนค่าเป็น ''list'' เช่น ['แมว','กิน'] - -**ตัวอย่าง** - -```python -from pythainlp.tokenize import word_tokenize -text='ผมรักคุณนะครับโอเคบ่พวกเราเป็นคนไทยรักภาษาไทยภาษาบ้านเกิด' -a=word_tokenize(text,engine='icu') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอ', 'เค', 'บ่', 'พวก', 'เรา', 'เป็น', 'คน', 'ไทย', 'รัก', 'ภาษา', 'ไทย', 'ภาษา', 'บ้าน', 'เกิด'] -b=word_tokenize(text,engine='dict') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คนไทย', 'รัก', 'ภาษาไทย', 'ภาษา', 'บ้านเกิด'] -c=word_tokenize(text,engine='mm') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คนไทย', 'รัก', 'ภาษาไทย', 'ภาษา', 'บ้านเกิด'] -d=word_tokenize(text,engine='pylexto') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คนไทย', 'รัก', 'ภาษาไทย', 'ภาษา', 'บ้านเกิด'] -e=word_tokenize(text,engine='newmm') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คนไทย', 'รัก', 'ภาษาไทย', 'ภาษา', 'บ้านเกิด'] -g=word_tokenize(text,engine='wordcutpy') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คน', 'ไทย', 'รัก', 'ภาษา', 'ไทย', 'ภาษา', 'บ้านเกิด'] -``` - -#### dict_word_tokenize - -```python -from pythainlp.tokenize import dict_word_tokenize -dict_word_tokenize(text,file,engine) -``` - -เป็นคำสั่งสำหรับตัดคำโดยใช้ข้อมูลที่ผู้ใช้กำหนด - -text คือ ข้อความที่ต้องการตัดคำ - -file คือ ที่ตั้งไฟล์ที่ต้องการมาเป็นฐานข้อมูลตัดคำ - -engine คือ เครื่องมือตัดคำ - -- newmm ตัดคำด้วย newmm -- wordcutpy ใช้ wordcutpy (https://github.com/veer66/wordcutpy) ในการตัดคำ -- mm ตัดคำด้วย mm -- longest-matching ตัดคำโดยใช้ longest matching - -ตัวอย่างการใช้งาน https://gist.github.com/wannaphongcom/1e862583051bf0464b6ef4ed592f739c - -#### sent_tokenize - -ใช้ตัดประโยคภาษาไทย - -```python -sent_tokenize(text,engine='whitespace+newline') -``` - -text คือ ข้อความในรูปแบบสตริง - -engine คือ เครื่องมือสำหรับใช้ตัดประโยค - -- whitespace ตัดประโยคจากช่องว่าง -- whitespace+newline ตัดประโยคจากช่องว่างและตัดจากการขึ้นบรรทัดใหม่ - -คืนค่า ออกมาเป็น list - -#### WhitespaceTokenizer - -ใช้ตัดคำ/ประโยคจากช่องว่างในสตริง - -```python ->>> from pythainlp.tokenize import WhitespaceTokenizer ->>> WhitespaceTokenizer("ทดสอบ ตัดคำช่องว่าง") -['ทดสอบ', 'ตัดคำช่องว่าง'] -``` - -#### isthai - -ใช้เช็คข้อความว่าเป็นภาษาไทยทั้งหมดกี่ % - -```python -isthai(text,check_all=False) -``` - -text คือ ข้อความหรือ list ตัวอักษร - -check_all สำหรับส่งคืนค่า True หรือ False เช็คทุกตัวอักษร - -**การส่งคืนค่า** - -```python -{'thai':% อักษรภาษาไทย,'check_all':tuple โดยจะเป็น (ตัวอักษร,True หรือ False)} -``` - -#### Thai Character Clusters (TCC) - -PyThaiNLP 1.4 รองรับ Thai Character Clusters (TCC) โดยจะแบ่งกลุ่มด้วย / - -**เดติด** - -TCC : Mr.Jakkrit TeCho - -grammar : คุณ Wittawat Jitkrittum (https://github.com/wittawatj/jtcc/blob/master/TCC.g) - -โค้ด : คุณ Korakot Chaovavanich - -**การใช้งาน** - -```python ->>> from pythainlp.tokenize import tcc ->>> tcc.tcc('ประเทศไทย') -'ป/ระ/เท/ศ/ไท/ย' -``` - -#### Enhanced Thai Character Cluster (ETCC) - -นอกจาก TCC แล้ว PyThaiNLP 1.4 ยังรองรับ Enhanced Thai Character Cluster (ETCC) โดยแบ่งกลุ่มด้วย / - -**การใช้งาน** - -```python ->>> from pythainlp.tokenize import etcc ->>> etcc.etcc('คืนความสุข') -'/คืน/ความสุข' -``` - -### keywords - -ใช้หา keywords จากข้อความภาษาไทย - -#### find_keyword - -การทำงาน หาคำที่ถูกใช้งานมากกว่าค่าขั้นต่ำที่กำหนดได้ โดยจะลบ stopword ออกไป - -```python -find_keyword(word_list,lentext=3) -``` - -word_list คือ list ของข้อความที่ผ่านการตัดคำแล้ว - -lentext คือ จำนวนคำขั้นต่ำที่ต้องการหา keyword - -คืนค่าออกมาเป็น dict - -### tag - -เป็น Part-of-speech tagging ภาษาไทย - -```python -from pythainlp.tag import pos_tag -pos_tag(list,engine='old') -``` - -list คือ list ที่เก็บข้อความหลังผ่านการตัดคำแล้ว - -engine คือ ชุดเครื่องมือในการ postaggers มี 2 ตัวดังนี้ - -1. old เป็น UnigramTagger (ค่าเริ่มต้น) -2. artagger เป็น RDR POS Tagger ละเอียดยิ่งกว่าเดิม รองรับเฉพาะ Python 3 เท่านั้น - -### romanization - -```python -from pythainlp.romanization import romanization -romanization(str,engine='pyicu') -``` -มี 2 engine ดังนี้ - -- pyicu ส่งค่า Latin -- royin ใช้หลักเกณฑ์การถอดอักษรไทยเป็นอักษรโรมัน ฉบับราชบัณฑิตยสถาน (**หากมีข้อผิดพลาด ให้ใช้คำอ่าน เนื่องจากตัว royin ไม่มีตัวแปลงคำเป็นคำอ่าน**) - -data : - -รับค่า ''str'' ข้อความ - -คืนค่าเป็น ''str'' ข้อความ - -**ตัวอย่าง** - -```python -from pythainlp.romanization import romanization -romanization("แมว") # 'mæw' -``` - -### spell - -เป็น API สำหรับเช็คคำผิดในภาษาไทย - -```python -spell(word,engine='pn') -``` - -engine ที่รองรับ - -- pn พัฒนามาจาก Peter Norvig (ค่าเริ่มต้น) -- hunspell ใช้ hunspell (ไม่รองรับ Python 2.7) - -**ตัวอย่างการใช้งาน** - -```python -from pythainlp.spell import * -a=spell("สี่เหลียม") -print(a) # ['สี่เหลี่ยม'] -``` -#### pn - -```python -correction(word) -``` - -แสดงคำที่เป็นไปได้มากที่สุด - -**ตัวอย่างการใช้งาน** - -```python -from pythainlp.spell.pn import correction -a=correction("สี่เหลียม") -print(a) # ['สี่เหลี่ยม'] -``` - -ผลลัพธ์ - -``` -สี่เหลี่ยม -``` - -### pythainlp.number - -```python -from pythainlp.number import * -``` -จัดการกับตัวเลข โดยมีดังนี้ - -- thai_num_to_num(str) - เป็นการแปลงเลขไทยสู่เลข -- thai_num_to_text(str) - เลขไทยสู่ข้อความ -- num_to_thai_num(str) - เลขสู่เลขไทย -- num_to_text(str) - เลขสู่ข้อความ -- text_to_num(str) - ข้อความสู่เลข -- numtowords(float) - อ่านจำนวนตัวเลขภาษาไทย (บาท) รับค่าเป็น ''float'' คืนค่าเป็น 'str' - -### collation - -ใช้ในการเรียงลำดับข้อมูลภาษาไทยใน List - -```python -from pythainlp.collation import collation -print(collation(['ไก่','ไข่','ก','ฮา'])) # ['ก', 'ไก่', 'ไข่', 'ฮา'] -``` - -รับ list คืนค่า list - -### date - -#### now - -รับเวลาปัจจุบันเป็นภาษาไทย - -```python -from pythainlp.date import now -now() # '30 พฤษภาคม 2560 18:45:24' -``` -### rank - -#### rank - -หาคำที่มีจำนวนการใช้งานมากที่สุด - -```python -from pythainlp.rank import rank -rank(list) -``` - -คืนค่าออกมาเป็น dict - -**ตัวอย่างการใช้งาน** - -```python ->>> rank(['แมง','แมง','คน']) -Counter({'แมง': 2, 'คน': 1}) -``` - -### change - -#### แก้ไขปัญหาการพิมพ์ลืมเปลี่ยนภาษา - -```python -from pythainlp.change import * -``` - -มีคำสั่งดังนี้ - -- texttothai(str) แปลงแป้นตัวอักษรภาษาอังกฤษเป็นภาษาไทย -- texttoeng(str) แปลงแป้นตัวอักษรภาษาไทยเป็นภาษาอังกฤษ - -คืนค่าออกมาเป็น str - -### soundex - -เดติด คุณ Korakot Chaovavanich (จาก https://gist.github.com/korakot/0b772e09340cac2f493868da035597e8) - -กฎที่รองรับในเวชั่น 1.4 - -- กฎการเข้ารหัสซาวน์เด็กซ์ของ วิชิตหล่อจีระชุณห์กุล และ เจริญ คุวินทร์พันธุ์ - LK82 -- กฎการเข้ารหัสซาวน์เด็กซ์ของ วรรณี อุดมพาณิชย์ - Udom83 - -**การใช้งาน** - -```python ->>> from pythainlp.soundex import LK82,Udom83 ->>> print(LK82('รถ')) -ร3000 ->>> print(LK82('รด')) -ร3000 ->>> print(LK82('จัน')) -จ4000 ->>> print(LK82('จันทร์')) -จ4000 ->>> print(Udom83('รถ')) -ร800000 -``` - -### Meta Sound ภาษาไทย - -``` -Snae & Brückner. (2009). Novel Phonetic Name Matching Algorithm with a Statistical Ontology for Analysing Names Given in Accordance with Thai Astrology. Retrieved from https://pdfs.semanticscholar.org/3983/963e87ddc6dfdbb291099aa3927a0e3e4ea6.pdf -``` - -**การใช้งาน** - -```python ->>> from pythainlp.MetaSound import * ->>> MetaSound('คน') -'15' -``` - -### sentiment - -เป็น Sentiment analysis ภาษาไทย ใช้ข้อมูลจาก [https://github.com/wannaphongcom/lexicon-thai/tree/master/ข้อความ/](https://github.com/wannaphongcom/lexicon-thai/tree/master/ข้อความ/) - -```python -from pythainlp.sentiment import sentiment -sentiment(str) -``` - -รับค่า str ส่งออกเป็น pos , neg - -### Util - -การใช้งาน - -```python -from pythainlp.util import * -``` - -#### ngrams - -สำหรับสร้าง n-grams - -```python -ngrams(token,num) -``` - -- token คือ list -- num คือ จำนวน ngrams - -#### bigrams - -สำหรับสร้าง bigrams - -```python -bigrams(token) -``` - -- token คือ list - -#### trigram - -สำหรับสร้าง trigram - -```python -trigram(token) -``` - -- token คือ list - -#### normalize - -ซ่อมข้อความภาษาไทย เช่น กี่่่ ไปเป็น กี่ - -```python -normalize(text) -``` - -**ตัวอย่าง** - -```python ->>> print(normalize("เเปลก")=="แปลก") # เ เ ป ล ก กับ แปลก -True -``` - -### Corpus - -#### WordNet ภาษาไทย - -เรียกใช้งาน - -```python -from pythainlp.corpus import wordnet -``` - -**การใช้งาน** - -API เหมือนกับ NLTK โดยรองรับ API ดังนี้ - -- wordnet.synsets(word) -- wordnet.synset(name_synsets) -- wordnet.all_lemma_names(pos=None, lang="tha") -- wordnet.all_synsets(pos=None) -- wordnet.langs() -- wordnet.lemmas(word,pos=None,lang="tha") -- wordnet.lemma(name_synsets) -- wordnet.lemma_from_key(key) -- wordnet.path_similarity(synsets1,synsets2) -- wordnet.lch_similarity(synsets1,synsets2) -- wordnet.wup_similarity(synsets1,synsets2) -- wordnet.morphy(form, pos=None) -- wordnet.custom_lemmas(tab_file, lang) - -**ตัวอย่าง** - -```python ->>> from pythainlp.corpus import wordnet ->>> print(wordnet.synsets('หนึ่ง')) -[Synset('one.s.05'), Synset('one.s.04'), Synset('one.s.01'), Synset('one.n.01')] ->>> print(wordnet.synsets('หนึ่ง')[0].lemma_names('tha')) -[] ->>> print(wordnet.synset('one.s.05')) -Synset('one.s.05') ->>> print(wordnet.synset('spy.n.01').lemmas()) -[Lemma('spy.n.01.spy'), Lemma('spy.n.01.undercover_agent')] ->>> print(wordnet.synset('spy.n.01').lemma_names('tha')) -['สปาย', 'สายลับ'] -``` - -#### stopword ภาษาไทย - -```python -from pythainlp.corpus import stopwords -stopwords = stopwords.words('thai') -``` - -#### ชื่อประเทศ ภาษาไทย - -```python -from pythainlp.corpus import country -country.get_data() -``` - -#### ตัววรรณยุกต์ในภาษาไทย - -```python -from pythainlp.corpus import tone -tone.get_data() -``` - -#### ตัวพยัญชนะในภาษาไทย - -```python -from pythainlp.corpus import alphabet -alphabet.get_data() -``` - -#### รายการคำในภาษาไทย - -```python -from pythainlp.corpus.thaiword import get_data # ข้อมูลเก่า -get_data() -from pythainlp.corpus.newthaiword import get_data # ข้อมูลใหม่ -get_data() -``` - -#### provinces - -สำหรับจัดการชื่อจังหวัดในประเทศไทย - -##### get_data - -รับข้อมูลชื่อจังหวัดในประเทศไทบ - -```python -get_data() -``` - -คือค่าออกมาเป็น list - -##### parsed_docs - -สำหรับใช้ Tag ชื่อจังหวัดในประเทศไทย - -```python -parsed_docs(text_list) -``` - -text_list คือ ข้อความภาษาไทยที่อยู่ใน list โดยผ่านการตัดคำมาแล้ว - -**ตัวอย่าง** - -```python ->>> d=['หนองคาย', 'เป็น', 'เมือง', 'น่าอยู่', 'อันดับ', 'ต้น', 'ๆ', 'ของ', 'โลก', 'นอกจากนี้', 'ยัง', 'มี', 'เชียงใหม่'] ->>> parsed_docs(d) -["[LOC : 'หนองคาย']", 'เป็น', 'เมือง', 'น่าอยู่', 'อันดับ', 'ต้น', 'ๆ', 'ของ', 'โลก', 'นอกจากนี้', 'ยัง', 'มี', "[LOC : 'เชียงใหม่']"] -``` - -#### ConceptNet - -เครื่องมือสำหรับ ConceptNet. - -**ค้นหา edges** - -```python -edges(word,lang='th') -``` - -return dict - -#### TNC - -สำหรับใช้จัดการกับ Thai National Corpus (http://www.arts.chula.ac.th/~ling/TNC/index.php) - -##### word_frequency - -ใช้วัดความถี่ของคำ - -```python -word_frequency(word,domain='all') -``` - -word คือ คำ - -domain คือ หมวดหมู่ของคำ - -มีหมวดหมู่ดังนี้ - -- all -- imaginative -- natural-pure-science -- applied-science -- social-science -- world-affairs-history -- commerce-finance -- arts -- belief-thought -- leisure -- others - -เขียนโดย นาย วรรณพงษ์ ภัททิยไพบูลย์ diff --git a/docs/archive/pythainlp-1-6-eng.md b/docs/archive/pythainlp-1-6-eng.md deleted file mode 100644 index df1c9f16e..000000000 --- a/docs/archive/pythainlp-1-6-eng.md +++ /dev/null @@ -1,502 +0,0 @@ -# User manual PyThaiNLP 1.6 - -[TOC] - -## API - -### tokenize - -#### word_tokenize - -word_tokenize is thai word segmatation. - -```python -from pythainlp.tokenize import word_tokenize -word_tokenize(text,engine) -``` -**text** refers to an input text string in Thai. - -**engine** refers to a thai word segmentation system; There are 6 systems to choose from. - -1. newmm (default) - Maximum Matching algorithm for Thai word segmatation. Developed by Korakot Chaovavanich (https://www.facebook.com/groups/408004796247683/permalink/431283740586455/) -2. icu - pyicu has a very poor performance. -3. dict - dictionary-based tokenizer. It returns False if the message can not be wrapped. -4. longest-matching - using Longest matching algorithm for Thai word segmentation. -5. mm - Maximum Matching algorithm for Thai word segmentation. -6. pylexto - LexTo. -7. deepcut - Deep Learning based Thai word segmentation (https://github.com/rkcosmos/deepcut) -8. wordcutpy - use wordcutpy (https://github.com/veer66/wordcutpy) for Thai word segmentation. - - -Output: ''list'' ex. ['แมว','กิน'] - -**Example** - - -```python -from pythainlp.tokenize import word_tokenize -text='ผมรักคุณนะครับโอเคบ่พวกเราเป็นคนไทยรักภาษาไทยภาษาบ้านเกิด' -a=word_tokenize(text,engine='icu') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอ', 'เค', 'บ่', 'พวก', 'เรา', 'เป็น', 'คน', 'ไทย', 'รัก', 'ภาษา', 'ไทย', 'ภาษา', 'บ้าน', 'เกิด'] -b=word_tokenize(text,engine='dict') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คนไทย', 'รัก', 'ภาษาไทย', 'ภาษา', 'บ้านเกิด'] -c=word_tokenize(text,engine='mm') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คนไทย', 'รัก', 'ภาษาไทย', 'ภาษา', 'บ้านเกิด'] -d=word_tokenize(text,engine='pylexto') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คนไทย', 'รัก', 'ภาษาไทย', 'ภาษา', 'บ้านเกิด'] -e=word_tokenize(text,engine='newmm') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คนไทย', 'รัก', 'ภาษาไทย', 'ภาษา', 'บ้านเกิด'] -g=word_tokenize(text,engine='wordcutpy') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คน', 'ไทย', 'รัก', 'ภาษา', 'ไทย', 'ภาษา', 'บ้านเกิด'] -``` - -#### dict_word_tokenize - -```python -from pythainlp.tokenize import dict_word_tokenize -dict_word_tokenize(text,file,engine) -``` - -A command for tokenize by using user-defined information. - -text : str - -file : name file data using in tokenize. - -engine - -- newmm -- wordcutpy : using wordcutpy (https://github.com/veer66/wordcutpy) -- mm -- longest-matching - -Example https://gist.github.com/wannaphongcom/1e862583051bf0464b6ef4ed592f739c - -#### sent_tokenize - -Thai Sentence Tokenizer - -```python -sent_tokenize(text,engine='whitespace+newline') -``` - -engine : - -- whitespace - tokenizer from whitespace -- whitespace+newline - tokenizer from whitespace and newline. - -#### Thai Character Clusters (TCC) - -TCC : Mr.Jakkrit TeCho - -grammar : Wittawat Jitkrittum (https://github.com/wittawatj/jtcc/blob/master/TCC.g) - -Code : Korakot Chaovavanich - -**Example** - -```python ->>> from pythainlp.tokenize import tcc ->>> tcc.tcc('ประเทศไทย') -'ป/ระ/เท/ศ/ไท/ย' -``` - -#### Enhanced Thai Character Cluster (ETCC) - -**Example** - -```python ->>> from pythainlp.tokenize import etcc ->>> etcc.etcc('คืนความสุข') -'/คืน/ความสุข' -``` - -#### WhitespaceTokenizer - -Tokenizer by using spaces - -```python ->>> from pythainlp.tokenize import WhitespaceTokenizer ->>> WhitespaceTokenizer("ทดสอบ ตัดคำช่องว่าง") -['ทดสอบ', 'ตัดคำช่องว่าง'] -``` -#### isthai - -check - -### Thai postaggers - -```python -from pythainlp.tag import pos_tag -pos_tag(list,engine='old') -``` - -engine - -1. old is the UnigramTagger (default) -2. artagger is the RDR POS Tagger. - -### romanization - -```python -from pythainlp.romanization import romanization -romanization(str,engine='pyicu') -``` -There are 2 engines - -- pyicu -- royin - -data : - -input ''str'' - -returns ''str'' - -**Example** - -```python -from pythainlp.romanization import romanization -romanization("แมว") # 'mæw' -``` - -### keywords - -#### find_keyword - -find keywords from thai text in list. - -```python -find_keyword(list,lentext=3) -``` -lentext is minimum number of keywords words. - -return dict {words: number of keywords} - -### Spell Check - -```python -spell(word,engine='pn') -``` -engine - -- 'pn' code from Peter Norvig -- 'hunspell' using hunspell - -Before using this module, please install hunspell and hunspell-th. - -```python -from pythainlp.spell import * -a=spell("สี่เหลียม") -print(a) # ['สี่เหลี่ยม'] -``` -#### pn - -```python -correction(word) -``` - -Show word possible - -**Sample usage** - -```python -from pythainlp.spell.pn import correction -a=correction("สี่เหลียม") -print(a) # ['สี่เหลี่ยม'] -``` -### number - -```python -from pythainlp.number import * -``` -- nttn(str) - convert thai numbers to numbers. -- nttt(str) - Thai Numbers to text. -- ntnt(str) - numbers to thai numbers. -- ntt(str) - numbers to text. -- ttn(str) - text to numbers. -- numtowords(float) - Read thai numbers (Baht) input ''float'' returns 'str' - -### Sort Thai text into List - -```python -from pythainlp.collation import collation -print(collation(['ไก่','ไข่','ก','ฮา'])) # ['ก', 'ไก่', 'ไข่', 'ฮา'] -``` - -input list - -returns list - -### Get current time in Thai - -```python -from pythainlp.date import now -now() # '30 พฤษภาคม 2560 18:45:24' -``` -### Find the most frequent words. - -```python -from pythainlp.rank import rank -rank(list) -``` - -returns dict - -**Example** - -```python ->>> rank(['แมง','แมง','คน']) -Counter({'แมง': 2, 'คน': 1}) -``` - -### Incorrect input language correction - -```python -from pythainlp.change import * -``` - -- texttothai(str) - eng to thai. -- texttoeng(str) - thai to eng. - -### word_vector - -```python -from pythainlp.word_vector import thai2vec -``` - -word_vector is word vector in PyThaiNLP - -It's work using thai2vec (https://github.com/cstorm125/thai2vec) - -thai2vec developed by Charin Polpanumas - -#### thai2vec - -requirements - -- gensim -- numpy - -##### API - -- get_model() - get gensim model -- most_similar_cosmul(positive,negative) -- doesnt_match(listdata) -- similarity(word1,word2) -- sentence_vectorizer(ss,dim=300,use_mean=False) -- about() - -### Thai Soundex - -credit Korakot Chaovavanich (from https://gist.github.com/korakot/0b772e09340cac2f493868da035597e8) - -- LK82 -- Udom83 - -**Example** - -```python ->>> from pythainlp.soundex import LK82,Udom83 ->>> print(LK82('รถ')) -ร3000 ->>> print(LK82('รด')) -ร3000 ->>> print(LK82('จัน')) -จ4000 ->>> print(LK82('จันทร์')) -จ4000 ->>> print(Udom83('รถ')) -ร800000 -``` - -### Thai meta sound - -``` -Snae & Brückner. (2009). Novel Phonetic Name Matching Algorithm with a Statistical Ontology for Analysing Names Given in Accordance with Thai Astrology. Retrieved from https://pdfs.semanticscholar.org/3983/963e87ddc6dfdbb291099aa3927a0e3e4ea6.pdf -``` - -**Example** - -```python ->>> from pythainlp.MetaSound import * ->>> MetaSound('คน') -'15' -``` - -### Thai sentiment analysis - -using data from [https://github.com/wannaphongcom/lexicon-thai/tree/master/ข้อความ/](https://github.com/wannaphongcom/lexicon-thai/tree/master/ข้อความ/) - -```python -from pythainlp.sentiment import sentiment -sentiment(str) -``` - -input str returns pos , neg or neutral - -### Util - -using - -```python -from pythainlp.util import * -``` - -#### ngrams - -for building ngrams - -```python -ngrams(token,num) -``` - -- token - list -- num - ngrams - -#### bigrams - -for building bigrams - -```python -bigrams(token) -``` - -- token - list - -#### trigram - -for building trigram - -```python -trigram(token) -``` - -- token - list - -#### normalize - -fix thai text - -```python -normalize(text) -``` - -**Example** - -```python ->>> print(normalize("เเปลก")=="แปลก") # เ เ ป ล ก กับ แปลก -True -``` - -### Corpus - -#### Thai stopword - -```python -from pythainlp.corpus import stopwords -stopwords = stopwords.words('thai') -``` - -#### Thai country name - -```python -from pythainlp.corpus import country -country.get_data() -``` - -#### Tone in Thai - -```python -from pythainlp.corpus import tone -tone.get_data() -``` - -#### Consonant in thai - -```python -from pythainlp.corpus import alphabet -alphabet.get_data() -``` - -#### Word list in thai - -```python -from pythainlp.corpus.thaiword import get_data # old data -get_data() -from pythainlp.corpus.newthaiword import get_data # new data -get_data() -``` - -#### ConceptNet - -Thai tool for ConceptNet. - -**find edges** - -```python -edges(word,lang='th') -``` - -return dict - -#### Thai WordNet - -import - -```python -from pythainlp.corpus import wordnet -``` - -**Use** - -It's like nltk. - -- wordnet.synsets(word) -- wordnet.synset(name_synsets) -- wordnet.all_lemma_names(pos=None, lang="tha") -- wordnet.all_synsets(pos=None) -- wordnet.langs() -- wordnet.lemmas(word,pos=None,lang="tha") -- wordnet.lemma(name_synsets) -- wordnet.lemma_from_key(key) -- wordnet.path_similarity(synsets1,synsets2) -- wordnet.lch_similarity(synsets1,synsets2) -- wordnet.wup_similarity(synsets1,synsets2) -- wordnet.morphy(form, pos=None) -- wordnet.custom_lemmas(tab_file, lang) - -**Example** - -```python ->>> from pythainlp.corpus import wordnet ->>> print(wordnet.synsets('หนึ่ง')) -[Synset('one.s.05'), Synset('one.s.04'), Synset('one.s.01'), Synset('one.n.01')] ->>> print(wordnet.synsets('หนึ่ง')[0].lemma_names('tha')) -[] ->>> print(wordnet.synset('one.s.05')) -Synset('one.s.05') ->>> print(wordnet.synset('spy.n.01').lemmas()) -[Lemma('spy.n.01.spy'), Lemma('spy.n.01.undercover_agent')] ->>> print(wordnet.synset('spy.n.01').lemma_names('tha')) -['สปาย', 'สายลับ'] -``` - -#### TNC - -Tool for Thai National Corpus (http://www.arts.chula.ac.th/~ling/TNC/index.php) - -##### word_frequency - -find word frequency - -```python -word_frequency(word,domain='all') -``` -domain - -- all -- imaginative -- natural-pure-science -- applied-science -- social-science -- world-affairs-history -- commerce-finance -- arts -- belief-thought -- leisure -- others \ No newline at end of file diff --git a/docs/archive/pythainlp-1-6-thai.md b/docs/archive/pythainlp-1-6-thai.md deleted file mode 100644 index b7e5b67a9..000000000 --- a/docs/archive/pythainlp-1-6-thai.md +++ /dev/null @@ -1,683 +0,0 @@ -# คู่มือการใช้งาน PyThaiNLP 1.6 - -[TOC] - -**เอกสารใหม่ย้ายไปที่ https://thainlp.org/pythainlp/docs/1.7/*** - -Natural language processing หรือ การประมวลภาษาธรรมชาติ โมดูล PyThaiNLP เป็นโมดูลที่ถูกพัฒนาขึ้นเพื่อพัฒนาการประมวลภาษาธรรมชาติภาษาไทยในภาษา Python และ**มันฟรี (ตลอดไป) เพื่อคนไทยและชาวโลกทุกคน !** - -> เพราะโลกขับเคลื่อนต่อไปด้วยการแบ่งปัน - -รองรับ Python 2.7 และ Python 3.4 ขึ้นไปเท่านั้น - -ติดตั้งใช้คำสั่ง - -``` -pip install pythainlp -``` - -ปัจจุบันนี้ PyThaiNLP ไม่ต้องการ PyICU ในการใช้งาน API อีกแล้ว แต่หากท่านต้องการใช้ API ที่มี PyICU ให้ทำตามคำแนะนำข้างล่างนี้ - -**วิธีติดตั้งสำหรับ Windows** - -การติดตั้ง pythainlp บน windows ต้องติดตั้ง pyicu ซึ่งทำได้ยากมาก -วิธีที่ง่ายที่สุดคือใช้ wheel - -1. [http://www.lfd.uci.edu/~gohlke/pythonlibs/#pyicu](http://www.lfd.uci.edu/~gohlke/pythonlibs/#pyicu) แล้ว download wheel ตาม python ตัวเองเช่น - ผมใช้ python x64 3.6.1 บน Windows ก็ให้ใช้ PyICU‑1.9.7‑cp36‑cp36m‑win_amd64.whl - -2. `pip install PyICU‑1.9.7‑cp36‑cp36m‑win_amd64.whl` - -3. `pip install pythainlp` - -**ติดตั้งบน Mac** - -** แนะนำให้ใช้ icu 58.2 เนื่องจาก icu 59.1 มาปัญหากับ PyICU ** - -```sh -$ brew install icu4c --force -$ brew link --force icu4c -$ CFLAGS=-I/usr/local/opt/icu4c/include LDFLAGS=-L/usr/local/opt/icu4c/lib pip install pythainlp -``` - -ข้อมูลเพิ่มเติม [คลิกที่นี้](https://medium.com/data-science-cafe/install-polyglot-on-mac-3c90445abc1f#.rdfrorxjx) - -## API - -### tokenize - -#### word_tokenize - -สำหรับการตัดคำไทยนั้น ใช้ API ดังต่อไปนี้ - -```python -from pythainlp.tokenize import word_tokenize -word_tokenize(text,engine) -``` -text คือ ข้อความในรูปแบบสตริง str เท่านั้น - -engine คือ ระบบตัดคำไทย ปัจจุบันนี้ PyThaiNLP ได้พัฒนามี 6 engine ให้ใช้งานกันดังนี้ - -1. newmm - ใช้ Maximum Matching algorithm ในการตัดคำภาษาไทย โค้ดชุดใหม่ โดยใช้โค้ดคุณ Korakot Chaovavanich จาก https://www.facebook.com/groups/408004796247683/permalink/431283740586455/ มาพัฒนาต่อ (ค่าเริ่มต้น) -2. icu - engine ตัวดั้งเดิมของ PyThaiNLP (ความแม่นยำต่ำ) -3. dict - เป็นการตัดคำโดยใช้พจานุกรมจาก thaiword.txt ใน corpus (ความแม่นยำปานกลาง) **จะคืนค่า False หากข้อความนั้นไม่สามารถตัดคำได้** -4. longest-matching ใช้ Longest matching ในการตัดคำ -5. mm - ใช้ Maximum Matching algorithm ในการตัดคำภาษาไทย - API ชุดเก่า **อยู่ในหมวดบำรุงรักษาเท่านั้น** -6. pylexto ใช้ LexTo ในการตัดคำ โดยเป็น Longest matching -7. deepcut ใช้ deepcut จาก https://github.com/rkcosmos/deepcut ในการตัดคำภาษาไทย -8. wordcutpy ใช้ wordcutpy (https://github.com/veer66/wordcutpy) ในการตัดคำ - -คืนค่าเป็น ''list'' เช่น ['แมว','กิน'] - -**ตัวอย่าง** - -``` -สำหรับผู้ใช้งาน Python 2.7 ให้ทำการ encode ให้เป็น UTF-8 ก่อนใช้งานโมดูล PyThaiNLP - -เช่น text=u'ผมรักคุณนะครับโอเคบ่พวกเราเป็นคนไทยรักภาษาไทยภาษาบ้านเกิด' -``` - -**การใช้งาน** - -```python -from pythainlp.tokenize import word_tokenize -text='ผมรักคุณนะครับโอเคบ่พวกเราเป็นคนไทยรักภาษาไทยภาษาบ้านเกิด' -a=word_tokenize(text,engine='icu') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอ', 'เค', 'บ่', 'พวก', 'เรา', 'เป็น', 'คน', 'ไทย', 'รัก', 'ภาษา', 'ไทย', 'ภาษา', 'บ้าน', 'เกิด'] -b=word_tokenize(text,engine='dict') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คนไทย', 'รัก', 'ภาษาไทย', 'ภาษา', 'บ้านเกิด'] -c=word_tokenize(text,engine='mm') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คนไทย', 'รัก', 'ภาษาไทย', 'ภาษา', 'บ้านเกิด'] -d=word_tokenize(text,engine='pylexto') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คนไทย', 'รัก', 'ภาษาไทย', 'ภาษา', 'บ้านเกิด'] -e=word_tokenize(text,engine='newmm') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คนไทย', 'รัก', 'ภาษาไทย', 'ภาษา', 'บ้านเกิด'] -g=word_tokenize(text,engine='wordcutpy') # ['ผม', 'รัก', 'คุณ', 'นะ', 'ครับ', 'โอเค', 'บ่', 'พวกเรา', 'เป็น', 'คน', 'ไทย', 'รัก', 'ภาษา', 'ไทย', 'ภาษา', 'บ้านเกิด'] -``` - -#### dict_word_tokenize - -```python -from pythainlp.tokenize import dict_word_tokenize -dict_word_tokenize(text,file,engine) -``` - -เป็นคำสั่งสำหรับตัดคำโดยใช้ข้อมูลที่ผู้ใช้กำหนด - -text คือ ข้อความที่ต้องการตัดคำ - -file คือ ที่ตั้งไฟล์ที่ต้องการมาเป็นฐานข้อมูลตัดคำ - -engine คือ เครื่องมือตัดคำ - -- newmm ตัดคำด้วย newmm -- wordcutpy ใช้ wordcutpy (https://github.com/veer66/wordcutpy) ในการตัดคำ -- mm ตัดคำด้วย mm -- longest-matching ตัดคำโดยใช้ longest matching - -ตัวอย่างการใช้งาน https://gist.github.com/wannaphongcom/1e862583051bf0464b6ef4ed592f739c - -``` -สำหรับผู้ใช้งาน Python 2.7 ให้ทำการ encode ให้เป็น UTF-8 ก่อนใช้งานโมดูล PyThaiNLP - -เช่น text=u'ผมรักคุณนะครับโอเคบ่พวกเราเป็นคนไทยรักภาษาไทยภาษาบ้านเกิด' -``` - -#### sent_tokenize - -ใช้ตัดประโยคภาษาไทย - -```python -sent_tokenize(text,engine='whitespace+newline') -``` - -text คือ ข้อความในรูปแบบสตริง - -engine คือ เครื่องมือสำหรับใช้ตัดประโยค - -- whitespace ตัดประโยคจากช่องว่าง -- whitespace+newline ตัดประโยคจากช่องว่างและตัดจากการขึ้นบรรทัดใหม่ - -คืนค่า ออกมาเป็น list - -#### WhitespaceTokenizer - -ใช้ตัดคำ/ประโยคจากช่องว่างในสตริง - -```python ->>> from pythainlp.tokenize import WhitespaceTokenizer ->>> WhitespaceTokenizer("ทดสอบ ตัดคำช่องว่าง") -['ทดสอบ', 'ตัดคำช่องว่าง'] -``` - -``` -สำหรับผู้ใช้งาน Python 2.7 ให้ทำการ encode ให้เป็น UTF-8 ก่อนใช้งานโมดูล PyThaiNLP - -เช่น WhitespaceTokenizer(u"ทดสอบ ตัดคำช่องว่าง") -``` - - - -#### isthai - -ใช้เช็คข้อความว่าเป็นภาษาไทยทั้งหมดกี่ % - -```python -isthai(text,check_all=False) -``` - -text คือ ข้อความหรือ list ตัวอักษร - -check_all สำหรับส่งคืนค่า True หรือ False เช็คทุกตัวอักษร - -**การส่งคืนค่า** - -```python -{'thai':% อักษรภาษาไทย,'check_all':tuple โดยจะเป็น (ตัวอักษร,True หรือ False)} -``` - -#### Thai Character Clusters (TCC) - -รองรับ Thai Character Clusters (TCC) โดยจะแบ่งกลุ่มด้วย / - -**เครดิต** - -TCC : Mr.Jakkrit TeCho - -grammar : คุณ Wittawat Jitkrittum (https://github.com/wittawatj/jtcc/blob/master/TCC.g) - -โค้ด : คุณ Korakot Chaovavanich - -**การใช้งาน** - -```python ->>> from pythainlp.tokenize import tcc ->>> tcc.tcc('ประเทศไทย') -'ป/ระ/เท/ศ/ไท/ย' -``` - -#### Enhanced Thai Character Cluster (ETCC) - -นอกจาก TCC แล้ว PyThaiNLP 1.4 ยังรองรับ Enhanced Thai Character Cluster (ETCC) โดยแบ่งกลุ่มด้วย / - -**การใช้งาน** - -```python ->>> from pythainlp.tokenize import etcc ->>> etcc.etcc('คืนความสุข') -'/คืน/ความสุข' -``` - -### tag - -เป็น Part-of-speech tagging ภาษาไทย - -```python -from pythainlp.tag import pos_tag -pos_tag(list,engine='old') -``` - -list คือ list ที่เก็บข้อความหลังผ่านการตัดคำแล้ว - -engine คือ ชุดเครื่องมือในการ postaggers มี 2 ตัวดังนี้ - -1. old เป็น UnigramTagger (ค่าเริ่มต้น) -2. artagger เป็น RDR POS Tagger ละเอียดยิ่งกว่าเดิม รองรับเฉพาะ Python 3 เท่านั้น - -### summarize - -เป็นระบบสรุปเอกสารภาษาไทยแบบง่าย ๆ - -summarize_text(text,n,engine='frequency') - - text เป็นข้อความ - n คือ จำนวนประโยคสรุป - engine ที่รองรับ - - frequency -**การใช้งาน** - -```python ->>> from pythainlp.summarize import summarize_text ->>> summarize_text(text="อาหาร หมายถึง ของแข็งหรือของเหลว ที่กินหรือดื่มเข้าสู่ร่างกายแล้ว จะทำให้เกิดพลังงานและความร้อนยเจริญเติบโต ซ่อมแซมส่วนที่สึกหรอ ควบคุมการเปลี่ยนแปลงต่างๆ ในร่างกาย ช่วยทำให้อวัยวะต่างๆ ทำงานได้อย่างปกติ อาหารจะต้องงกาย",n=1,engine='frequency') -['อาหารจะต้องไม่มีพิษและไม่เกิดโทษต่อร่างกาย'] -``` - -### word_vector - -```python -from pythainlp.word_vector import thai2vec -``` - -word_vector เป็นระบบ word vector ใน PyThaiNLP - -ปัจจุบันนี้รองรับเฉพาะ thai2vec (https://github.com/cstorm125/thai2vec) - -thai2vec พัฒนาโดยคุณ Charin Polpanumas - -#### thai2vec - -ความต้องการโมดูล - -- gensim -- numpy - -##### API - -- get_model() - รับข้อมูล model ในรูปแบบของ gensim -- most_similar_cosmul(positive,negative) -- doesnt_match(listdata) -- similarity(word1,word2) - หาค่าความคล้ายกันระหว่าง 2 คำ โดยทั้งคู่เป็น str -- sentence_vectorizer(ss,dim=300,use_mean=False) -- about() - รายละเอียด thai2vec - - - -### keywords - -ใช้หา keywords จากข้อความภาษาไทย - -#### find_keyword - -การทำงาน หาคำที่ถูกใช้งานมากกว่าค่าขั้นต่ำที่กำหนดได้ โดยจะลบ stopword ออกไป - -```python -find_keyword(word_list,lentext=3) -``` - -word_list คือ list ของข้อความที่ผ่านการตัดคำแล้ว - -lentext คือ จำนวนคำขั้นต่ำที่ต้องการหา keyword - -คืนค่าออกมาเป็น dict - -### romanization - -```python -from pythainlp.romanization import romanization -romanization(str,engine='royin') -``` -มี 2 engine ดังนี้ - -- pyicu ส่งค่า Latin -- royin ใช้หลักเกณฑ์การถอดอักษรไทยเป็นอักษรโรมัน ฉบับราชบัณฑิตยสถาน (**หากมีข้อผิดพลาด ให้ใช้คำอ่าน เนื่องจากตัว royin ไม่มีตัวแปลงคำเป็นคำอ่าน**) - -data : - -รับค่า ''str'' ข้อความ - -คืนค่าเป็น ''str'' ข้อความ - -**ตัวอย่าง** - -```python -from pythainlp.romanization import romanization -romanization("แมว") # 'maew' -``` - -### spell - -เป็น API สำหรับเช็คคำผิดในภาษาไทย - -```python -spell(word,engine='pn') -``` - -engine ที่รองรับ - -- pn พัฒนามาจาก Peter Norvig (ค่าเริ่มต้น) -- hunspell ใช้ hunspell (ไม่รองรับ Python 2.7) - -**ตัวอย่างการใช้งาน** - -```python -from pythainlp.spell import * -a=spell("สี่เหลียม") -print(a) # ['สี่เหลี่ยม'] -``` -#### pn - -```python -correction(word) -``` - -แสดงคำที่เป็นไปได้มากที่สุด - -**ตัวอย่างการใช้งาน** - -```python -from pythainlp.spell.pn import correction -a=correction("สี่เหลียม") -print(a) # ['สี่เหลี่ยม'] -``` - -ผลลัพธ์ - -``` -สี่เหลี่ยม -``` - -### pythainlp.number - -```python -from pythainlp.number import * -``` -จัดการกับตัวเลข โดยมีดังนี้ - -- thai_num_to_num(str) - เป็นการแปลงเลขไทยสู่เลข -- thai_num_to_text(str) - เลขไทยสู่ข้อความ -- num_to_thai_num(str) - เลขสู่เลขไทย -- num_to_text(str) - เลขสู่ข้อความ -- text_to_num(str) - ข้อความสู่เลข -- numtowords(float) - อ่านจำนวนตัวเลขภาษาไทย (บาท) รับค่าเป็น ''float'' คืนค่าเป็น 'str' - -### collation - -ใช้ในการเรียงลำดับข้อมูลภาษาไทยใน List - -```python -from pythainlp.collation import collation -print(collation(['ไก่','ไข่','ก','ฮา'])) # ['ก', 'ไก่', 'ไข่', 'ฮา'] -``` - -รับ list คืนค่า list - -### date - -#### now - -รับเวลาปัจจุบันเป็นภาษาไทย - -```python -from pythainlp.date import now -now() # '30 พฤษภาคม 2560 18:45:24' -``` -### rank - -#### rank - -หาคำที่มีจำนวนการใช้งานมากที่สุด - -```python -from pythainlp.rank import rank -rank(list) -``` - -คืนค่าออกมาเป็น dict - -**ตัวอย่างการใช้งาน** - -```python ->>> rank(['แมง','แมง','คน']) -Counter({'แมง': 2, 'คน': 1}) -``` - -### change - -#### แก้ไขปัญหาการพิมพ์ลืมเปลี่ยนภาษา - -```python -from pythainlp.change import * -``` - -มีคำสั่งดังนี้ - -- texttothai(str) แปลงแป้นตัวอักษรภาษาอังกฤษเป็นภาษาไทย -- texttoeng(str) แปลงแป้นตัวอักษรภาษาไทยเป็นภาษาอังกฤษ - -คืนค่าออกมาเป็น str - -### soundex - -เดติด คุณ Korakot Chaovavanich (จาก https://gist.github.com/korakot/0b772e09340cac2f493868da035597e8) - -กฎที่รองรับในเวชั่น 1.4 - -- กฎการเข้ารหัสซาวน์เด็กซ์ของ วิชิตหล่อจีระชุณห์กุล และ เจริญ คุวินทร์พันธุ์ - LK82 -- กฎการเข้ารหัสซาวน์เด็กซ์ของ วรรณี อุดมพาณิชย์ - Udom83 - -**การใช้งาน** - -```python ->>> from pythainlp.soundex import LK82,Udom83 ->>> print(LK82('รถ')) -ร3000 ->>> print(LK82('รด')) -ร3000 ->>> print(LK82('จัน')) -จ4000 ->>> print(LK82('จันทร์')) -จ4000 ->>> print(Udom83('รถ')) -ร800000 -``` - -### Meta Sound ภาษาไทย - -``` -Snae & Brückner. (2009). Novel Phonetic Name Matching Algorithm with a Statistical Ontology for Analysing Names Given in Accordance with Thai Astrology. Retrieved from https://pdfs.semanticscholar.org/3983/963e87ddc6dfdbb291099aa3927a0e3e4ea6.pdf -``` - -**การใช้งาน** - -```python ->>> from pythainlp.MetaSound import * ->>> MetaSound('คน') -'15' -``` - -### sentiment - -เป็น Sentiment analysis ภาษาไทย ใช้ข้อมูลจาก [https://github.com/wannaphongcom/lexicon-thai/tree/master/ข้อความ/](https://github.com/wannaphongcom/lexicon-thai/tree/master/ข้อความ/) - -```python -from pythainlp.sentiment import sentiment -sentiment(str) -``` - -รับค่า str ส่งออกเป็น pos , neg - -### Util - -การใช้งาน - -```python -from pythainlp.util import * -``` - -#### ngrams - -สำหรับสร้าง n-grams - -```python -ngrams(token,num) -``` - -- token คือ list -- num คือ จำนวน ngrams - -#### bigrams - -สำหรับสร้าง bigrams - -```python -bigrams(token) -``` - -- token คือ list - -#### trigram - -สำหรับสร้าง trigram - -```python -trigram(token) -``` - -- token คือ list - -#### normalize - -ซ่อมข้อความภาษาไทย เช่น กี่่่ ไปเป็น กี่ - -```python -normalize(text) -``` - -**ตัวอย่าง** - -```python ->>> print(normalize("เเปลก")=="แปลก") # เ เ ป ล ก กับ แปลก -True -``` - -### Corpus - -#### WordNet ภาษาไทย - -เรียกใช้งาน - -```python -from pythainlp.corpus import wordnet -``` - -**การใช้งาน** - -API เหมือนกับ NLTK โดยรองรับ API ดังนี้ - -- wordnet.synsets(word) -- wordnet.synset(name_synsets) -- wordnet.all_lemma_names(pos=None, lang="tha") -- wordnet.all_synsets(pos=None) -- wordnet.langs() -- wordnet.lemmas(word,pos=None,lang="tha") -- wordnet.lemma(name_synsets) -- wordnet.lemma_from_key(key) -- wordnet.path_similarity(synsets1,synsets2) -- wordnet.lch_similarity(synsets1,synsets2) -- wordnet.wup_similarity(synsets1,synsets2) -- wordnet.morphy(form, pos=None) -- wordnet.custom_lemmas(tab_file, lang) - -**ตัวอย่าง** - -```python ->>> from pythainlp.corpus import wordnet ->>> print(wordnet.synsets('หนึ่ง')) -[Synset('one.s.05'), Synset('one.s.04'), Synset('one.s.01'), Synset('one.n.01')] ->>> print(wordnet.synsets('หนึ่ง')[0].lemma_names('tha')) -[] ->>> print(wordnet.synset('one.s.05')) -Synset('one.s.05') ->>> print(wordnet.synset('spy.n.01').lemmas()) -[Lemma('spy.n.01.spy'), Lemma('spy.n.01.undercover_agent')] ->>> print(wordnet.synset('spy.n.01').lemma_names('tha')) -['สปาย', 'สายลับ'] -``` - -#### stopword ภาษาไทย - -```python -from pythainlp.corpus import stopwords -stopwords = stopwords.words('thai') -``` - -#### ชื่อประเทศ ภาษาไทย - -```python -from pythainlp.corpus import country -country.get_data() -``` - -#### ตัววรรณยุกต์ในภาษาไทย - -```python -from pythainlp.corpus import tone -tone.get_data() -``` - -#### ตัวพยัญชนะในภาษาไทย - -```python -from pythainlp.corpus import alphabet -alphabet.get_data() -``` - -#### รายการคำในภาษาไทย - -```python -from pythainlp.corpus.thaiword import get_data # ข้อมูลเก่า -get_data() -from pythainlp.corpus.newthaiword import get_data # ข้อมูลใหม่ -get_data() -``` - -#### provinces - -สำหรับจัดการชื่อจังหวัดในประเทศไทย - -##### get_data - -รับข้อมูลชื่อจังหวัดในประเทศไทบ - -```python -get_data() -``` - -คือค่าออกมาเป็น list - -##### parsed_docs - -สำหรับใช้ Tag ชื่อจังหวัดในประเทศไทย - -```python -parsed_docs(text_list) -``` - -text_list คือ ข้อความภาษาไทยที่อยู่ใน list โดยผ่านการตัดคำมาแล้ว - -**ตัวอย่าง** - -```python ->>> d=['หนองคาย', 'เป็น', 'เมือง', 'น่าอยู่', 'อันดับ', 'ต้น', 'ๆ', 'ของ', 'โลก', 'นอกจากนี้', 'ยัง', 'มี', 'เชียงใหม่'] ->>> parsed_docs(d) -["[LOC : 'หนองคาย']", 'เป็น', 'เมือง', 'น่าอยู่', 'อันดับ', 'ต้น', 'ๆ', 'ของ', 'โลก', 'นอกจากนี้', 'ยัง', 'มี', "[LOC : 'เชียงใหม่']"] -``` - -#### ConceptNet - -เครื่องมือสำหรับ ConceptNet - -**ค้นหา edges** - -```python -edges(word,lang='th') -``` - -return dict - -#### TNC - -สำหรับใช้จัดการกับ Thai National Corpus (http://www.arts.chula.ac.th/~ling/TNC/index.php) - -##### word_frequency - -ใช้วัดความถี่ของคำ - -```python -word_frequency(word,domain='all') -``` - -word คือ คำ - -domain คือ หมวดหมู่ของคำ - -มีหมวดหมู่ดังนี้ - -- all -- imaginative -- natural-pure-science -- applied-science -- social-science -- world-affairs-history -- commerce-finance -- arts -- belief-thought -- leisure -- others - -เขียนโดย PyThaiNLP diff --git a/docs/archive/pythainlp-1-7.md b/docs/archive/pythainlp-1-7.md deleted file mode 100644 index 09a8b76a0..000000000 --- a/docs/archive/pythainlp-1-7.md +++ /dev/null @@ -1 +0,0 @@ -See https://thainlp.org/pythainlp/docs/1.7/ diff --git a/docs/archive/pythainlp-dev-thai.md b/docs/archive/pythainlp-dev-thai.md deleted file mode 100644 index ec7c575d1..000000000 --- a/docs/archive/pythainlp-dev-thai.md +++ /dev/null @@ -1,599 +0,0 @@ -# คู่มือการใช้งาน PyThaiNLP - -[TOC] - -โมดูล PyThaiNLP เป็นโมดูลที่ถูกพัฒนาขึ้นเพื่อประมวลภาษาไทยด้วยภาษาโปรแกรม Python และ**มันฟรี (ตลอดไป) เพื่อคนไทยและชาวโลกทุกคน!** - -> เพราะโลกขับเคลื่อนต่อไปด้วยการแบ่งปัน - -รองรับ Python 3.4 ขึ้นไปเท่านั้น - -ติดตั้งโดยใช้คำสั่ง - -``` -pip install pythainlp -``` - -ปัจจุบัน PyThaiNLP ไม่จำเป็นต้องใช้ PyICU แล้ว แต่หากท่านต้องการใช้ API ที่มี PyICU ให้ทำตามคำแนะนำข้างล่างนี้ - -**ติดตั้ง PyICU บน Windows** - -การติดตั้ง PyThaiNLP บน Windows ต้องติดตั้ง PyICU ก่อน วิธีที่ง่ายที่สุดคือใช้ wheel ที่ถูกสร้างมาก่อนแล้ว - -1. ดาวน์โหลด wheel ตามแพลตฟอร์มที่ต้องการจาก [http://www.lfd.uci.edu/~gohlke/pythonlibs/#pyicu](http://www.lfd.uci.edu/~gohlke/pythonlibs/#pyicu) เช่น Python x64 3.6 บน Windows ให้ใช้ PyICU‑2.x‑cp36‑cp36m‑win_amd64.whl - -2. `pip install PyICU‑2.1‑cp36‑cp36m‑win_amd64.whl` - -3. `pip install pythainlp` - -**ติดตั้ง PyICU บน macOS** - -```sh -brew install icu4c --force -brew link --force icu4c -CFLAGS=-I/usr/local/opt/icu4c/include LDFLAGS=-L/usr/local/opt/icu4c/lib pip install pythainlp -``` - -ข้อมูลเพิ่มเติมที่ https://medium.com/data-science-cafe/install-polyglot-on-mac-3c90445abc1f - -## API - -### tokenize - -#### word_tokenize - -ตัดคำภาษาไทย - -```python -from pythainlp.tokenize import word_tokenize - -word_tokenize(text, engine) -``` -text คือ ข้อความในรูปแบบสตริง str เท่านั้น - -engine คือ ระบบตัดคำ ปัจจุบันมี engine ดังนี้ - -- newmm (ค่าเริ่มต้น) - ใช้พจนานุกรม ด้วยวิธี Maximum Matching + Thai Character Cluster โค้ดชุดใหม่[โดยคุณ Korakot Chaovavanich](https://www.facebook.com/groups/408004796247683/permalink/431283740586455/) -- longest - ใช้พจนานุกรม ด้วยวิธี Longest Matching -- icu - เรียกใช้ตัวตัดคำจาก ICU ใช้พจนานุกรม (ความแม่นยำต่ำ) -- deepcut - เรียกใช้ตัวตัดคำจาก [deepcut](https://github.com/rkcosmos/deepcut) ใช้การเรียนรู้ของเครื่อง - -คืนค่าเป็น ''list'' เช่น ['แมว', 'กิน'] - -**การใช้งาน** - -```python -from pythainlp.tokenize import word_tokenize - -text = "โอเคบ่เรารักภาษาถิ่น" -word_tokenize(text, engine="newmm") # ['โอเค', 'บ่', 'เรา', 'รัก', 'ภาษาถิ่น'] -word_tokenize(text, engine="icu") # ['โอ', 'เค', 'บ่', 'เรา', 'รัก', 'ภาษา', 'ถิ่น'] -``` - -#### dict_word_tokenize - -ตัดคำโดยใช้พจนานุกรมที่ผู้ใช้กำหนด - -```python -from pythainlp.tokenize import dict_word_tokenize -dict_word_tokenize(text, filename, engine) -``` - -text คือ ข้อความที่ต้องการตัดคำ - -filename คือ ที่ตั้งไฟล์ที่ต้องการมาเป็นฐานข้อมูลตัดคำ - -engine คือ ระบบตัดคำ (ดูรายละเอียดที่ word_tokenize) -- newmm -- longest - -ตัวอย่างการใช้งาน https://gist.github.com/wannaphongcom/1e862583051bf0464b6ef4ed592f739c - - -#### sent_tokenize - -ตัดประโยคภาษาไทย - -```python -sent_tokenize(text, engine="whitespace+newline") -``` - -text คือ ข้อความในรูปแบบสตริง - -engine คือ เครื่องมือสำหรับใช้ตัดประโยค - -- whitespace ตัดประโยคจากช่องว่าง -- whitespace+newline ตัดประโยคจากช่องว่างและตัดจากการขึ้นบรรทัดใหม่ - -คืนค่าเป็น list - -#### WhitespaceTokenizer - -ใช้ตัดคำ/ประโยคจากช่องว่างในสตริง - -```python -from pythainlp.tokenize import WhitespaceTokenizer - -WhitespaceTokenizer("ทดสอบ ตัดคำช่องว่าง") # ['ทดสอบ', 'ตัดคำช่องว่าง'] -``` - - -#### isthai - -ตรวจสอบข้อความว่ามีอักษรไทยร้อยละเท่าใด - -```python -isthai(text, check_all=False) -``` - -text คือ ข้อความหรือ list ตัวอักษร - -check_all สำหรับส่งคืนค่า True หรือ False เช็คทุกตัวอักษร - -**การส่งคืนค่า** - -```python -{'thai':% อักษรภาษาไทย,'check_all':tuple โดยจะเป็น (ตัวอักษร,True หรือ False)} -``` - -#### Thai Character Clusters (TCC) - -รองรับ Thai Character Clusters (TCC) โดยจะแบ่งกลุ่มด้วย / - -**เครดิต** - -- TCC: Jakkrit TeCho -- Grammar: Wittawat Jitkrittum (https://github.com/wittawatj/jtcc/blob/master/TCC.g) -- Python code: Korakot Chaovavanich - -**การใช้งาน** - -```python -from pythainlp.tokenize import tcc - -tcc.tcc("ประเทศไทย") # 'ป/ระ/เท/ศ/ไท/ย' -``` - -#### Enhanced Thai Character Cluster (ETCC) - -นอกจาก TCC แล้ว PyThaiNLP ยังรองรับ Enhanced Thai Character Cluster (ETCC) โดยแบ่งกลุ่มด้วย / - -**การใช้งาน** - -```python -from pythainlp.tokenize import etcc - -etcc.etcc('คืนความสุข') # '/คืน/ความสุข' -``` - -### tag - -Part-of-speech tagging ภาษาไทย - -```python -from pythainlp.tag import pos_tag - -pos_tag(text, engine="unigram", corpus="orchid") -``` - -list คือ list ที่เก็บข้อความหลังผ่านการตัดคำแล้ว - -engine คือ ตัวติดป้ายกำกับคำ (pos tagger) มีดังนี้ -- unigram (ค่าเริ่มต้น) - UnigramTagger -- perceptron - PerceptronTagger -- artagger - RDR POS Tagger ละเอียดยิ่งกว่าเดิม - -corpus ที่รองรับ -- orchid ใช้ข้อมูลจากคลังคำ ORCHID โดยเนคเทค -- pud ใช้ข้อมูล Parallel Universal Dependencies (PUD) treebanks - -### summarize - -สรุปเอกสารภาษาไทยแบบง่าย ๆ - -```python -summarize(text, n, engine="frequency") -``` - -text เป็นข้อความ - -n คือ จำนวนประโยคสรุป - -engine ที่รองรับ -- frequency - -**การใช้งาน** - -```python -from pythainlp.summarize import summarize - -summarize(text="อาหาร หมายถึง ของแข็งหรือของเหลว ที่กินหรือดื่มเข้าสู่ร่างกายแล้ว จะทำให้เกิดพลังงานและความร้อนยเจริญเติบโต ซ่อมแซมส่วนที่สึกหรอ ควบคุมการเปลี่ยนแปลงต่างๆ ในร่างกาย ช่วยทำให้อวัยวะต่างๆ ทำงานได้อย่างปกติ อาหารจะต้องงกาย", n=1, engine="frequency") -# ['อาหารจะต้องไม่มีพิษและไม่เกิดโทษต่อร่างกาย'] -``` - -### word_vector - -สร้างเวกเตอร์คำ - - -#### thai2fit - -ต้องการโมดูล -- gensim -- numpy - -##### API - -- get_model() - รับข้อมูล model ในรูปแบบของ gensim -- most_similar_cosmul(positive, negative) -- doesnt_match(listdata) -- similarity(word1, word2) - หาค่าความคล้ายระหว่าง 2 คำ โดยทั้งคู่เป็น str -- sentence_vectorizer(ss, dim=300, use_mean=False) - -### keywords - -หาคำสำคัญจากข้อความภาษาไทย - -#### find_keyword - -การทำงาน หาคำที่ถูกใช้งานมากกว่าค่าขั้นต่ำที่กำหนดได้ โดยจะลบ stopword ออก - -```python -from pythainlp.util import find_keyword - -find_keyword(word_list, lentext=3) -``` - -word_list คือ list ของข้อความที่ตัดคำแล้ว - -lentext คือ จำนวนคำขั้นต่ำที่ต้องการหา keyword - -คืนค่าเป็น dict - -### transliteration - -```python -from pythainlp.transliterate import romanize, transliterate - -romanize(str, engine="royin") -transliterate(str, engine="pyicu") -``` - -มี engine ดังนี้ -- pyicu ส่งค่าสัทอักษร -- royin ใช้หลักเกณฑ์การถอดอักษรไทยเป็นอักษรโรมัน ฉบับราชบัณฑิตยสถาน (**หากมีข้อผิดพลาด ให้ใช้คำอ่าน เนื่องจากตัว royin ไม่มีตัวแปลงคำเป็นคำอ่าน**) - -รับค่า ''str'' ข้อความ - -คืนค่าเป็น ''str'' ข้อความ - -**ตัวอย่าง** - -```python -from pythainlp.transliterate import romanize, transliterate - -romanize("แมว") # 'maew' -transliterate("นก") -``` - -### spell - -ตรวจสอบคำผิดในภาษาไทย - -```python -spell(word, engine="pn") -``` - -engine ที่รองรับ -- pn (ค่าเริ่มต้น) พัฒนาจาก Peter Norvig - -**ตัวอย่างการใช้งาน** - -```python -from pythainlp.spell import spell - -a = spell("สี่เหลียม") -print(a) # ['สี่เหลี่ยม'] -``` -#### pn - -```python -correct(word) -``` - -จะคืนค่าคำที่เป็นไปได้มากที่สุด - -**ตัวอย่างการใช้งาน** - -```python -from pythainlp.spell.pn import correct - -a = correct("สี่เหลียม") -print(a) # ['สี่เหลี่ยม'] -``` - -### pythainlp.number - -จัดการกับตัวเลข - -```python -from pythainlp.number import * -``` - -มีฟังก์ชันดังนี้ -- thai_num_to_num(str) - แปลงเลขไทยสู่เลขอารบิก -- thai_num_to_text(str) - เลขไทยสู่คำอ่านไทย -- num_to_thai_num(str) - เลขอารบิกสู่เลขไทย -- num_to_text(str) - เลขสู่ข้อความ -- text_to_num(str) - ข้อความสู่เลข -- bahttext(float) - อ่านจำนวนภาษาไทย (หน่วยเงินบาท) รับค่าเป็น ''float'' คืนค่าเป็น 'str' -- num_to_thaiword(float) - อ่านจำนวนภาษาไทย รับค่าเป็น ''float'' คืนค่าเป็น 'str' -- thaiword_to_num(List[str]) - แปลคำอ่านจำนวนไทยเป็นตัวเลขจำนวนเต็ม รับค่าเป็น ''List[str]'' คืนค่าเป็น int - -**ตัวอย่าง** - -```python -thaiword_to_num(["หกหมื่น", "หกพัน", "หกร้อย", "หกสิบ", "หก"]) # 66666 -``` - -### collation - -เรียงลำดับข้อมูลภาษาไทยใน List - -```python -from pythainlp.util import collate -print(collate(["ไก่", "ไข่", "กา", "ฮา"])) # ['กา', 'ไก่', 'ไข่', 'ฮา'] -``` - -รับ list คืนค่า list - -### date - -#### thai_strftime - -จัดรูปแบบข้อความบอกวันที่และเวลาเป็นภาษาไทยและปีพุทธศักราช - -```python -import datetime -from pythainlp.util import thai_strftime - -fmt = "%Aที่ %-d %B พ.ศ. %Y เวลา %H:%Mน. (%a %d-%b-%y)" -date = datetime.datetime(1976, 10, 6, 1, 40) -print(thai_strftime(date, fmt)) -# วันพุธที่ 6 ตุลาคม พ.ศ. 2519 เวลา 01:40น. (พ 06-ต.ค.-19) -``` -### rank - -#### rank - -หาคำที่มีจำนวนการใช้งานมากที่สุด - -```python -from pythainlp.util import rank - -rank(list) -``` - -คืนค่าออกมาเป็น dict - -**ตัวอย่างการใช้งาน** - -```python -rank(["แมง", "แมง", "คน"]) # Counter({'แมง': 2, 'คน': 1}) -``` - -### soundex - -กฎที่รองรับ -- lk82 - กฎการเข้ารหัสซาวน์เด็กซ์ของ วิชิตหล่อจีระชุณห์กุล และ เจริญ คุวินทร์พันธุ์ -- udom83 - กฎการเข้ารหัสซาวน์เด็กซ์ของ วรรณี อุดมพาณิชย์ -- metasound - กฎการเข้ารหัส MetaSound ของ Snae & Brückner (2009) - -เครดิต -- โค้ด lk82 และ udom83 - Korakot Chaovavanich https://gist.github.com/korakot/0b772e09340cac2f493868da035597e8 -- โค้ด metasound - Wannaphong Phattiyaphaibun, ปรับปรุงต่อโดย Arthit Suriyawongkul - -**การใช้งาน** - -```python -from pythainlp.soundex import lk82, metasound, udom83 - -print(lk82("รถ")) # ร3000 -print(lk82("รด")) # ร3000 -print(lk82("จัน")) # จ4000 -print(lk82("จันทร์")) # จ4000 -print(udom83("รถ")) # ร800000 -print(metasound("รัก")) # 'ร100' -``` - -### Util - -#### normalize - -ซ่อมข้อความภาษาไทย - -```python -normalize(text) -``` - -**ตัวอย่าง** - -```python -from pythainlp.util import normalize - -# เ เ ป ล ก กับ แปลก -normalize("เเปลก") == "แปลก" # True -``` - -#### แก้ไขปัญหาการลืมเปลี่ยนภาษาแป้นพิมพ์ - -```python -from pythainlp.util import eng_to_thai, thai_to_eng -``` - -มีคำสั่งดังนี้ - -- eng_to_thai(str) แปลงแป้นตัวอักษรอังกฤษเป็นไทย -- thai_to_eng(str) แปลงแป้นตัวอักษรไทยเป็นอังกฤษ - -คืนค่าออกมาเป็น str - -### Corpus - -#### WordNet ภาษาไทย - -เรียกใช้งาน - -```python -from pythainlp.corpus import wordnet -``` - -**การใช้งาน** - -API เหมือนกับ NLTK โดยรองรับ API ดังนี้ - -- wordnet.synsets(word) -- wordnet.synset(name_synsets) -- wordnet.all_lemma_names(pos=None, lang="tha") -- wordnet.all_synsets(pos=None) -- wordnet.langs() -- wordnet.lemmas(word,pos=None,lang="tha") -- wordnet.lemma(name_synsets) -- wordnet.lemma_from_key(key) -- wordnet.path_similarity(synsets1,synsets2) -- wordnet.lch_similarity(synsets1,synsets2) -- wordnet.wup_similarity(synsets1,synsets2) -- wordnet.morphy(form, pos=None) -- wordnet.custom_lemmas(tab_file, lang) - -**ตัวอย่าง** - -```python -from pythainlp.corpus import wordnet - -print(wordnet.synsets("หนึ่ง")) -# [Synset('one.s.05'), Synset('one.s.04'), Synset('one.s.01'), Synset('one.n.01')] - -print(wordnet.synsets("หนึ่ง")[0].lemma_names("tha")) -# [] - -print(wordnet.synset("one.s.05")) -# Synset('one.s.05') - -print(wordnet.synset("spy.n.01").lemmas()) -# [Lemma('spy.n.01.spy'), Lemma('spy.n.01.undercover_agent')] - -print(wordnet.synset("spy.n.01").lemma_names("tha")) -# ['สปาย', 'สายลับ'] -``` - -#### พยัญชนะในภาษาไทย - -```python -from pythainlp import thai_consonants -``` - -จะได้ str ที่มีพยัญชนะในภาษาไทยทั้งหมด - -#### วรรณยุกต์ในภาษาไทย - -```python -from pythainlp import thai_tonemarks -``` -จะได้ str ที่มีวรรณยุกต์ในภาษาไทยทั้งหมด - -#### stopword ภาษาไทย - -```python -from pythainlp.corpus import thai_stopwords - -stopwords = thai_stopwords() -``` - -#### รายการคำในภาษาไทย - -```python -from pythainlp.corpus import thai_words - -words = thai_words() -``` - -#### ชื่อประเทศ ภาษาไทย - -```python -from pythainlp.corpus import countries - -for country in countries(): - print(country) -``` - -#### provinces - -ข้อมูลชื่อจังหวัดในประเทศไทย - -```python -from pythainlp.corpus import provinces - -for province in provinces(): - print(province) -``` - -##### tag_provinces - -สำหรับใช้ติดป้ายกำกับชื่อจังหวัดในประเทศไทย - -```python -from pythainlp.tag.locations import tag_provinces - -tag_provinces(text_list) -``` - -text_list คือ ข้อความภาษาไทยที่อยู่ใน list โดยผ่านการตัดคำมาแล้ว - -**ตัวอย่าง** -```python -text = ['หนองคาย', 'น่าอยู่'] -tag_provinces(text) -# [('หนองคาย', 'B-LOCATION'), ('น่าอยู่', 'O')] -``` - -#### ConceptNet - -เครื่องมือสำหรับ ConceptNet - -**ค้นหา edges** - -```python -edges(word, lang="th") -``` - -return dict - -#### TNC - -สำหรับใช้จัดการกับ Thai National Corpus (http://www.arts.chula.ac.th/~ling/TNC/index.php) - -##### word_freq - -ใช้วัดความถี่ของคำ - -```python -word_freq(word, domain="all") -``` - -word คือ คำ - -domain คือ หมวดหมู่ของคำ - -มีหมวดหมู่ดังนี้ -- all -- imaginative -- natural-pure-science -- applied-science -- social-science -- world-affairs-history -- commerce-finance -- arts -- belief-thought -- leisure -- others diff --git a/tests/__init__.py b/tests/__init__.py index 11aa934fe..4a37baed6 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -371,7 +371,7 @@ def test_word_tokenize_mm(self): ["ฉัน", "รัก", "ภาษาไทย", "เพราะ", "ฉัน", "เป็น", "คนไทย"], ) - self.assertIsNone(multi_cut.mmcut("ทดสอบ")) + self.assertIsNotNone(multi_cut.mmcut("ทดสอบ")) self.assertIsNotNone(multi_cut.find_all_segment("รถไฟฟ้ากรุงเทพมหานครBTS")) self.assertEqual(multi_cut.find_all_segment(None), []) From eaedee2be402ffd38b9429b5efb88c411bfcb572 Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Sat, 20 Apr 2019 23:43:46 +0200 Subject: [PATCH 8/9] Put version number in sphinx doc --- docs/conf.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 5c2db572b..cdb7e7fe4 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,9 +1,6 @@ # -*- coding: utf-8 -*- # # Configuration file for the Sphinx documentation builder. -# -# This file does only contain a selection of the most common options. For a -# full list see the documentation: # http://www.sphinx-doc.org/en/master/config # -- Path setup -------------------------------------------------------------- @@ -27,9 +24,9 @@ copyright = u'2017-%s, %s (Apache Software License 2.0)' % (curyear, project) # The short X.Y version -version = '' +version = '2.0' # The full version, including alpha/beta/rc tags -release = '2.0' +release = '2.0.3' # -- General configuration --------------------------------------------------- From 7671f0e27e0fd082f2b7200f062228d7b042722a Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Sun, 21 Apr 2019 00:43:33 +0200 Subject: [PATCH 9/9] Update version number to 2.0.4 --- README.md | 4 ++-- bin/pythainlp | 2 +- conda.recipe/meta.yaml | 2 +- meta.yaml | 2 +- pythainlp/__init__.py | 2 +- setup.cfg | 2 +- setup.py | 2 +- tests/__init__.py | 1 + 8 files changed, 9 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index bec7ab474..f78384709 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ PyThaiNLP is a Python package for text processing and linguistic analysis, simil **This is a document for development branch (post 2.0). Things will break.** -- The latest stable release is [2.0.3](https://github.com/PyThaiNLP/pythainlp/tree/master) +- The latest stable release is [2.0.4](https://github.com/PyThaiNLP/pythainlp/tree/master) - PyThaiNLP 2 supports Python 3.6+. Some functions may work with older version of Python 3, but it is not well-tested and will not be supported. See [change log](https://github.com/PyThaiNLP/pythainlp/issues/118). - [Upgrading from 1.7](https://thainlp.org/pythainlp/docs/2.0/notes/pythainlp-1_7-2_0.html) - [Upgrade ThaiNER from 1.7](https://github.com/PyThaiNLP/pythainlp/wiki/Upgrade-ThaiNER-from-PyThaiNLP-1.7-to-PyThaiNLP-2.0) @@ -106,7 +106,7 @@ PyThaiNLP เป็นไลบารีภาษาไพทอนเพื่ **เอกสารนี้สำหรับรุ่นพัฒนา อาจมีการเปลี่ยนแปลงได้ตลอด** -- รุ่นเสถียรล่าสุดคือรุ่น [2.0.3](https://github.com/PyThaiNLP/pythainlp/tree/master) +- รุ่นเสถียรล่าสุดคือรุ่น [2.0.4](https://github.com/PyThaiNLP/pythainlp/tree/master) - PyThaiNLP 2 รองรับ Python 3.6 ขึ้นไป - ผู้ใช้ Python 2.7+ ยังสามารถใช้ PyThaiNLP 1.6 ได้ diff --git a/bin/pythainlp b/bin/pythainlp index 71d2a1686..d49f9fd3d 100644 --- a/bin/pythainlp +++ b/bin/pythainlp @@ -1,7 +1,7 @@ #!python3 # -*- coding: utf-8 -*- -_VERSION = "2.0.3" +_VERSION = "2.0.4" import argparse diff --git a/conda.recipe/meta.yaml b/conda.recipe/meta.yaml index 7d493cd50..71dd45e27 100644 --- a/conda.recipe/meta.yaml +++ b/conda.recipe/meta.yaml @@ -1,4 +1,4 @@ -{% set version = "2.0.3" %} +{% set version = "2.0.4" %} package: name: pythainlp diff --git a/meta.yaml b/meta.yaml index 78d7a4794..52ebb1d37 100644 --- a/meta.yaml +++ b/meta.yaml @@ -1,4 +1,4 @@ -{% set version = "2.0.3" %} +{% set version = "2.0.4" %} package: name: pythainlp diff --git a/pythainlp/__init__.py b/pythainlp/__init__.py index 6ad04250e..c316fa4e9 100644 --- a/pythainlp/__init__.py +++ b/pythainlp/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -__version__ = "2.0.3" +__version__ = "2.0.4" thai_consonants = "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรลวศษสหฬอฮ" # 44 chars thai_vowels = "ฤฦะ\u0e31าำ\u0e34\u0e35\u0e36\u0e37\u0e38\u0e39เแโใไ\u0e45\u0e47" # 19 diff --git a/setup.cfg b/setup.cfg index 785b90fc3..4a4459102 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 2.0.3 +current_version = 2.0.4 commit = True tag = True diff --git a/setup.py b/setup.py index a6678cb86..7449ada24 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ setup( name="pythainlp", - version="2.0.3", + version="2.0.4", description="Thai Natural Language Processing library", long_description=readme, long_description_content_type="text/markdown", diff --git a/tests/__init__.py b/tests/__init__.py index 4a37baed6..3e8782c3f 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -357,6 +357,7 @@ def test_word_tokenize_deepcut(self): def test_word_tokenize_longest(self): self.assertEqual(longest.segment(None), []) self.assertEqual(longest.segment(""), []) + self.assertIsNotNone(longest.segment("กรุงเทพฯมากๆเพราโพาง BKKฯ")) self.assertEqual( word_tokenize("ฉันรักภาษาไทยเพราะฉันเป็นคนไทย", engine="longest"), ["ฉัน", "รัก", "ภาษาไทย", "เพราะ", "ฉัน", "เป็น", "คนไทย"],