# cn_sort
按拼音和笔顺精确、快速排序大量简体中文词组(支持百万数量级,简体中文与非中文混用的词组也可),有效解决多音字混排的问题。
# 依赖
运行python版本:
+ 3.6+
本项目涉及以下依赖:
+ jieba
+ pypinyin
# 安装
pip安装命令:
```shell
pip install cn_sort --upgrade
```
如果提示缺少依赖,运行以下命令:
```shell
pip install -r requirements.txt
```
# 使用
## 入门
基本用法如下:
```python
from cn_sort.process_cn_word import *
if __name__ == "__main__":
# 先按拼音,再按笔顺排序
text_list = ["重心", "河水", "重庆", "河流", "WTO世贸组织"] # 待排序的中文词组列表
result_text_list = list(sort_text_list(text_list, mode=Mode.pinyin)) # mode=Mode.pinyin可以不写
print(result_text_list)
# 输出:
# ['WTO世贸组织', '重庆', '河流', '河水', '重心']
# 只按笔顺排序
text_list = ["二", "重要", "三", "一二", "一", "22", "1", "a", "重庆"] # 待排序的中文词组列表
a = list(sort_text_list(mode=Mode.bihua))
print(a)
# 输出:
# ['1', '22', 'a', '一', '一二', '二', '三', '重庆', '重要']
```
## 设置输出日志级别
```python
from cn_sort.process_cn_word import *
if __name__ == "__main__":
set_stdout_level("CRITICAL") # 日志级别:DEBUG、INFO、WARN、ERROR、CRITIAL
```
# 构思
cn_sort库的核心思想是基数排序,思想类似这篇提到的对英文单词的排序: [Java 实现 单词排序———基数排序](https://www.jianshu.com/p/3331930a90bf) 。
cn_sort库排序分两种模式:
* 模式一(默认):对词从左往右逐字先对比拼音排序,当拼音相同时再对比笔顺的排序方式,如`["唯依","唯衣","唯一","啊"]`排序为`["啊","唯一", "唯衣", "唯依"]`。
* 模式二:对词从左往右逐字对比笔顺排序,如`["三","二","一","一二"]`排序为`["一","一二", "二", "三"]`。
cn_sort库分为词组量少(排序方式一)与词组量多(排序方式二)的排序方式,这两种方式默认的词组量阈值为1000000。模式一分排序方式一与排序方式二,模式二只采用排序方式一。
核心算法位于./cn_sort/process_cn_word.py。
## 排序方式一
cn_sort库当词组量少时,包含最基本的排序功能。
对于模式一的基本思想如下图:
<div align="center">
<img src="https://github.com/bmxbmx3/cn_sort/blob/master/readme_pic/%E8%AF%8D%E7%BB%84%E9%87%8F%E5%B0%91.png" width="60%"/>
<br>词组量少时的排序思想</br>
<br></br>
</div>
①每个词用pypinyin转化为拼音二维组。
②各字按其拼音和笔顺查表,转化为优先级二维组(列数以所有词的最大长度为界,短于该长度的词末尾补0)
③从最低位往最高位,对优先级二维数组纵向采用python的tim排序,横向采用基数排序。
④查表,将优先级二维数组恢复为排序好的词组。
对于模式二,只需把词组拆成字,再按笔顺转换成对应的优先级二维组即可,原理同上。
## 排序方式二
cn_sort库当词组量多时,采用多进程提高运算速度。先将词组量按cpu数-1来分段,用cpu数-1个生产者进程处理这分段后的词组量,为了提高运行速度,采用jieba分割并过滤出重复的词元,最后将这些词元放于1个消费者进程中,按词组量少时的情况排序,最后再按每个词间的'\n'
定位标志,重新恢复成排序好的词组。大体构思如下:
<div align="center">
<img src="https://github.com/bmxbmx3/cn_sort/blob/master/readme_pic/%E8%AF%8D%E7%BB%84%E9%87%8F%E5%A4%9A.png" width="60%"/>
<br>词组量多时的排序思想</br>
<br></br>
</div>
为了便于对多进程处理中文排序的理解,我这里写了一个demo:
```python
from multiprocessing import *
from multiprocessing.pool import Pool
from openpyxl import Workbook
from time import *
# 这里替换为生产者与消费者进程,包括中文排序
def write_to_excel(num_split):
index = num_split[0] # 第index个进程
num_list = num_split[1] # 顺序分割后的数据量
# 写入文件
wb = Workbook()
ws = wb.create_sheet("newSheet")
for i in range(len(num_list)):
ws.cell(row=i + 1, column=1).value = num_list[i]
wb.save('test' + str(index) + '.xlsx')
print("正在生成第" + str(index + 1) + "个文件")
if __name__ == "__main__":
# 计算程序运行时间
start_time = time()
# 原始数据
num = list(range(1, 10000000 + 1))
# 分割数据量
n = 100
num_split = []
quotient, remainder = divmod(len(num), n)
for i in range(n):
first_index = i * quotient
end_index = (i + 1) * quotient if i < n - 1 else None
temp = num[first_index:end_index]
num_split.append([i, temp])
# 多进程处理数据(耗尽cpu算力)
cpu_n = cpu_count() # 获取cpu数,控制进程量
pool = Pool(cpu_n)
for i in range(n):
pool.apply_async(func=write_to_excel, args=(num_split[i],))
pool.close()
pool.join()
end_time = time()
print("耗时" + str(end_time - start_time))
```
在这个demo基础上,cn_sort库增添了生产者与消费者进程,以及进程间的通信。
当多进程任务切换频繁出现错误时,应将程序入口放在`if __name__=="__main__":`下执行,除此以外,sort_text_list函数的freeze参数应设置为True:
```python
if __name__ == "__main__":
set_stdout_level("INFO")
sort_text_list(["992", "3.", "2.", "重庆", "人民", "Awsl"] * 1000000, freeze=True)
# 一百万词组量排序输出
# 2021-03-02 16:36:45,611 - all - INFO - get_text_spit_list函数运行时间为0.182560s
# 2021-03-02 16:36:48,230 - all - INFO - get_word_dict函数运行时间为0.222509s
# 2021-03-02 16:37:00,198 - all - INFO - 分词进程1已切割8个不重复的词
# 2021-03-02 16:37:00,314 - all - INFO - 分词进程9已切割8个不重复的词
# 2021-03-02 16:37:00,327 - all - INFO - 分词进程5已切割8个不重复的词
# 2021-03-02 16:37:00,389 - all - INFO - 分词进程3已切割8个不重复的词
# 2021-03-02 16:37:00,390 - all - INFO - 分词进程10已切割8个不重复的词
# 2021-03-02 16:37:00,397 - all - INFO - 分词进程2已切割8个不重复的词
# 2021-03-02 16:37:00,417 - all - INFO - 分词进程4已切割8个不重复的词
# 2021-03-02 16:37:00,490 - all - INFO - 分词进程11已切割8个不重复的词
# 2021-03-02 16:37:00,493 - all - INFO - 分词进程6已切割8个不重复的词
# 2021-03-02 16:37:00,558 - all - INFO - 分词进程7已切割8个不重复的词
# 2021-03-02 16:37:00,598 - all - INFO - 分词进程8已切割8个不重复的词
# 2021-03-02 16:37:00,598 - all - INFO - 分词总结果为7个不重复的词
# 2021-03-02 16:37:02,306 - all - INFO - multiprocess_split_text_list函数运行时间为16.693209s
# 2021-03-02 16:37:02,308 - all - INFO - hadle_seged_text_word函数运行时间为0.000000s
# 2021-03-02 16:37:02,448 - all - INFO - sort_text_list函数运行时间为18.033616s
```
## 设置词组量阈值
如果你要更改使用排序方式一与排序方式二之间的词组量阈值,可对sort_text_list函数的threshold参数进行调整:
```python
if __name__ == "__main__":
set_stdout_level("INFO")
sort_text_list(["992", "人民", "Awsl"] * 100000, freeze=True, threshold=5000)
# 所处理的词组量为3*100000=300000,大于阈值5000,此时cn_sort采用排序方式二
```
## 注意
pypinyin会将一个字的不同声调标注为这种形式:�