juba 0.2.1
Juba Chinese Natural Language Processing

# juba

**juba自然语言处理工具包由李汉巨研究员开发和维护。旧版本juba0.12使用文档参见[Python第三方库juba中文文本处理详细使用方法](https://www.toutiao.com/a6561576442730119693/)。**

juba2.0新功能:
- 时间序列拟合
- 中文命名实体识别（机构、地点、人名、电话、时间）

本项目属于MIT许可。

## 1.依赖

- python (>=2.7)
- jieba (>=0.39)
- numpy (>=1.17.3)

## 2.快速开始

juba主要功能有文档词汇矩阵、词汇文档矩阵、文档相似、词汇关联分析、文档生成、时间序列拟合、时间序列周期性特征提取、中文命名实体识别。通过命令pip install juba安装。

## 3.词向量和文档向量
首先从juba导入Similar类。
```
>>> from juba import Similar
```
其次实例化一个Similar类，并接受中文文本矩阵docs作为参数。
docs是二维矩阵，是Similar类接收的数据结构，比如考虑如下三个文档：
- 文档1：我喜欢你
- 文档2：我讨厌你
- 文档3：狗喜欢吃骨头

这时可以用jieba进行分词：
- 文档1：我喜欢你>>> ['我','喜欢','你']
- 文档2：我讨厌你>>> ['我','讨厌','你']
- 文档3：狗喜欢吃骨头>>> ['狗','喜欢','吃','骨头']

docs就是把上面三个列表按照顺序排起来（相当于列表的列表），形成中文文本矩阵，即
```
>>> docs = [['我','喜欢','你'],['我','讨厌','你'],['狗','喜欢','吃','骨头']]
```
接着实例化一个Similar类了，即
```
>>> S = Similar(docs)
```
下面就可以引用Similar类的方法实现各种本文处理功能了。

### 3.1词汇表（vocabularyList）
词汇表是docs出现过的词汇形成的列表，引用方法是S.vocabularyList。
```
>>> S.vocabularyList
['我', '喜欢', '你', '讨厌', '狗', '吃', '骨头']
```
### 3.2词汇表字典（vocabulary）
词汇表字典是docs共有词汇表字典，存储每个词出现总次数，引用方法是S.vocabulary。

```
>>> S.vocabulary
{'我': 2, '喜欢': 2, '你': 2, '讨厌': 1, '狗': 1, '吃': 1, '骨头': 1}
```
### 3.3文档词汇矩阵（document term matrix，dtm）
文档词汇矩阵按照词汇表vocabularyList的词汇顺序将每个文档表示为向量（doc2vec），有三种统计模式：
- tf_tdm()：词频模式
- prob_tdm ()：概率模式
- tfidf_tdm ()：词频逆文档频率模式

下面以tf_dtm()模式为例，引用方法是S.tf_dtm()。

```
>>> S.tf_dtm()
[[1, 1, 1, 0, 0, 0, 0], [1, 0, 1, 1, 0, 0, 0], [0, 1, 0, 0, 1, 1, 1]]
```
即对应以下的矩阵：

 dtm | 我 | 喜欢 | 你 | 讨厌 | 狗 | 吃 | 骨头
---- | ---- | ---- | ---- | ---- | ---- | ----| ----
文档1| 1| 1| 1| 0| 0| 0| 0
文档2|1| 0| 1| 1| 0| 0|0
文档3|0|1|0| 0|1|1|1

即用向量[1, 1, 1, 0, 0, 0, 0]表示文档1，以此类推。

tfidf_dtm()模式基于Tf-idf算法实现。tfidf_dtm()有一个参数norm，其中norm=True对向量进行L2模标准化，norm=False是默认情形，不对向量做任何处理。

```
>>> S.tfidf_dtm()
[[0.2876820724517808, 0.2876820724517808, 0.2876820724517808, 0, 0, 0, 0], [0.2876820724517808, 0, 0.2876820724517808, 0.6931471805599453, 0, 0, 0], [0, 0.2876820724517808, 0, 0, 0.6931471805599453, 0.6931471805599453, 0.6931471805599453]]
>>> S.tfidf_dtm(norm=True)
[[0.5773502691896257, 0.5773502691896257, 0.5773502691896257, 0.0, 0.0, 0.0, 0.0], [0.35793564988393073, 0.0, 0.35793564988393073, 0.8624176140851578, 0.0, 0.0, 0.0], [0.0, 0.23302537487517572, 0.0, 0.0, 0.5614561943922499, 0.5614561943922499, 0.5614561943922499]]
```
有了文档词汇矩阵，就可以对文档进行高级文本分析，如文档相似、文档分类、情感分析、文档聚类等，与通常的机器学习没有什么区别。

### 3.4词汇文档矩阵（term document matrix，tdm）
词汇文档矩阵可以看成文档词汇矩阵的转置，不过是以字典结构给出，即按照文档顺序将每个词汇表示为向量（word2vec)。同样有三种模式。
- tf_tdm()：词频模式
- prob_tdm ()：概率模式
- tfidf_tdm ()：词频逆文档频率模式

下面以tf_tdm()模式为例，引用方法是S.tf_tdm()。

```
>>> S.tf_tdm()
{'我': [1, 1, 0], '喜欢': [1, 0, 1], '你': [1, 1, 0], '讨厌': [0, 1, 0], '狗': [0, 0, 1], '吃': [0, 0, 1], '骨头': [0, 0, 1]}
>>> S.tf_tdm()['狗']
[0, 0, 1]
```
即用向量[0,0,1]表示'狗'，说明词汇'狗'在文档1中出现次数为0，在文档2中出现次数为0，在文档3中出现次数为1。以此类推。有了词汇文档矩阵，可以对词汇进行关联分析或词汇聚类分析等。

### 3.5文档相似
文档相似是信息检索的基础，应用于智能问答、推荐系统。juba提供四种文档相似度计算方法，都是用于计算第一个文档与其余文档的相似度：
- cosine_sim(dtm='tfidf_dtm')：余弦相似度，值越大，越相似，默认参数dtm='tfidf_dtm'，即默认使用tfidf_dtm矩阵计算相似度，还有其他选项dtm='prob_dtm'和dtm='tf_dtm'。

```
>>> S.cosine_sim(dtm='tfidf_dtm')
[0.1655219496203033, 0.08276097481015166]
```
- weight_jaccard_sim()：权重jaccard相似度。
- jaccard_sim()：jaccard相似度。
- bm25_sim()：bm25相似度。

### 3.6词汇关联分析

利用词汇文档矩阵（tdm）进行词汇关联分析，有两种模式：
- two_term_assocs(word_one,word_two,tdm='tf_tdm',norm=False):计算两个词汇的相关系数，参数tdm确定使用何种词汇文档矩阵计算，包含'tf_tdm'、'tfidf_tdm'和'prob_tdm'，参数norm只对tdm='tfidf_tdm'起作用。
```
>>> S.two_term_assocs('讨厌','喜欢',tdm='tfidf_tdm',norm=False)
-0.9999999999999998
>>> S.two_term_assocs('讨厌','喜欢',tdm='tfidf_tdm',norm=True)
-0.8054025027750852
```
- find_assocs(word,mu=0,tdm='tf_tdm',norm=False)：找出与word的相关系数的绝对值不少于mu的所有词汇，参数tdm确定使用何种词汇文档矩阵计算，包含'tf_tdm'、'tfidf_tdm'和'prob_tdm'，参数norm只对tdm='tfidf_tdm'起作用。
```
>>> S.find_assocs('我')
[['我', 1.0000000000000002], ['喜欢', -0.5000000000000001], ['你', 1.0000000000000002], ['讨厌', 0.5000000000000001], ['狗', -1.0000000000000002], ['吃', -1.0000000000000002], ['骨头', -1.0000000000000002]]
>>> S.find_assocs('狗',0.2,tdm='tfidf_tdm',norm=True)
[['我', -0.9264398530750386], ['你', -0.9264398530750386], ['讨厌', -0.4999999999999999], ['狗', 1.0], ['吃', 1.0], ['骨头', 1.0]]
>>> S.find_assocs('狗',0.4,tdm='prob_tdm')
[['我', -1.0000000000000002], ['喜欢', 0.5000000000000001], ['你', -1.0000000000000002], ['讨厌', -0.5000000000000001], ['狗', 1.0000000000000002], ['吃', 1.0000000000000002], ['骨头', 1.0000000000000002]]
```
## 4.文本生成
Markov类用于自动文档生成，当然需要输入原始文章，甚至可以输入不用风格文章，就可以生成混合风格文章。自动文档生成的原理是基于马尔科夫（Markov）随机模型，读者可以搜索马尔科夫模型相关知识。使用方法很简单。

（1）准备原始文章，我这里使用了一首诗（在春天，http://www.zgshige.com/c/2018-04-23/6012125.shtml），需要注意的是尽量保持诗歌的结构，段落与段落之间要保持空格。
```
>>> text='月光朦胧下 夜并未脱光衣服 梦却倒在卧榻上赤裸裸的等待着 清亮的春姑娘 风在窗外吼着 我想既然已是春天 你又何必学着隆冬的狼嚎虫声新透 窗纱并没有绿 况且窗外还有那么多不洁之物在 萌发 我的花儿还在藏着掖着 人言可畏呀 你这大地上失贞的玫瑰 被那急于表现自己的迎春花所代替早春陷在残雪的泥淖里 蓝天却洗净了足迹 我要带着一条鱼去旅游 要经过蓝天的驿站 当然还要带着 美酒 以及不曾消弭的冰冷而又飘忽的 白云我可敬的绿植翻卷着波涛般的梦幻 如细沙堆起的海市蜃楼 如深陷泥潭的孤岛 如一朵蘑菇在花园里悄悄盛开'
```

（2）使用jieba进行分词。


```
>>> import jieba
>>> text = jieba.lcut(text)
```

（3）实例化一个Markov类，并传入上述的test。

```
>>> from juba import Markov
>>> M = Markov(text)
```
（4）引用Markov类的方法random_text(textlength,firstWord) ，其中textlength是生成文档的词汇数量，firstWord是生成文档的第一个词汇。

```
>>> M.random_text(200,'我')
'我的春姑娘 我要带着 窗纱并没有绿 白云我可敬的海市蜃楼 蓝天却倒在 窗纱并没有绿 你又何必学着隆冬的驿站 况且窗外吼着 风在窗外吼着波涛般的梦幻 当然还要带着 萌发 我的等待着波涛般的梦幻 人言可畏呀 你这大地上失贞的冰冷而又飘忽的驿站 况且窗外还有那么多不洁之物在卧榻上赤裸裸的等待着 我想既然已是春天 如一朵蘑菇在藏着波涛般的梦幻 我要带着 况且窗外还有那么多不洁之物在 清亮的春姑娘 要经过蓝天却洗净了足迹 清亮的泥淖里 况且窗外还有那么多不洁之物在卧榻上赤裸裸的狼嚎虫声新透 萌发 我要带着 美酒 美酒 白云我的玫瑰 如深陷泥潭的冰冷而又飘忽的狼嚎虫声新透 夜并未脱光衣服 被那急于表现自己的玫瑰 我......'
```
## 5.时间序列拟合

ForwordFitting类用于时间序列拟合，适合有周期性特征的序列。具体算法参考[(Li 2017)](https://www.itm-conferences.org/articles/itmconf/pdf/2017/04/itmconf_ita2017_04013.pdf)。

（1）数据结构。处理类似下面的一维序列。

```
>>> data = [47.06,36.17,32.31,44.61,46.1,48.07,52.13,56.05,59.34,50.88,48.76,45.21,45.02,29.76,39.81,46.19,47.09,53.56,54.32,58.16,61.04,53.85,50.23,42.14,51.01,44.25,26.21,46.67,47.67,58.59,63.33,62.87,57.99,53.09,50.46,50.6,39.8,34.12,51.2,50.95,58.2,63.31,70.87,68.62,64.08,56.73,51.17,51.92,50.66]
```
（2）构造模型。F=ForwordFitting(periodic_factor=2,complexity=10)，其中periodic_factor为周期因子，为整数，表示一个周期含有len(data)+periodic_factor个数据，即一个周期的序列长度；
complexity为复杂度，越大拟合得越好，但会过拟合。实际应用中，需要调整这两个参数。

```
>>> from juba import ForwordFitting
>>> F = ForwordFitting(periodic_factor=len(data),complexity=30)
```
（3）训练模型。
```
>>> F.fit(sequence=data)
```
（4）预测。predict(pred_step=5)，默认预测后五步。
```
>>> F.predict(pred_step=5)
[52.74823520112094, 53.32119483100382, 54.99967821187696, 55.46596488115129, 55.67936429877518]
>>> F.fit_original_data() #原始数据拟合值
[41.56868404546823, 34.382563807114764, 36.35108455837123, 47.17369024332727, 52.71180306759437, 50.77977144640642, 54.55445292293315, 62.72581129706526, 59.705548373813365, 49.18745974511483, 46.7121301149, 46.097158296577334, 36.17938246898176, 29.354903198825646, 36.50265283989673, 46.251185262505984, 50.446660952331555, 53.89526797868876, 57.75736729025388, 61.29511360260004, 63.68341152459449, 57.344061358251466, 45.31889990467366, 44.39249050019477, 49.09071179700192, 38.97435879647022, 26.24480822419798, 34.772408854211534, 51.78983928225305, 58.57797550848757, 63.910872200966836, 68.9178254165771, 61.16034915201646, 51.598176572790074, 53.64559122791665, 50.71138376600812, 35.43342276801361, 30.529677301439182, 41.73296184626696, 49.79786703816569, 52.60360876815621, 61.36100635606929, 71.36764854348468, 73.23810844719983, 68.90561639101554, 62.15292348480011, 55.379932908352714, 52.50401795003867, 52.79169141846165]
>>> F.interpolate(1) #计算插值
41.56868404546823
>>> F.interpolate(1.5)
37.553210700706565
>>> F.interpolate(2)
34.382563807114764
```
（5）其它属性。F.best_complexity,在complexity内的最佳复杂度，实际应用中，先取比较大的complexity，求最佳的best_complexity，最后利用best_complexity重新建模，以防过拟合；
F.intercept，函数截距；F.cos_coef，余弦分量系数；F.sin_coef，正弦分量系数。

```
>>> F.best_complexity
30
>>> F.intercept
50.453673469387745
>>> F.cos_coef
[-5.208036331159572, 0.7259410786845892, -1.3346965861766538, 0.22914782820478302, -0.8089892609376188, -0.3721798432855622, -2.4626969830908885, -5.140349419792267, 4.047718644998989, -0.0158128433141528, 0.5422844872778734, 0.7248744687090481, 0.4560598864939642, -0.3040049872854211, -0.12733906651254945, 0.922006507842111, 0.13998455452133707, -0.1569492428042904, 0.9006687456579556, 0.6624998733327541, -0.9146087648701946, 0.5890225526354206, 0.8943754891354844, 1.2291896299728986, 0.5732271373727359, -0.5322146367495807, 0.8475291666306735, -0.05774144043048804, -1.0201985176741697, 0.3598710250214147]
>>> F.sin_coef
[-0.34439382863432016, 0.898170734388574, 0.662469071132231, -0.12120613942858569, 1.5097576305142832, -0.11403045538900328, 3.2297656387698535, -4.609276647728478, -2.5579474546983696, 1.1678182849006928, -1.2836600737936843, 0.3290158108609084, -0.044414691870264734, 0.44527105431475855, -1.0153813099397622, -0.08906989925962419, 0.25439154786149565, -0.355219907225197, -0.6241503285900435, 1.0031206697620907, -0.1780614731701408, -1.0168008658092058, -0.10215390374778463, 0.10958009723508969, 1.343093478469613, 0.12564234271936384, 0.04536749014629629, 1.3400435891661528, -0.17704973649088906, -0.5971344484074085]
```
因此f(x) = F.intercept + sum([F.cos_coef[j]*cos(2*pi*j*x)+ F.sin_coef[j]*sin(2*pi*j*x) for j in range(1,complexity+1,1)])。

## 6.命名实体识别
调用聚吧数据（www.pynlp.net）的公开版接口（需连接互联网），限制200个字符，不限字数调用服务需联系微信13266190077。
可以识别以下实体：
- 机构名称，以【ORG：实体】标记
- 地址名称，以【LOC：实体】标记
- 中国人名，以【PER：实体】标记
- 电话传真，以【TEL：实体】标记
- 时间日期，以【TIME：实体】标记

```
>>> from juba import Ner
>>> text='9月22日，2019中国人工智能大会期间，腾讯AI Lab主任张正友专程到访少海汇，与少海汇合伙人李中攀就人工智能场景化应用与拓展等方面展开交流，双方就智能家居、机器人领域达成初步合作意向，对未来AI智能场景应用及市场需求达成研发合作意向。'
>>> Ner(text)
【TIME：9月22日】，【TIME：2019】中国人工智能大会期间，【ORG：腾讯AI Lab】主任【PER：张正友】专程到访【ORG：少海汇】，与【ORG：少海汇】合伙人【PER：李中攀】就人工智能场景化应用与拓展等方面展开交流，双方就智能家居、机器人领域达成初步合作意向，对未来AI智能场景应用及市场需求达成研发合作意向。
```
可以识别长地址：
```
>>> text='六、摇珠时间、地点：1、时间：2019年10月17日15：00时。2、地点：广州市南沙区大岗镇规划国土建设办二楼209室（广州市南沙区大岗镇兴业路39号）七、广州市南沙区大岗镇小额以下建设项目企业库（市政工程施工库）名单： 序号 单位名称 序号 单位名称 1 广州市芳村市政园林建设工程有限公司 14 广东中林建筑园林工程有限公司 2 广州广荔市政建设有限公司 15 广东悦丰建筑工程有限公司'
>>> Ner(text)
六、摇珠时间、地点：1、时间：【TIME：2019年10月17日15：00时】。2、地点：【LOC：广州市南沙区大岗镇规划国土建设办二楼209室（广州市南沙区大岗镇兴业路39号）】七、【LOC：广州市南沙区大岗镇】小额以下建设项目企业库（市政工程施工库）名单： 序号 单位名称 序号 单位名称 1 【ORG：广州市芳村市政园林建设工程有限公司】 14 【ORG：广东中林建筑园林工程有限公司】 2 【ORG：广州广荔市政建设有限公司】 15 【ORG：广东悦丰建筑工程有限公司】
```
可以识别长时间：
```
>>> text='竞投人可于2019-12-05至2019-12-11上午9：00至11：30，下午2：30至4：30（节假日除外），携带相关资料到新沙股份合作经济联合社或大岗镇政府006室镇财所农村组（大岗镇豪岗大道13号）进行报名或咨询。 '
>>> Ner(text)
竞投人可于【TIME：2019-12-05至2019-12-11上午9：00至11：30，下午2：30至4：30（节假日除外）】，携带相关资料到【ORG：新沙股份合作经济联合社】或【LOC：大岗镇政府006室镇财所农村组（大岗镇豪岗大道13号）】进行报名或咨询。 

```
## 7.联系我们
聚吧数据实验室  李研究员  13266190077（微信）  99959858@qq.com

聚吧数据实验室  www.pynlp.net