某公司手机点餐门店分布可视化

背景:该公司是全球大型连锁快餐企业,为了解其手机点餐门店分布情况,爬取门店详细地址,实现数据分析及可视化。可视化需要解决的思路:

  • 1.需要把门店地址转换成经纬度;
  • 2.需要根据得到的经纬度提取门店详细的省份、城市等信息;
  • 3.构造Geopandas所需数据格式;
  • 4.合并各省市2018年GDP(亿元)和常住人口(万人)数据。

导入相关包

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.pylab as plb
from  matplotlib import cm
import seaborn as sns
plt.rc('font', family='SimHei', size=18)# 显示中文标签
plt.rcParams['axes.unicode_minus'] = False
sns.set()
%matplotlib inline
import geopandas 
from shapely.geometry import LineString,Point
import re
import requests
import json
import hashlib
from wordcloud import WordCloud
from matplotlib.patches import Polygon
from mpl_toolkits.basemap import Basemap
from matplotlib.collections import PatchCollection
import warnings
warnings.filterwarnings('ignore')

数据处理

导入数据

data=pd.read_excel(r"D:\PycharmProjects\BK_store.xls")
data.head()
storename address
0 北京星泰中心 北京市朝阳区酒仙桥路甲16号星泰中心A1-02
1 后街商业广场 昆山市虹祺路243号后街商业广场C5幢楼一楼部分商铺。
2 哈尔滨国际餐厅 哈尔滨西大直街4号567漫天地二层
3 海那城百联奥特莱斯 济南市槐荫区美里路555号10号楼一层商铺
4 扬州三盛店 扬州市邗江中路358号三盛国际广场5号楼1 - 4号商铺。

数据查看

data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1150 entries, 0 to 1149
Data columns (total 2 columns):
storename    1150 non-null object
address      1150 non-null object
dtypes: object(2)
memory usage: 18.0+ KB
data.dtypes
storename    object
address      object
dtype: object
data.isnull().sum()#查看缺失值
storename    0
address      0
dtype: int64
data.duplicated().sum()#查看重复值
0

自定义函数,根据地址查询经纬度

data.storename=data.storename.astype(str)
data.address=data.address.astype(str)
def get_locationdata(address):
    requests.adapters.DEFAULT_RETRIES =5
    requests.session().keep_alive = False
    url='http://restapi.amap.com/v3/geocode/geo?key=1fbbc548b828f33ccc64f4613ae52497&s=rsv3&city=35&address={}'.format(address)
    headers = {'User-Agent': 'Mozilla/5.0'}
    response = requests.get(url, headers=headers)
    if 'location' in response.text:
        for i in json.loads(response.text)['geocodes']:
            return i.get('location')
    else:
        pass
get_locationdata('昆山市虹祺路243号后街商业广场C5幢楼一楼部分商铺')
'120.920690,31.411805'
data['loncationdetail']=data.address.apply(get_locationdata)
data[data.loncationdetail.isnull()]#查看缺失值
storename address loncationdetail city province district township
data.drop([1018],inplace=True)#索引1018为测试门店数据,删除该行数据
data.loc[95].address='徐州市'+data.loc[95].address
data.loc[332].address='昆明市'+data.loc[332].address
data.loc[648].address='常州市'+data.loc[648].address
data.loc[740].address='昆明市'+data.loc[740].address
data.loc[1033].address='镇江市'+data.loc[1033].address
data.loc[1095].address='嘉兴市'+data.loc[1095].address
data['loncationdetail']=data.address.apply(get_locationdata)#之前缺失的行再执行一次,获取经纬度信息
data[data.loncationdetail.isnull()]#查看缺失值
storename address loncationdetail city province district township
data['lon']=data.loncationdetail.str.split(',').apply(lambda x:x[0]).astype(float)##经度
data['lat']=data.loncationdetail.str.split(',').apply(lambda x:x[1]).astype(float)#纬度
data.head()
storename address loncationdetail city province district township lon lat
0 北京星泰中心 北京市朝阳区酒仙桥路甲16号星泰中心A1-02 116.490200,39.971870 北京市 北京市 朝阳区 将台镇 116.490200 39.971870
1 后街商业广场 昆山市虹祺路243号后街商业广场C5幢楼一楼部分商铺。 120.920690,31.411805 苏州市 江苏省 昆山市 玉山镇 120.920690 31.411805
2 哈尔滨国际餐厅 哈尔滨西大直街4号567漫天地二层 126.643240,45.756718 哈尔滨市 黑龙江省 南岗区 花园街道 126.643240 45.756718
3 海那城百联奥特莱斯 济南市槐荫区美里路555号10号楼一层商铺 116.913773,36.716513 济南市 山东省 槐荫区 美里湖街道 116.913773 36.716513
4 扬州三盛店 扬州市邗江中路358号三盛国际广场5号楼1 - 4号商铺。 119.397392,32.380924 扬州市 江苏省 邗江区 邗上街道 119.397392 32.380924
data.isnull().sum()#再次查看缺失值
storename          0
address            0
loncationdetail    0
city               0
province           0
district           0
township           0
lon                0
lat                0
dtype: int64

自定义函数,根据经纬度查询详细地址

def get_data(location):
    url='https://restapi.amap.com/v3/geocode/regeo?key=1fbbc548b828f33ccc64f4613ae52497&s=rsv3&city=35&location={}'.format(location)
    requests.adapters.DEFAULT_RETRIES =5
    requests.session().keep_alive = False
    headers = {'User-Agent': 'Mozilla/5.0'}
    response = requests.get(url, headers=headers)
    address_dict={}
    if 'regeocode' in response.text:
        address_detail=re.search('"addressComponent":(.*),(.*),(.*),(.*)',response.text).group(1)
        address_dict['city']=json.loads(address_detail)['city']
        address_dict["province"]=json.loads(address_detail)["province"]
        address_dict["district"]=json.loads(address_detail)["district"]
        address_dict["township"]=json.loads(address_detail)["township"]
    else:
        pass
    return address_dict 
get_data('119.397392,32.380924')
{'city': '扬州市', 'province': '江苏省', 'district': '邗江区', 'township': '邗上街道'}
data['city']=data.loncationdetail.apply(get_data).apply(lambda x:x['city'])#提取城市
data['province']=data.loncationdetail.apply(get_data).apply(lambda x:x['province'])#提取省份
data['district']=data.loncationdetail.apply(get_data).apply(lambda x:x['district'])#提取辖区
data['township']=data.loncationdetail.apply(get_data).apply(lambda x:x['township'])#提取街道
data.head()
storename address loncationdetail city province district township lon lat
0 北京星泰中心 北京市朝阳区酒仙桥路甲16号星泰中心A1-02 116.490200,39.971870 北京市 北京市 朝阳区 将台镇 116.490200 39.971870
1 后街商业广场 昆山市虹祺路243号后街商业广场C5幢楼一楼部分商铺。 120.920690,31.411805 苏州市 江苏省 昆山市 玉山镇 120.920690 31.411805
2 哈尔滨国际餐厅 哈尔滨西大直街4号567漫天地二层 126.643240,45.756718 哈尔滨市 黑龙江省 南岗区 花园街道 126.643240 45.756718
3 海那城百联奥特莱斯 济南市槐荫区美里路555号10号楼一层商铺 116.913773,36.716513 济南市 山东省 槐荫区 美里湖街道 116.913773 36.716513
4 扬州三盛店 扬州市邗江中路358号三盛国际广场5号楼1 - 4号商铺。 119.397392,32.380924 扬州市 江苏省 邗江区 邗上街道 119.397392 32.380924
for i in ['city','province','district','township']:#查看含有空列表的列
        print(str(i),':',(data[i].apply(len)==0).sum())
city : 294
province : 0
district : 10
township : 7
data[data.province.str.contains('市')].index#查看含有city列为空列表的行索引
Int64Index([   0,    5,    7,    9,   11,   14,   20,   21,   28,   29,
            ...
            1123, 1130, 1131, 1134, 1135, 1136, 1137, 1138, 1146, 1147],
           dtype='int64', length=294)
for i in data[data.province.str.contains('市')].index:
    data.loc[i,'city']=data.loc[i,'province']
data.head()
storename address loncationdetail city province district township
0 北京星泰中心 北京市朝阳区酒仙桥路甲16号星泰中心A1-02 116.490200,39.971870 北京市 北京市 朝阳区 将台镇
1 后街商业广场 昆山市虹祺路243号后街商业广场C5幢楼一楼部分商铺。 120.920690,31.411805 苏州市 江苏省 昆山市 玉山镇
2 哈尔滨国际餐厅 哈尔滨西大直街4号567漫天地二层 126.643240,45.756718 哈尔滨市 黑龙江省 南岗区 花园街道
3 海那城百联奥特莱斯 济南市槐荫区美里路555号10号楼一层商铺 116.913773,36.716513 济南市 山东省 槐荫区 美里湖街道
4 扬州三盛店 扬州市邗江中路358号三盛国际广场5号楼1 - 4号商铺。 119.397392,32.380924 扬州市 江苏省 邗江区 邗上街道

可视化分析

转换点数据

xy = [Point(xy) for xy in zip(data.lon,data.lat)]
geo_data=geopandas.GeoDataFrame(data,geometry=xy)
geo_data.head()
storename address loncationdetail city province district township lon lat geometry
0 北京星泰中心 北京市朝阳区酒仙桥路甲16号星泰中心A1-02 116.490200,39.971870 北京市 北京市 朝阳区 将台镇 116.490200 39.971870 POINT (116.4902 39.97187)
1 后街商业广场 昆山市虹祺路243号后街商业广场C5幢楼一楼部分商铺。 120.920690,31.411805 苏州市 江苏省 昆山市 玉山镇 120.920690 31.411805 POINT (120.92069 31.411805)
2 哈尔滨国际餐厅 哈尔滨西大直街4号567漫天地二层 126.643240,45.756718 哈尔滨市 黑龙江省 南岗区 花园街道 126.643240 45.756718 POINT (126.64324 45.756718)
3 海那城百联奥特莱斯 济南市槐荫区美里路555号10号楼一层商铺 116.913773,36.716513 济南市 山东省 槐荫区 美里湖街道 116.913773 36.716513 POINT (116.913773 36.716513)
4 扬州三盛店 扬州市邗江中路358号三盛国际广场5号楼1 - 4号商铺。 119.397392,32.380924 扬州市 江苏省 邗江区 邗上街道 119.397392 32.380924 POINT (119.397392 32.380924)

导入中国地图shp

gdf = geopandas.read_file(r"D:\PycharmProjects\中国地图shp格式\china_basic_map\bou2_4p.shp",encoding='gbk')
gdf.head()
AREA PERIMETER BOU2_4M_ BOU2_4M_ID ADCODE93 ADCODE99 NAME geometry
0 54.447 68.489 2 23 230000 230000 黑龙江省 POLYGON ((121.4884414672852 53.33264923095703,...
1 129.113 129.933 3 15 150000 150000 内蒙古自治区 POLYGON ((121.4884414672852 53.33264923095703,...
2 175.591 84.905 4 65 650000 650000 新疆维吾尔自治区 POLYGON ((96.38328552246094 42.72695541381836,...
3 21.315 41.186 5 22 220000 220000 吉林省 POLYGON ((123.1710433959961 46.24668121337891,...
4 15.603 38.379 6 21 210000 210000 辽宁省 POLYGON ((123.6901931762695 43.37676620483398,...

各省市门店分布空间分析

gdf.plot(figsize=(15,15), alpha=0.5, edgecolor='white',color='gray')
plt.gca().xaxis.set_major_locator(plt.NullLocator()) #去掉x轴刻度
plt.gca().yaxis.set_major_locator(plt.NullLocator()) #去掉y轴刻度
output_41_0.png
geo_data.plot(ax=gdf.plot(figsize=(15,15),legend=True,edgecolor='white',color='gray'),color='yellow',markersize=8)
plt.rc('font', family='SimHei', size=20)# 显示中文标签
plt.rcParams['axes.unicode_minus'] = False
plt.title('B快餐公司微信手机点餐门店城市分布图',size=20)
plt.gca().xaxis.set_major_locator(plt.NullLocator()) #去掉x轴刻度
plt.gca().yaxis.set_major_locator(plt.NullLocator()) #去掉y轴刻度
output_42_0.png
geo_data['shop_counts']=geo_data.groupby('province')['geometry'].transform(lambda x: x.count())
geo_data.head()
storename address loncationdetail city province district township lon lat geometry shop_counts
0 北京星泰中心 北京市朝阳区酒仙桥路甲16号星泰中心A1-02 116.490200,39.971870 北京市 北京市 朝阳区 将台镇 116.490200 39.971870 POINT (116.4902 39.97187) 102
1 后街商业广场 昆山市虹祺路243号后街商业广场C5幢楼一楼部分商铺。 120.920690,31.411805 苏州市 江苏省 昆山市 玉山镇 120.920690 31.411805 POINT (120.92069 31.411805) 156
2 哈尔滨国际餐厅 哈尔滨西大直街4号567漫天地二层 126.643240,45.756718 哈尔滨市 黑龙江省 南岗区 花园街道 126.643240 45.756718 POINT (126.64324 45.756718) 15
3 海那城百联奥特莱斯 济南市槐荫区美里路555号10号楼一层商铺 116.913773,36.716513 济南市 山东省 槐荫区 美里湖街道 116.913773 36.716513 POINT (116.913773 36.716513) 68
4 扬州三盛店 扬州市邗江中路358号三盛国际广场5号楼1 - 4号商铺。 119.397392,32.380924 扬州市 江苏省 邗江区 邗上街道 119.397392 32.380924 POINT (119.397392 32.380924) 156
geo_data.plot(ax=gdf.plot(figsize=(15,15),legend=True,edgecolor='white',color='gray'),column='shop_counts', cmap='rainbow',scheme='quantiles',legend=True,figsize=(8,10)
             ,marker='o', markersize=100) #按个数多少叠加底色
plt.gca().xaxis.set_major_locator(plt.NullLocator()) #去掉x轴刻度
plt.gca().yaxis.set_major_locator(plt.NullLocator()) #去年y轴刻度
output_45_0.png

各省市门店数统计

#各省市门店数
province_pc=geo_data.groupby('province',as_index=False)['shop_counts'].count().sort_values(by='shop_counts',ascending=False)
province_pc.head(10)
province shop_counts
14 江苏省 156
11 广东省 132
0 上海市 113
3 北京市 102
18 浙江省 73
9 山东省 68
25 辽宁省 60
5 四川省 49
23 福建省 44
6 天津市 42
#对含有自治区的字符串换行处理
import textwrap
for i in province_pc[province_pc.province.str.contains('区')].index.to_list():
     province_pc.loc[i,'province']=textwrap.fill( province_pc.loc[i,'province'],province_pc.loc[i,'province'].find('自'))
province_pc[province_pc.province.str.contains('区')]
province shop_counts
13 新疆维吾尔\n自治区 7
2 内蒙古\n自治区 7
12 广西壮族\n自治区 5
7 宁夏回族\n自治区 4
import squarify
labels = province_pc.apply(lambda x: str(x[0]) + "\n (" + str(x[1]) + ")", axis=1)
sizes = province_pc['shop_counts'].values.tolist()
colors = [plt.cm.Set1(i/float(len(labels))) for i in range(len(labels))]
plt.figure(figsize=(10,12),dpi= 80)
squarify.plot(sizes=sizes, label=labels, color=colors, alpha=.8)
plt.rc('font',size=10)
plt.title('各省市门店数树形图')
plt.axis('off')#去除上边框和右边框刻度
plt.tick_params(top='off',right='off')##去除上边框和右边框刻度
output_50_0.png
  • 结论1:门店主要集中以下区域:江苏省、上海市、浙江省、广东省、北京市及山东省

各省市门店数标准化分析:Z-Score

x=province_pc.shop_counts
province_pc['province_z']= (x - x.mean())/x.std()#各省市门店数量的z分位数
province_pc['colors'] = ['red' if x < 0 else 'green' for x in province_pc['province_z']]
province_pc.sort_values('province_z', inplace=True)
province_pc.reset_index(inplace=True)
province_pc.head(10)
index province shop_counts province_z colors
0 28 青海省 3 -0.870936 red
1 24 贵州省 4 -0.846264 red
2 7 宁夏回族自治区 4 -0.846264 red
3 12 广西壮族自治区 5 -0.821592 red
4 22 甘肃省 5 -0.821592 red
5 19 海南省 6 -0.796919 red
6 2 内蒙古自治区 7 -0.772247 red
7 13 新疆维吾尔自治区 7 -0.772247 red
8 15 江西省 9 -0.722902 red
9 10 山西省 12 -0.648885 red
plt.figure(figsize=(16,20), dpi= 80)

plt.hlines(y=province_pc.index, xmin=0, xmax=province_pc.province_z, color=province_pc.colors, alpha=0.6, linewidth=15)

for x, y, tex in zip(province_pc.province_z, province_pc.index, province_pc.province_z):

    t = plt.text(x, y, round(tex,2), horizontalalignment='right' if x < 0 else 'left',

                 verticalalignment='center', fontdict={'color':'red' if x < 0 else 'green', 'size':15})

plt.gca().set(ylabel='省市', xlabel='门店数量')

plt.yticks(province_pc.index, province_pc.province, fontsize=15)

plt.title('各省市门店数量Z-Score', fontdict={'size':20})

plt.grid(linestyle='--', alpha=0.1)
output_54_0.png
#门店数Top10省市
province_pc.sort_values(by='shop_counts',ascending=False).head(10).province.to_list()
['江苏省', '广东省', '上海市', '北京市', '浙江省', '山东省', '辽宁省', '四川省', '福建省', '天津市']
  • 结论2:Z-Score衡量各省市门店数相比于整体均值的偏差水平。其中门店数Top10的省市Z-Score>0,其他省市Z-Score<0。

各省市门店数与GDP、常住人口数相关性分析

#导入各省市2018年GDP(亿元)和常住人口(万人)数据
gp=pd.read_csv(r"D:\PycharmProjects\分省年度GDP及人口数据.csv",encoding="gbk")
gp.head()
province GDP (亿元) Popluation (万人)
0 北京市 30319.98 2154
1 天津市 18809.64 1560
2 河北省 36010.27 7556
3 山西省 16818.11 3718
4 内蒙古自治区 17289.22 2534
#将各省份门店数、2018年GDP、常住人口数合并
result=pd.merge(province_pc.loc[:,['province','shop_counts']],gp,how='left',on='province')
corr = result.corr()#相关系数
corr
shop_counts GDP (亿元) Popluation (万人)
shop_counts 1.000000 0.797473 0.431992
GDP\n(亿元) 0.797473 1.000000 0.841422
Popluation\n(万人) 0.431992 0.841422 1.000000
cmap = sns.diverging_palette(220,10, as_cmap=True) 
sns.heatmap(corr, cmap=cmap,center=0.5, annot=True)
plt.xticks(fontsize=10)
plt.yticks(fontsize=10)
plt.title('各省市2018年GDP、常住人口数与门店数热度图', fontsize=15)
output_60_1.png
  • 结论3:各市门店数与2018年GDP、常住人口数均正相关,其中与GDP相关性较高。

门店数Top14城市

#各城市门店数
city_pc=geo_data.groupby('city',as_index=False)['shop_counts'].count().sort_values(by='shop_counts',ascending=False)
city_pc
city shop_counts
3 上海市 113
21 北京市 102
109 苏州市 46
90 深圳市 44
41 天津市 42
22 南京市 39
59 成都市 39
127 重庆市 37
69 杭州市 35
52 广州市 32
118 西安市 28
40 大连市 25
77 沈阳市 24
73 武汉市 24
63 无锡市 23
134 青岛市 20
84 济南市 20
105 福州市 19
65 昆明市 16
131 长春市 15
101 珠海市 14
28 厦门市 14
45 宁波市 14
30 合肥市 13
50 常州市 12
26 南通市 12
104 石家庄市 12
34 哈尔滨市 11
125 郑州市 11
132 长沙市 10
... ... ...
5 东营市 1
7 临汾市 1
9 丹东市 1
10 丽江市 1
12 亳州市 1
13 伊犁哈萨克自治州 1
15 佳木斯市 1
17 信阳市 1
27 南阳市 1
32 周口市 1
37 嘉峪关市 1
38 四平市 1
39 大庆市 1
48 宝鸡市 1
100 牡丹江市 1
49 宿迁市 1
54 延边朝鲜族自治州 1
55 张家口市 1
62 揭阳市 1
64 日照市 1
66 晋中市 1
67 普洱市 1
68 曲靖市 1
1 三明市 1
72 梅州市 1
75 汕尾市 1
82 泸州市 1
88 淮北市 1
98 潮州市 1
139 齐齐哈尔市 1

140 rows × 2 columns

y=city_pc.shop_counts
city_pc['city_z']= (y - y.mean())/y.std()#各省市门店数量的z分位数
city_pc.sort_values('city_z', inplace=True,ascending=False)
city_pc.reset_index(inplace=True)
del city_pc["index"]
#门店数Top14城市
city_pc[city_pc.city_z>=1]
city shop_counts city_z
0 上海市 113 6.735240
1 北京市 102 6.028248
2 苏州市 46 2.429020
3 深圳市 44 2.300476
4 天津市 42 2.171932
5 南京市 39 1.979116
6 成都市 39 1.979116
7 重庆市 37 1.850573
8 杭州市 35 1.722029
9 广州市 32 1.529213
10 西安市 28 1.272125
11 大连市 25 1.079309
12 沈阳市 24 1.015037
13 武汉市 24 1.015037
input_colors =cm.rainbow(np.arange(len(city_pc.values))-2/len(city_pc.values)-2) 
plt.rc('font', family='SimHei', size=18)# 显示中文标签
plt.rcParams['axes.unicode_minus'] = False
plt.figure(figsize=(10,10))
plt.pie(city_pc.shop_counts.head(14),  startangle=70, labels=city_pc.city.head(14),  
    autopct='%1.2f%%', pctdistance=0.8,  
    labeldistance=1.1, textprops={'fontsize':15,'color':'black'},colors=input_colors)  
plt.legend(bbox_to_anchor=(1.05, 0), loc=3, borderaxespad=0)
plt.title('门店数Top14城市',fontsize=20)
output_66_1.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容