Skip to content

Commit deccd9e

Browse files
committed
update parse_money & extract_money
1 parent 590a701 commit deccd9e

18 files changed

+953
-313
lines changed

README.md

+40-12
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<a alt="Downloads">
1212
<img src="https://img.shields.io/badge/downloads-5k-yellow" /></a>
1313
<a alt="Version">
14-
<img src="https://img.shields.io/badge/version-1.3.37-green" /></a>
14+
<img src="https://img.shields.io/badge/version-1.3.38-green" /></a>
1515
<a href="https://github.com/dongrixinyu/JioNLP/pulse" alt="Activity">
1616
<img src="https://img.shields.io/github/commit-activity/m/dongrixinyu/JioNLP?color=blue" /></a>
1717
</p>
@@ -29,24 +29,51 @@
2929
#### 功能主要包括:文本清洗,删除HTML标签、删除异常字符、删除冗余字符,转换全角字母、数字、空格为半角,抽取及删除E-mail及域名、抽取及删除(手机号、座机号)电话号码、抽取及删除QQ号、抽取及删除括号内容、抽取及删除身份证号、抽取及删除IP地址、抽取及删除URL超链接、抽取及删除货币金额与单位,金额数字转大写汉字,时间语义解析,解析身份证号信息、解析手机号码归属地、解析座机区号归属地、解析手机号码运营商,按行快速读写文件,(多功能)停用词过滤,(优化的)分句,地址解析,新闻地域识别,繁简体转换,汉字转拼音,汉字偏旁、字形、四角编码、五笔编码拆解,基于词典的情感分析,色情数据过滤,反动数据过滤,关键短语抽取,抽取式文本摘要,成语接龙,成语词典、歇后语词典、新华字典、新华词典、停用词典、中国地名词典、中国县级地名变更词典、世界地名词典,时间实体抽取,基于词典的NER,NER的字、词级别转换,NER的entity和tag格式转换,NER模型的预测阶段加速并行工具集,NER标注和模型预测的结果差异对比,NER标注数据集分割与统计,NER实体收集、文本分类标注数据集的分割与统计、回译数据增强、相邻近汉字换位数据增强、同音词替换数据增强、随机增删字符数据增强、实体替换数据增强、公历转农历日期、农历转公历日期
3030

3131

32-
#### Update 2021-09-20
33-
## 新增 [时间实体抽取](../../wiki/NER-说明文档#user-content-时间实体抽取)
34-
35-
#### jio.ner.extract_time 从文本中抽取时间实体(不依赖模型,纯规则)。
32+
#### Update 2021-10-25
33+
## 更新 [货币金额解析](../../wiki/NER-说明文档#user-content-货币金额实体抽取)
3634

35+
#### jio.ner.extract_money 从文本中抽取货币金额实体(不依赖模型,纯规则)。
36+
#### 配合 jio.parse_time 使用(见下)
3737
``` python
38-
import time
3938
import jionlp as jio
40-
text = '【新华社报2021-9-9】国家统计局今天发布了2021年8月份全国CPI(居民消费价格指数)和PPI(工业生产者出厂价格指数)数据'
41-
res = jio.ner.extract_time(text, time_base={'year': 2021})
39+
text = '张三赔偿李大花人民币车费601,293.11元,工厂费一万二千三百四十五元,利息9佰日元,打印费十块钱'
40+
res = jio.ner.extract_money(text, with_parsing=False)
4241
print(res)
4342

44-
# [{'text': '2021-9-9', 'offset': [5, 13], 'type': 'time_point'},
45-
# {'text': '今天', 'offset': [19, 21], 'type': 'time_point'},
46-
# {'text': '2021年8月份', 'offset': [24, 32], 'type': 'time_point'}]
43+
# [{'text': '601,293.11元', 'offset': [12, 23], 'type': 'money'},
44+
# {'text': '一万二千三百四十五元', 'offset': [27, 37], 'type': 'money'},
45+
# {'text': '9佰日元', 'offset': [40, 44], 'type': 'money'},
46+
# {'text': '十块钱', 'offset': [48, 51], 'type': 'money'}]
47+
48+
```
49+
50+
#### Update 2021-10-25
51+
## 更新 [货币金额解析](../../wiki/时间语义解析-说明文档#user-content-时间语义解析)
52+
53+
#### jio.parse_time 给定时间字符串,解析其为时间戳、时长等。
54+
55+
解析货币金额字符串,并将其转换为标准数字格式。
56+
57+
```python
58+
import jionlp as jio
59+
text_list = ['约4.287亿美元', '两个亿卢布', '六十四万零一百四十三元一角七分', '3000多欧元']
60+
moneys = [jio.parse_money(text) for text in text_list]
61+
62+
# 约4.287亿美元: {'num': '428700000.00', 'case': '美元', 'definition': 'blur'}
63+
# 两个亿卢布: {'num': '200000000.00', 'case': '卢布', 'definition': 'accurate'}
64+
# 六十四万零一百四十三元一角七分: {'num': '640143.17', 'case': '元', 'definition': 'accurate'}
65+
# 3000多欧元: {'num': ['3000.00', '4000.00'], 'case': '欧元', 'definition': 'blur'}
4766

4867
```
4968

69+
- 支持纯数字格式,如:987273.3美元
70+
- 支持大写中文金额,如:柒仟六佰零弎萬肆仟叁佰贰拾壹元伍分
71+
- 支持混合格式,如:1.26万港元
72+
- 支持**修饰词**解析,如:将近6万块钱、至少1000块钱以上
73+
- 支持**模糊金额**解析,如:两万多元钱,6千多亿日元
74+
- 支持**口语化中文**格式,如:三十五块三毛;但对于“三十五块八”这样的字符串,在文本中存在**歧义**,如“三十五块八颗糖”等,因此,```jio.ner.extract_money``` 对于此字符串不予抽取,但```parse_money```可以将“三十五块八”看作完整的口语化金额,标准化为“35.80元”
75+
- 支持多种常见货币类型:人民币,港元,澳门元,美元,日元,澳元,韩元,卢布,英镑,马克,法郎,欧元,加元, 泰铢等。
76+
5077
#### Update 2021-10-11
5178
## 新增 [时间语义解析](../../wiki/时间语义解析-说明文档#user-content-时间语义解析)
5279

@@ -160,7 +187,7 @@ $ jio_help
160187
|--------|--------|-------|-------|
161188
|[**清洗文本**](../../wiki/正则抽取与解析-说明文档#user-content-清洗文本) |clean_text|去除文本中的**异常字符、冗余字符、HTML标签、括号信息、**<br>**URL、E-mail、电话号码,全角字母数字转换为半角** ||
162189
|[抽取 **E-mail**](../../wiki/正则抽取与解析-说明文档#user-content-抽取-e-mail) |extract_email|抽取文本中的 E-mail,返回**位置****域名** | |
163-
|[抽取 **金额**](../../wiki/正则抽取与解析-说明文档#user-content-抽取金额字符串) |extract_money<br>money_standardization|抽取文本中的金额,并将其以**数字 + 单位**标准形式输出 ||
190+
|[解析 **货币金额**](../../wiki/正则抽取与解析-说明文档#user-content-货币金额解析) |extract_money|解析货币金额字符串 ||
164191
|[抽取**电话号码**](../../wiki/正则抽取与解析-说明文档#user-content-抽取电话号码) |extract_phone_number| 抽取电话号码(含**手机号****座机号**),返回**域名****类型****位置** | |
165192
|[抽取中国**身份证** ID](../../wiki/正则抽取与解析-说明文档#user-content-抽取身份证号) |extract_id_card|抽取身份证 ID,配合 **jio.parse_id_card** 返回身份证的<br>详细信息(**省市县****出生日期****性别****校验码**)| |
166193
|[抽取 **QQ**](../../wiki/正则抽取与解析-说明文档#user-content-抽取-qq) |extract_qq|抽取 QQ 号,分为严格规则和宽松规则 | |
@@ -204,6 +231,7 @@ $ jio_help
204231

205232
| 功能 | 函数 |描述 |星级 |
206233
|--------|--------|-------|-------|
234+
|[抽取**货币金额实体**](../../wiki/NER-说明文档#user-content-货币金额实体抽取) |extract_money |从文本中抽取出货币金额实体 ||
207235
|[抽取**时间实体**](../../wiki/NER-说明文档#user-content-时间实体抽取) |extract_time |从文本中抽取出时间实体 ||
208236
|[基于**词典NER**](../../wiki/NER-说明文档#user-content-基于词典-ner) |LexiconNER|依据指定的实体词典,前向最大匹配实体 ||
209237
|[**entity 转 tag**](../../wiki/NER-说明文档#user-content-entity-转-tag) |entity2tag|将 json 格式实体转换为模型处理的 tag 序列 | |

image/qr_code_for_collection.png

-14 KB
Loading

jionlp/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
# description: Preprocessing tool for Chinese NLP
99
"""
1010

11-
__version__ = '1.3.37'
11+
__version__ = '1.3.38'
1212

1313
import os
1414

jionlp/algorithm/ner/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
from .ner_entity_compare import entity_compare
1313
from .analyse_dataset import analyse_dataset, collect_dataset_entities
1414
from .time_extractor import TimeExtractor
15-
15+
from .money_extractor import MoneyExtractor
1616

1717
extract_time = TimeExtractor()
18+
extract_money = MoneyExtractor()
1819

+188
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
# -*- coding=utf-8 -*-
2+
# library: jionlp
3+
# author: dongrixinyu
4+
# license: Apache License 2.0
5+
6+
# github: https://github.com/dongrixinyu/JioNLP
7+
# description: Preprocessing tool for Chinese NLP
8+
9+
10+
import re
11+
12+
from jionlp.util.funcs import bracket, bracket_absence, absence
13+
from jionlp.rule.rule_pattern import MONEY_PREFIX_STRING, \
14+
MONEY_SUFFIX_STRING, MONEY_NUM_MIDDLE_STRING, MONEY_NUM_STRING, \
15+
MONEY_KUAI_MAO_JIAO_FEN_STRING, MONEY_PREFIX_CASE_STRING, MONEY_SUFFIX_CASE_STRING
16+
from jionlp.gadget.money_parser import MoneyParser
17+
18+
19+
class MoneyExtractor(object):
20+
""" 货币金额抽取器。不依赖模型,将文本中的货币金额进行抽取,并对其做金额解析。
21+
22+
Args:
23+
text(str): 输入待抽取货币金额的文本
24+
with_parsing(bool): 指示返回结果是否包含解析信息,默认为 True
25+
ret_all(bool): 某些货币金额表达,在大多数情况下并非表达货币金额,如 “几分” 之于 “他有几分不友善”,默认按绝大概率处理,
26+
即不返回此类伪货币金额表达,该参数默认为 False;若希望返回所有抽取到的货币金额表达,须将该参数置 True。
27+
28+
Returns:
29+
list(dict): 包含货币金额的列表,其中包括 text、type、offset 三个字段,和工具包中 NER 标准处理格式一致。
30+
31+
Example:
32+
>>> import jionlp as jio
33+
>>> text = '海航亏损7000万港元出售香港公寓。12月12日,据《香港经济日报》报道,' \
34+
'海航集团将持有的部分位于香港铜锣湾Yoo Residence大楼中的物业以2.6亿港元的价格出售'
35+
>>> res = jio.ner.extract_money(text, with_parsing=False)
36+
>>> print(res)
37+
38+
"""
39+
def __init__(self):
40+
self.parse_money = None
41+
42+
def _prepare(self):
43+
self.parse_money = MoneyParser()
44+
self.money_string_pattern = re.compile(
45+
''.join([absence(MONEY_PREFIX_STRING),
46+
absence(MONEY_PREFIX_CASE_STRING), '(', MONEY_NUM_STRING, '+',
47+
bracket(MONEY_NUM_MIDDLE_STRING + MONEY_NUM_STRING + '+'), '*',
48+
MONEY_SUFFIX_CASE_STRING, ')+',
49+
bracket_absence(MONEY_NUM_STRING),
50+
absence(MONEY_SUFFIX_STRING)]))
51+
52+
# 此类表达虽然可按货币金额解析,但是文本中很大概率并非表示货币金额,故以大概率进行排除,
53+
# 并设参数 ret_all,即返回所有进行控制,默认为 False,即根据词典进行删除
54+
# 删除性正则
55+
# - 单纯包含 分、角、块,而无其它格式货币的
56+
# - 特殊词汇如 “多元” 等
57+
self.money_kuai_map_jiao_fen_pattern = re.compile(MONEY_KUAI_MAO_JIAO_FEN_STRING)
58+
self.non_money_string_list = ['多元']
59+
60+
def __call__(self, text, with_parsing=True, ret_all=False):
61+
if self.parse_money is None:
62+
self._prepare()
63+
64+
candidates_list = self.extract_money_candidates(text)
65+
66+
money_entity_list = list()
67+
for candidate in candidates_list:
68+
offset = [0, 0]
69+
bias = 0
70+
while candidate['offset'][0] + offset[1] < candidate['offset'][1]:
71+
# 此循环意在找出同一个 candidate 中包含的多个 money_entity
72+
73+
true_string, result, offset = self.grid_search(
74+
candidate['money_candidate'][bias:])
75+
76+
if true_string is not None:
77+
78+
# rule 1: 判断字符串是否为大概率非货币金额语义
79+
if (true_string in self.non_money_string_list) and (not ret_all):
80+
bias += offset[1]
81+
continue
82+
83+
if with_parsing:
84+
money_entity_list.append(
85+
{'text': true_string,
86+
'offset': [candidate['offset'][0] + bias + offset[0],
87+
candidate['offset'][0] + bias + offset[1]],
88+
'type': 'money',
89+
'detail': result})
90+
else:
91+
money_entity_list.append(
92+
{'text': true_string,
93+
'offset': [candidate['offset'][0] + bias + offset[0],
94+
candidate['offset'][0] + bias + offset[1]],
95+
'type': 'money'})
96+
bias += offset[1]
97+
else:
98+
break
99+
100+
return money_entity_list
101+
102+
def grid_search(self, money_candidate):
103+
""" 全面搜索候选货币金额字符串,从长至短,较优 """
104+
length = len(money_candidate)
105+
for i in range(length): # 控制总长,若想控制单字符的串也被返回考察,此时改为 length + 1
106+
for j in range(i): # 控制偏移
107+
try:
108+
offset = [j, length - i + j + 1]
109+
sub_string = money_candidate[j: offset[1]]
110+
result = self.parse_money(sub_string)
111+
112+
return sub_string, result, offset
113+
except (ValueError, Exception):
114+
continue
115+
116+
return None, None, None
117+
118+
def _grid_search_2(self, money_candidate):
119+
""" 全面搜索候选货币金额字符串,从前至后,从长至短 """
120+
print(money_candidate)
121+
length = len(money_candidate)
122+
for i in range(length - 1): # 控制起始点
123+
for j in range(length, i, -1): # 控制终止点
124+
try:
125+
offset = [i, j]
126+
sub_string = money_candidate[i: j]
127+
print(sub_string)
128+
# 处理假阳性。检查子串,对某些产生歧义的内容进行过滤。
129+
# 原因在于,parse_money 会对某些不符合要求的字符串做正确解析.
130+
if not MoneyExtractor._filter(sub_string):
131+
continue
132+
133+
result = self.parse_money(sub_string, strict=True)
134+
135+
return sub_string, result, offset
136+
except (ValueError, Exception):
137+
continue
138+
139+
return None, None, None
140+
141+
def extract_money_candidates(self, text):
142+
""" 获取所有的候选货币金额字符串,其中包含了货币金额 """
143+
idx_count = 0
144+
text_length = len(text)
145+
money_candidates_list = list()
146+
while idx_count < text_length:
147+
matched_res = self.money_string_pattern.search(text[idx_count:])
148+
149+
if matched_res is not None:
150+
tmp_str = matched_res.group()
151+
if len(tmp_str) > 1:
152+
if len(''.join(self.money_kuai_map_jiao_fen_pattern.findall(tmp_str))) == 1 and (
153+
'元' not in tmp_str and '钱' not in tmp_str):
154+
# 仅有一个 `分毛角块` 字符且无 `元钱` 字符
155+
idx_count += matched_res.span()[1]
156+
continue
157+
158+
money_candidates_list.append(
159+
{'money_candidate': matched_res.group(),
160+
'offset': [idx_count + matched_res.span()[0],
161+
idx_count + matched_res.span()[1]],
162+
'context': text[max(0, idx_count - 5 + matched_res.span()[0]):
163+
min(text_length, idx_count + 5 + matched_res.span()[1])]}
164+
)
165+
idx_count += matched_res.span()[1]
166+
else:
167+
break
168+
169+
return money_candidates_list
170+
171+
172+
if __name__ == '__main__':
173+
text = '''海航亏损7000万港元出售香港公寓。12月12日,据《香港经济日报》报道,
174+
海航集团将持有的部分位于香港铜锣湾Yoo Residence大楼中的物业以2.6亿港元的价格出售,相对于去年入手时3.3亿港元的价格来看,
175+
海航此次出售该物业以公司股权转让的模式转售,亏损了7000多万港元。该物业包括一个顶层复式豪华公寓、1个分层物业及5个车位。
176+
报道称,两个月前,海航在市场上为该部分物业寻找买家,一度报价达到几千万美元。此外,海航在数月前将去年同时买下的一个地下连1楼的商铺
177+
以8650万港元的价格出售,买家为香港一家名为荣企的公司,较去年近1.2亿港元入手的价格亏损了约3350万港元。
178+
以此来看,海航投资Yoo Residence在一年内亏损逾1亿港元。今年以来,海航在香港连续出售其持有的地产类资产。
179+
2月份,海航集团把香港启德区6565号地块和6562号地块以159.59亿港元卖给了香港恒基兆业地产(00012.HK),股价为二十三块四毛钱。
180+
3月份,海航又把位于九龙启德第1L区1号地盘新九龙内地段第6564号以63.59亿港元的价格卖给了会德丰(00020.HK)。
181+
已在5个月前为其融到了50.47亿港元。除了出售地块之外,海航还卖掉了在香港金钟的一处办公室。3月21日,据香港当地媒体《明报》报道,
182+
海航已于今日出售位于香港金钟力宝中心的一处办公室,成交价为4000多万港元,折合单价为28000港元/平方英尺(折合243300元/平方米),
183+
较该物业的市场价值38000港元/平方英尺低了近两成。截至目前,海航在香港出售地产类物业已套现至少227亿港元。'''
184+
185+
extract_money = MoneyExtractor()
186+
res = extract_money(text, with_parsing=False)
187+
print(res)
188+

0 commit comments

Comments
 (0)