提交 902dbfe3 编写于 作者: Q QinYuan

init

上级
__pycache__
dist
MANIFEST
\ No newline at end of file
The MIT License (MIT)
Copyright (c) 2018 QinYuan Du
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
include LICENSE
exclude .gitignore
exclude addr.csv
\ No newline at end of file
#简介
一个用于提取简体中文字符串中省,市和区并能够进行映射,检验和简单绘图的python模块。
举个例子:
["徐汇区虹漕路461号58号楼5楼", "泉州市洛江区万安塘西工业区"]
↓ 转换
|省 |市 |区 |
|上海市|上海市|徐汇区|
|福建省|泉州市|洛江区|
#特点
- 基于jieba分词进行匹配,同时用比较复杂的匹配逻辑保证了准确率,笔者根据手头的海量地址描述数据进行了测试
- 自带完整的省,市,区三级地名及其经纬度的数据
- 支持自定义省,市,区映射
- 输出的是基于pandas的DataFrame类型的表结构,易于理解和使用
- 封装了简单的绘图功能,可以很方便地进行简单的数据可视化
- MIT 授权协议
#安装说明
代码目前仅仅支持python3
`pip install chinese_province_city_area_mapper`
#Get Started
本模块中最主要的类是chinese_province_city_area_mapper.transformer.CPCATransformer(注:CPCA是Chinese Province City Area的缩写),该类的transform方法可以输入任意的可迭代类型(如list,Series等),然后将其转换为一个DataFrame,示例代码如下:
from chinese_province_city_area_mapper.transformer import CPCATransformer
location_str = ["徐汇区虹漕路461号58号楼5楼", "泉州市洛江区万安塘西工业区", "朝阳区北苑华贸城"]
from chinese_province_city_area_mapper.transformer import CPCATransformer
cpca = CPCATransformer()
df = cpca.transform(location_str)
df
输出的结果为:
区 市 省
0 徐汇区 上海市 上海市
1 洛江区 泉州市 福建省
2 朝阳区
从上面的程序输出中你会发现朝阳区并没有被映射到北京市,这是因为在中国有多个同名的叫做朝阳区的区,并且他们位于不同的市,所以程序就不知道该映射到哪一个市了,因此就不对其进行映射,如果你确定你的数据中的朝阳区都是指北京市的那个朝阳区的话,可以在CPCATransformer的构造函数中传一个字典(叫做umap参数,是user map的简称),指定朝阳区都要映射到北京市,注意只有区到市的这一级映射存在重名问题,中国的市的名称都是唯一的,省的名称也都是唯一的,示例代码如下:
from chinese_province_city_area_mapper.transformer import CPCATransformer
location_str = ["徐汇区虹漕路461号58号楼5楼", "泉州市洛江区万安塘西工业区", "朝阳区北苑华贸城"]
from chinese_province_city_area_mapper.transformer import CPCATransformer
cpca = CPCATransformer({"朝阳区":"北京市"})
df = cpca.transform(location_str)
df
输出结果为:
区 市 省
0 徐汇区 上海市 上海市
1 洛江区 泉州市 福建省
2 朝阳区 北京市 北京市
模块中还内置了一个我推荐大家使用的umap,这个umap中我根据处理地址数据的经验将那些重名的区映射到了它最常见的一个市,这个umap位于chinese_province_city_area_mapper.myumap.umap,使用如下:
from chinese_province_city_area_mapper.transformer import CPCATransformer
location_str = ["徐汇区虹漕路461号58号楼5楼", "泉州市洛江区万安塘西工业区", "朝阳区北苑华贸城"]
from chinese_province_city_area_mapper.transformer import CPCATransformer
from chinese_province_city_area_mapper import myumap
print(myumap.umap) #查看这个umap的内容
cpca = CPCATransformer(myumap.umap)
df = cpca.transform(location_str)
df
输出和上一个程序一样
模块中还自带一个简单绘图工具,可以在地图上将上面输出的数据以热力图的形式画出来,代码如下:
from chinese_province_city_area_mapper import drawers
#df为上一段代码输出的df
drawers.draw_locations(df, "df.html")
这一段代码运行结束后会在运行代码的当前目录下生成一个df.html文件,用浏览器打开即可看到
绘制好的地图(如果某条数据'省','市'或'区'字段有缺,则会忽略该条数据不进行绘制),速度会比较慢,需要耐心等待,绘制的图像如下:
![绘图展示](http://ou427u8j5.bkt.clouddn.com/%E5%8C%97%E4%BA%AC_%E4%B8%8A%E6%B5%B7_%E6%B3%89%E5%B7%9E.png)
draw_locations函数还可以通过指定path参数来改变输出路径,示例代码如下::
from chinese_province_city_area_mapper import drawers
#在当前目录的父目录生成df.html
drawers.draw_locations(df, "df.html", path="../")
到这里就你就已经知道了本模块的基本使用了,接下来我会阐明更多细节。
#数据接口
本模块自带全国省市区的映射关系及其经纬度,如果你只是想使用这个数据的话可以使用如下代码:
```
from chinese_province_city_area_mapper.infrastructure import SuperMap
#地区到市的映射数据库,是一个字典类型(key为区名,value为其所属的市名),注意其中包含重复的区名
SuperMap.area_city_mapper
#重复的区名列表,列表类型,如果区名在这个列表中,则area_city_mapper的映射是不准确的
SuperMap.rep_areas
#市到省的映射数据库,字典类型(key为市的名称,value为省的名称)
SuperMap.city_province_mapper
```
#关于匹配与映射的细节
为了保证匹配与映射的正确性,我做了很多细节上的处理。
- 能够匹配到省或者市的缩写,比如将"北京市"缩写为"北京","江苏省"缩写为"江苏",依旧能够匹配到并且能够自动补全为全称,示例代码如下:
```
#测试数据
location_strs = ["江苏省南京市鼓楼区256号", "江苏南京鼓楼区256号"]
from chinese_province_city_area_mapper.transformer import CPCATransformer
cpca = CPCATransformer()
df = cpca.transform(location_strs)
df
```
输出的结果为:
```
区 市 省
0 鼓楼区 南京市 江苏省
1 鼓楼区 南京市 江苏省
```
- 能够自动检测字符串中匹配到的省,市和区是否是所属关系,如果不是所属关系的话,则会删去优先级较低的(注:如果匹配到的是缩写的话,即将"南京市"缩写为"南京",则认为优先级较低),如果优先级一样的话,则删除地域范围较小的,示例代码如下:
```
#测试数据,一些故意错乱的地址描述
location_strs = ["静安区南京西路30号", "南京市静安区", "江苏省上海市"]
from chinese_province_city_area_mapper.transformer import CPCATransformer
cpca = CPCATransformer()
df = cpca.transform(location_strs)
df
```
输出结果如下:
```
区 市 省
0 静安区 上海市 上海市
1 南京市 江苏省
2 江苏省
```
分析:第一个测试数据"静安区南京西路"会同时匹配到"静安区"和"南京"两个地域名称,但是静安区是属于上海的,和"南京"想矛盾,而且因为"南京"是"南京市"的缩写,因为优先级比较低,故放弃"南京"这个地域名称。
第二个测试数据匹配到"南京市"和"静安区"两个矛盾的地域名称,而且这两个名称都是全称,优先级相同,所以保留地域范围比较大的,即保留"南京市"而放弃"静安区"。
#测试数据
本仓库放了一份大约一万多条地址描述信息addr.csv,这是我当时测试与开发用的数据,目前的版本可以保证比较高的准确率,大家可以用这个数据继续进行测试,测试代码如下:
```
#读取数据
import pandas as pd
origin = pd.read_csv("addr.csv")
#转换
from chinese_province_city_area_mapper.transformer import CPCATransformer
from chinese_province_city_area_mapper import myumap
cpca = CPCATransformer(myumap.umap)
addr_df = cpca.transform(origin["原始地址"])
#输出
processed = pd.concat([origin, addr_df], axis=1)
processed.to_csv("processed.csv", index=False, encoding="utf-8")
```
绘图代码:
```
from chinese_province_city_area_mapper import drawers
#df为上一段代码输出的df
drawers.draw_locations(processed, "processed.html")
```
绘制的局部图像如下:
![绘制图像](http://ou427u8j5.bkt.clouddn.com/%E9%95%BF%E4%B8%89%E8%A7%92%E7%83%AD%E5%8A%9B%E5%9B%BE.png)
\ No newline at end of file
此差异已折叠。
# -*- coding: utf-8 -*-
# __init__.py
__version__ = "0.1"
# -*- coding: utf-8 -*-
"""
Created on Wed Feb 28 18:45:10 2018
@author: 燃烧杯
"""
from .infrastructure import SuperMap
#代表一个(省,市,区)组成的地点
class Location:
#直辖市
#municipality = ["北京市", "天津市", "上海市", "重庆市"]
def __init__(self):
self.province = Province()
self.city = City()
self.area = Area()
def setPlace(self, name, place_type):
if not hasattr(self, place_type):
from .exceptions import PlaceTypeNotExistException
raise PlaceTypeNotExistException(place_type + \
" 地区类型不存在")
if getattr(self, place_type).isEmpty():
getattr(self, place_type).setPlace(name)
def pca_map(self, umap):
if self.area.isEmpty():
self.__city_and_province()
else:
if (self.area.name not in SuperMap.rep_areas) or (umap.get(self.area.name)):
if umap.get(self.area.name):
tmp = umap.get(self.area.name)
else:
tmp = SuperMap.area_city_mapper.get(self.area.name)
if self.city.isEmpty() or self.city.precision == 0:
self.city.setPlace(tmp)
elif self.city.isNotEmpty() and self.city.precision == 1:
if not self.area.isBlong(self.city.name) \
and umap.get(self.area.name) != self.city.name:
self.area.reset()
self.__city_and_province()
else:#重复区名 代码执行路径
import logging
SuperMap.rep_area_set.add(self.area.name)
logging.warning("在多个市存在相同区名-'" + self.area.name + \
"',最好在CPCATransformer的构造函数传入一个map指定其所属市")
if self.city.isNotEmpty():
self.__city_and_province()
if self.city.name.isdigit():
self.city.reset()
import pandas as pd
#组装成DataFrame
return pd.DataFrame({"省":[self.province.name], "市":[self.city.name], \
"区":[self.area.name]})
def __city_and_province(self):
if self.city.isNotEmpty() and self.province.isNotEmpty():
if not self.city.isBlong(self.province.name):
if self.city.precision > self.province.precision:
self.province.name = self.city.belong
else:
self.city.reset()
elif self.city.isNotEmpty() and self.province.isEmpty():
self.province.name = self.city.belong
class Place:
def __init__(self, name=""):
self.name = name
self.precision = 1
def reset(self):
self.name = ""
def isBlong(self, mayBe):
return self.belong == mayBe
def isEmpty(self):
return False if self.name else True
def isNotEmpty(self):
return True if self.name else False
class City(Place):
def __init__(self, name=""):
super().__init__()
def __getBlong(self):
return SuperMap.city_province_mapper.get(self.name)
def setPlace(self, name):
self.name, isfilled = SuperMap.fillCity(name)
if isfilled:#如果是需要补充字的,则认为这个匹配准确率比较低
self.precision = 0
self.belong = self.__getBlong()
class Province(Place):
def __init__(self, name=""):
super().__init__()
def __getBlong(self):
return SuperMap.province_country_mapper.get(self.name)
def setPlace(self, name):
self.name, isfilled = SuperMap.fillProvince(name)
if isfilled:#如果是需要补充字的,则认为这个匹配准确率比较低
self.precision = 0
self.belong = self.__getBlong()
class Area(Place):
def __init__(self, name=""):
super().__init__()
def __getBlong(self):
return SuperMap.area_city_mapper.get(self.name)
def setPlace(self, name):
self.name = name
self.precision = 1
self.belong = self.__getBlong()
\ No newline at end of file
# -*- coding: utf-8 -*-
"""
Created on Sun Mar 4 13:38:00 2018
@author: 燃烧杯
"""
def draw_locations(locations, fileName, path="./"):
import pandas as pd
if not isinstance(locations, pd.DataFrame):
from .exceptions import InputTypeNotSuportException
raise InputTypeNotSuportException(InputTypeNotSuportException.input_type)
if "省" not in locations.columns or "市" not in locations.columns \
or "区" not in locations.columns:
raise InputTypeNotSuportException(InputTypeNotSuportException.input_type)
import folium
from folium.plugins import HeatMap
from .infrastructure import SuperMap
map_keys = locations["省"] + "," + locations["市"] + "," + locations["区"]
heatData = []
for map_key in map_keys:
if SuperMap.lat_lon_mapper.get(map_key):
lat_lon = SuperMap.lat_lon_mapper[map_key]
heatData.append([lat_lon[0], lat_lon[1], 1])
map_osm = folium.Map(location=[35,110],zoom_start=5) #绘制Map,开始缩放程度是5倍
HeatMap(heatData).add_to(map_osm) # 将热力图添加到前面建立的map里
file_path = path + fileName
map_osm.save(file_path) #保存为html文件
\ No newline at end of file
# -*- coding: utf-8 -*-
"""
Created on Wed Feb 28 18:55:58 2018
@author: 燃烧杯
"""
class CPCAException(Exception):
pass
class PlaceTypeNotExistException(CPCAException):
pass
class InputTypeNotSuportException(CPCAException):
input_type = \
"""
输入应该为
|省 |市 |区 |
|江苏省 |扬州市 |邗江区 |
格式的pandas.core.frame.DateFrame
"""
pass
\ No newline at end of file
# -*- coding: utf-8 -*-
"""
Created on Sun Feb 25 00:55:14 2018
@author: 燃烧杯
"""
import jieba
class Record:
def __init__(self, line):
from .domain import Location
self.location = Location()
#默认选取第一个遇到的省,市或者自治区
for word in jieba.cut(line):
if word == "上海市浦东新区":
self.location.setPlace("上海市", SuperMap.CITY)
self.location.setPlace("浦东新区", SuperMap.AREA)
continue
word_type = SuperMap.getType(word)
if word_type:
self.location.setPlace(word, word_type)
def pca_map(self, umap):
return self.location.pca_map(umap)
class SuperMap:
from .mappers import area_city_mapper, city_province_mapper,\
province_country_mapper, rep_areas, \
lat_lon_mapper
AREA = "area"
CITY = "city"
PROVINCE = "province"
rep_area_set = set()
@classmethod
def getType(cls, word):
if cls.area_city_mapper.get(word):
return cls.AREA
if cls.city_province_mapper.get(word):
return cls.CITY
if cls.province_country_mapper.get(word):
return cls.PROVINCE
return ""
#如果将“北京市”简写作“北京”,则补全“市”字
@classmethod
def fillCity(cls, word):
if word and not word.endswith("市"):
return word + "市", True
return word, False
#如果将“重庆省”简写成“重庆”,则补全“省字”
@classmethod
def fillProvince(cls, word):
if word and not word.endswith("市") and not word.endswith("省"):
if cls.province_country_mapper.get(word + "市"):
return word + "市", True
if cls.province_country_mapper.get(word + "省"):
return word + "省", True
return word, False
\ No newline at end of file
此差异已折叠。
# -*- coding: utf-8 -*-
umap = {'南关区': '长春市',
'南山区': '深圳市',
'宝山区': '上海市',
'市辖区': '东莞市',
'普陀区': '上海市',
'朝阳区': '北京市',
'河东区': '天津市',
'白云区': '广州市',
'西湖区': '杭州市',
'铁西区': '沈阳市'}
# -*- coding: utf-8 -*-
"""
Created on Sun Feb 25 00:47:32 2018
@author: 燃烧杯
"""
from .infrastructure import Record
class CPCATransformer:
#可以传入用户自定义区县map,因为原始数据区县map有很多key重复
def __init__(self, u_map={}):
self.umap = u_map
def transform(self, data):
from .infrastructure import SuperMap
SuperMap.rep_area_set = set()
import pandas as pd
if isinstance(data, pd.Series):
data = data.astype(str)
lines = []
for line in data:
lines.append(Record(line).pca_map(self.umap))
import logging
if len(SuperMap.rep_area_set) != 0:
logging.warning("建议添加到umap中的区有:" + str(SuperMap.rep_area_set))
import pandas as pd
return pd.concat(lines, ignore_index=True)
\ No newline at end of file
pandas>=0.20.1
folium>=0.5.0
jieba>=0.39
\ No newline at end of file
# -*- coding: utf-8 -*-
from distutils.core import setup
LONGDOC = """
chinese_province_city_area_mapper
==================================
chinese_province_city_area_mapper:一个用于识别简体中文字符串中省,市和区并能够进行映射,检验和简单绘图的python模块
举个例子::
["徐汇区虹漕路461号58号楼5楼", "泉州市洛江区万安塘西工业区"]
↓ 转换
|省 |市 |区 |
|上海市|上海市|徐汇区|
|福建省|泉州市|洛江区|
chinese_province_city_area_mapper: built to be recognize Chinese province,city and area in simplified Chinese string, it can automaticall map area to city
and map city to province.
for example::
["徐汇区虹漕路461号58号楼5楼", "泉州市洛江区万安塘西工业区"]
↓ transform
|省 |市 |区 |
|上海市|上海市|徐汇区|
|福建省|泉州市|洛江区|
完整文档见该模块的Github,
GitHub: `https://github.com/DQinYuan/chinese_province_city_area_mapper <https://github.com/DQinYuan/chinese_province_city_area_mapper>`_
特点
====
- 基于jieba分词进行匹配,同时拥有比较复杂的匹配逻辑保证了准确率,笔者根据手头的海量地址描述数据进行了测试
- 自带完整的省,市,区三级地名及其经纬度的数据
- 支持自定义省,市,区映射
- 输出的是基于pandas的DataFrame类型的表结构,易于理解和使用
- 封装了简单的绘图功能,可以很方便地进行简单的数据可视化
- MIT 授权协议
安装说明
========
代码目前仅仅支持python3
``pip install chinese_province_city_area_mapper``
Get Started
============
本模块中最主要的类是chinese_province_city_area_mapper.transformer.CPCATransformer(注:CPCA是Chinese Province City Area的缩写),
该类的transform方法可以输入任意的可迭代类型(如list,Series等),然后将其转换为一个DataFrame,
示例代码如下::
location_str = ["徐汇区虹漕路461号58号楼5楼", "泉州市洛江区万安塘西工业区", "朝阳区北苑华贸城"]
from chinese_province_city_area_mapper.transformer import CPCATransformer
cpca = CPCATransformer()
df = cpca.transform(location_str)
df
输出的结果为::
区 市 省
0 徐汇区 上海市 上海市
1 洛江区 泉州市 福建省
2 朝阳区
从上面的程序输出中你会发现朝阳区并没有被映射到北京市,这是因为在中国有多个同名的叫做朝阳区的区,
并且他们位于不同的市,所以程序就不知道该映射到哪一个市了,因此就不对其进行映射,如果你确定你
的数据中的朝阳区都是指北京市的那个朝阳区的话,可以在CPCATransformer的构造函数中传一个字典
(叫做umap参数,是user map的简称),指定朝阳区都要映射到北京市,
注意只有区到市的这一级映射存在重名问题,中国的市的名称都是唯一的,省的名称也都是唯一的
,示例代码如下::
location_str = ["徐汇区虹漕路461号58号楼5楼", "泉州市洛江区万安塘西工业区", "朝阳区北苑华贸城"]
from chinese_province_city_area_mapper.transformer import CPCATransformer
cpca = CPCATransformer({"朝阳区":"北京市"})
df = cpca.transform(location_str)
df
输出结果为::
区 市 省
0 徐汇区 上海市 上海市
1 洛江区 泉州市 福建省
2 朝阳区 北京市 北京市
模块中还内置了一个我推荐大家使用的umap,这个umap中我根据处理地址数据的经验将那些重名的区映射到了它最常见的一个市,
这个umap位于chinese_province_city_area_mapper.myumap.umap,使用如下::
location_str = ["徐汇区虹漕路461号58号楼5楼", "泉州市洛江区万安塘西工业区", "朝阳区北苑华贸城"]
from chinese_province_city_area_mapper.transformer import CPCATransformer
from chinese_province_city_area_mapper import myumap
print(myumap.umap) #查看这个umap的内容
cpca = CPCATransformer(myumap.umap)
df = cpca.transform(location_str)
df
输出和上一个程序一样
模块中还自带一个简单绘图工具,可以在地图上将上面输出的数据以热力图的形式画出来
,代码如下::
from chinese_province_city_area_mapper import drawers
#df为上一段代码输出的df
drawers.draw_locations(df, "df.html")
这一段代码运行结束后会在运行代码的当前目录下生成一个df.html文件,用浏览器打开即可看到
绘制好的地图(如果某条数据'省','市'或'区'字段有缺,则会忽略该条数据不进行绘制)。
draw_locations函数还可以通过指定path参数来改变输出路径,示例代码如下::
from chinese_province_city_area_mapper import drawers
#在当前目录的父目录生成df.html
drawers.draw_locations(df, "df.html", path="../")
本模块的基本使用方法大概就是这些了,如果还想知道更多的细节,请访问该
模块的github地址 `https://github.com/DQinYuan/chinese_province_city_area_mapper <https://github.com/DQinYuan/chinese_province_city_area_mapper>`_,
在那里我写了更多的细节
"""
requires = ['pandas(>=0.20.1)',
'folium(>=0.5.0)',
'jieba(>=0.39)',
]
setup(name='chinese_province_city_area_mapper',
version='1.6',
description='Chinese Province, City and Area Recognition Utilities',
long_description=LONGDOC,
author='DQinYuan',
author_email='sa517067@mail.ustc.edu.cn',
url='https://github.com/DQinYuan/chinese_province_city_area_mapper',
license="MIT",
classifiers=[
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Natural Language :: Chinese (Simplified)',
'Programming Language :: Python :: 3.6',
'Topic :: Text Processing',
'Topic :: Text Processing :: Indexing',
],
keywords='Simplified Chinese,Chinese geographic information,Chinese province city and area recognition and map',
packages=['chinese_province_city_area_mapper'],
package_dir={'chinese_province_city_area_mapper':'chinese_province_city_area_mapper'},
package_data={'chinese_province_city_area_mapper':['*.*']},
install_requires = requires,
)
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册