一、引言
本博客是本人是基于本人可视化课设所做的总结,其中有些过程的实现可能并不是最优的实现方法,有些实现效果也因为本人的实力有限,并不能达到预期的效果,所以也欢迎大家指点和改良。(刚考完期末回家,终于有时间把这个课设写个博客了,虽然这课设是明天截至的,我今天发也不知道算不算半场开香槟,只能希望没有人这么快就抄了我的作业吧...)(为什么都是绝对地址,因为我的python工作区不知道为什么就固定了,只是在另一个文件夹里)
二、数据获取
说实话,本人在做这个课设的时候,80%的时间都是在做数据相关的事情,讲真的,可视化的重点不在这里,希望其他人能少走点弯路,实在不行就直接去拿别人那种已经做好的数据是最好的。
1、星巴克
星巴克门店我们在进入星巴克官网的时候就能发现上面有一栏显示是星巴克门店的信息,点击进入后,我们按F12进入开发者工具,点开其中的网络项刷新,能看到浏览器端的响应过程,通过对几个特别大的数据过程,点开查看响应,我们可以发现这是这个请求,浏览器接收到了服务器端发来的位置信息。
因此,我们的下一步就是要如何获取,接受这些信息。继续点开开发者工具中的消息头,我们可以看到,浏览器发送的请求是长这个样子的。
点开倒三角,可以看到在域名www.starbucks.com.cn下通过服务器根位置/api/stores/nearby发送了六个参数,来获取需要的信息,分别是经纬度,传回信息限制数(门店信息数量),这个locale可能有对Linux有涉及的朋友知道这是是软件在运行时的语言环境,ZH就是中文的意思,而这个features一般都填空,这是html5的内容(大概,网页交互效果?),后面radius就是指圆的半径。总结一下,就是向客户端发送api包括经纬度和半径,实现在这个经纬度为圆心的圈里获取不超过limit数量的门店信息。
# 定义获取星巴克门店信息的函数
def starbucks(row):
url = "https://www.starbucks.com.cn/api/stores/nearby"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
}#这里是浏览器的环境信息,改成自己的,在开发者工具里面的消息头有请求头的内容,里面就有这一项
params = {
"lat": row['lat'],
"lon": row['lon'],
"limit": "3000",
"locale": "ZH",
"features": "",
"radius": "200000",
}
2、蜜雪冰城
一开始脑子不太好选择了这么一个让自己难受的东西,首先蜜雪冰城的官网里面没有像星巴克,瑞幸一样有明确的门店信息,说明你不能通过上述方法直接获取准确无误的位置信息。所以,我当时想到就是百度地图,一看百度地图也有开发者平台,而且可以使用api迅速返回查询信息,就去申请了(学生申请百度地图的开发者很简单,只要申请的时候贴上你的学信网截图第二天就能过)。
注意注意注意
我最后的获取信息并没有通过百度地图开发者api,因为他存在着很大的局限性,通过百度地图开发者api,我获取上海的蜜雪冰城门店的时候,发现少的可伶,但是具体在百度地图上查找不是这样的,百度地图明显比通过百度地图开发者api获取的信息多。
(我大魔都明显不可能这么拉跨对吧)
因为百度地图的api存在一定滞后性,并且无关紧要的信息一般不会及时更新,故难以通过百度地图开发者api获取较为准确的信息,而且他有调用限制,如果你想通过他来实现获取蜜雪冰城全国信息,不是不可能,而是你需要做个数据表,还要做个验证机制,来判断每一个省的门店信息在上一次查询中是否完全打印出来。嘿嘿。在后来笔者也想通过百度地图的静态地图实现标点,但是百度地图给的api标点有数量限制,不能超过20多个(大概),笔者的能力也做不到下载地图后在上面标点,geo的map中国地图显示没问题,但是如果用省份地图总感觉太干净了(很神奇,居然能显示省份的),下面分别是百度地图api和geo库
import pandas as pd
import requests
from urllib.parse import quote
# 导入Excel数据
df = pd.read_excel("f:\\可视化课设\\星巴克门店-2023-12-28 -6947个门店.xlsx")
# 确保AK是有效的
ak = '自己的'
# 选择前十个门店
top_ten = df[df['city'] == '上海市'].head(100)
# 准备URL参数
url_params = {
'ak': ak,
'center': '121.4737,31.2304', # 上海市的中心点
'width': 800,
'height': 600,
'zoom': 10,
# 使用URL编码处理坐标中的逗号
'markers': '|'.join([quote(f"{row['longitude']},{row['latitude']}") for index, row in top_ten.iterrows()]),
'markerStyles': 's, ,0xFF0000' # 假设A代表透明度,如果API不支持,需要去除A
}
# 构建请求URL
url = f"https://api.map.baidu.com/staticimage/v2?{'&'.join([f'{k}={v}' for k, v in url_params.items()])}"
# 发送请求并保存图片
response = requests.get(url)
if response.status_code == 200:
with open('shanghai_starbucks_map.png', 'wb') as f:
f.write(response.content)
print('地图已保存。')
else:
print('请求失败,状态码:', response.status_code)
import pandas as pd
from pyecharts.charts import Geo
from pyecharts import options
from pyecharts.globals import GeoType
# 导入Excel数据
df = pd.read_excel("f:\\可视化课设\\星巴克门店-2023-12-28 -6947个门店.xlsx")
# 创建 Geo 图表
g = Geo().add_schema(maptype="上海")
# 给所有上海市的门店附上标签 'name',假设Excel文件中有门店名称的列名为 'name'
for index, row in df.iterrows():
if row['city'] == '上海市':
g.add_coordinate(row['name'], row['longitude'], row['latitude'])
# 给每个点的值赋为 1,仅包括上海市的门店
data_pair = [(row['name'], 1) for index, row in df.iterrows() if row['city'] == '上海市']
# 画图
g.add('', data_pair, type_=GeoType.EFFECT_SCATTER, symbol_size=2)
g.set_series_opts(label_opts=options.LabelOpts(is_show=False))
g.set_global_opts(title_opts=options.TitleOpts(title="星巴克上海市门店位置分布"))
# 保存结果到 HTML
result = g.render('shanghai_starbucks.html')
回到正题,还是说一下百度开发者的api调用,开发者文档
在文档中,我们可以看到地图检索的api调用案例,请求参数和返回参数,如果你仔细阅读,你会发现它所提供的地点检索里面,也有一个叫圆形区域检索的,做个api是不是和星巴克的api很像呢,对的。这里的代码给出的是百度地图调用的函数,注意,这个代码的请求参数里的output是非必须(文档里面看看吧),我设置为json是希望查询结果下载下来方便我看。
# 百度地图API搜索函数
def baidu_search(query, region, total_results=1000, page_size=20, ak=''):
url = 'http://api.map.baidu.com/place/v2/search'
output = 'json'
results = []
page_num = 0
# 循环直到获取到足够的结果
while len(results) < total_results:
params = {
'query': query,
'region': region,
'output': output,
'ak': ak,
'page_size': page_size,
'page_num': page_num
}
uri = f"{url}?{'&'.join([f'{k}={v}' for k, v in params.items()])}"
response = requests.get(uri)
response_dict = response.json()
# 检查API响应状态
if response_dict.get("status") != 0:
print(f"API调用失败: {response_dict.get('message')}")
break
page_results = response_dict.get("results", [])
if not page_results:
break # 如果没有更多结果,跳出循环
results.extend(page_results)
page_num += 1
return results
# 指定要搜索的地区和查询关键字
region = '上海'
query = '蜜雪冰城'
page_size = 20 # 每页结果数量
total_results = 1000 # 希望获取的总结果数量
# 请确保使用有效的API密钥
ak = '这里写自己的'
# 调用百度地图API搜索指定地区的门店
results = baidu_search(query, region, total_results, page_size, ak)
# 存储结果的列表
all_results = []
显然这些结果都不能让人满足,所以最后又回到了老路子,从网页爬取信息,我们打开百度地图的网页,打开开发者工具,如法炮制。
如何你就会发现这一次的响应信息和消息头,非常的抽象,而且,里面居然没有经纬度的信息,(当时的笔者已经emo了)那就退一步,看全国蜜雪冰城的城市分布数量吧。
找不到的同学可以搜索/?newmap,再看看每一个的具体响应,总有一个是你想要的。
3 、代码实现
首先给出蜜雪冰城的百度地图网页爬取的代码
import requests
import json
from openpyxl import Workbook
# 定义请求URL
url = 'https://map.baidu.com/?newmap=1&reqflag=pcmap&biz=1&from=webmap&from=webmap&da_par=direct&pcevaname=pc4.1&qt=s&da_src=searchBox.button&wd=蜜雪冰城&c=1&src=0&wd2=&pn=0&sug=0&l=5&b=(3742035.2134748735,5951323.878929981;19470675.213474873,8621915.87892998)&biz_forward={"scaler":1,"styles":"pl"}&sug_forward=&auth=549e6fF9QCSQe1I3BJd0yJNKae7cZ7B2uxNxTNEENRLtCMMASAyF7SvyuwyS8v7uvkGcuVtvvhguVtvyheuVtvCMGuVtvCQMuVtvIPcuVtvYvjuVtvZgMuVtv@vcuVtvc3CuVtvcPPuVtcvY1SGpuxEtHrQH2eGveh3uxtwiKDv7uvhgMuxVVtvrMhuVtGccZcuxtf0wd0vyOyUOFFOSM&seckey=dgMCS16K54jQZC7pIxNWd4doFpVCa6JvKs4Oj2syq3Y=,zmBZkuLrT8fboNKCG1Ws16Fi_qxLXEijmfOg5d9isdx6kLIrF-7pboPVUmaF6mcV6rWHdQOkkrZzhqEOzFDhvU_eeZ-YbGqcjj04X8zGU11tFzE-mXHIXRhoxqdjpt8ZlXcRz7At9iIGHPp0IHrgTM5fXScKoSeUtGDMnQhVv1jCRcf0mUDhBecZhmw55PZO&device_ratio=1&tn=B_NORMAL_MAP&nn=0&u_loc=12609277,4165687&ie=utf-8&t=1719744804755&newfrom=zhuzhan_webmap'
# 定义请求头
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
}
# 发起GET请求,包含请求头
response = requests.get(url, headers=headers)
# 尝试解析JSON数据
try:
# 获取JSON数据
data = response.json()
# 创建一个新的Excel工作簿
wb = Workbook()
# 激活当前的工作表
ws = wb.active
ws.title = "City Data" # 设置工作表的标题
# 写入表头
ws.append(["省份", "省份ID", "城市", "num"])
# 遍历more_city列表
for province_data in data["more_city"]:
province = province_data['province']
province_id = province_data['province_id']
# 遍历省份中的每个城市
for city in province_data["city"]:
city_name = city['name']
num = city['num']
# 写入每行数据到Excel
ws.append([province, province_id, city_name, num])
# 定义Excel文件路径
excel_file_path = r'C:\Users\13441\city_data.xlsx'
# 保存Excel文件
wb.save(excel_file_path)
print(f"数据已成功保存到 {excel_file_path}")
except ValueError:
# 捕获解析JSON失败的异常
print('Response content is not valid JSON')
except KeyError:
# 捕获,访问不存在的键的异常
print("Required key not found in JSON data")
这里获取信息得到的表格如下。
就这么简单,吗?当然不是,如果你是自己写的的爬虫并打印信息的时候你会发现,不通过json,而是代码普通的接受服务器发来的信息,其中的city值根本打印不出来,因为它的信息虽然是utf-8, 但是city值却是unicode!给自己敲代码实现的大伙提个醒,记得转码(这大概也是一种简单的反爬虫机制?就像密码本一样,内容是没办法改变的,你能改变编码,像字体和图像的编码)。
以下是星巴克爬虫。就是遍历发送经纬度获取数据(记得获得的数据要自己清理,比如查重,我把addressline1的前三个字作为省份列的数值了)
经纬度数据
import requests
import pandas as pd
import time
from datetime import datetime
from tqdm import tqdm
# 定义获取星巴克门店信息的函数
def starbucks(row):
url = "https://www.starbucks.com.cn/api/stores/nearby"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
}
params = {
"lat": row['lat'],
"lon": row['lon'],
"limit": "3000",
"locale": "ZH",
"features": "",
"radius": "200000",
}
try:
response = requests.get(url, headers=headers, params=params)
response.raise_for_status()
content_json = response.json()
# 将返回的JSON数据转换为DataFrame
df = pd.json_normalize(content_json.get('data', []), errors='ignore')
# 处理空值情况
if 'address.streetAddressLine1' in df.columns:
df['province'] = df['address.streetAddressLine1'].apply(lambda x: x[:3] if isinstance(x, str) else '')
else:
df['province'] = ''
return df
except requests.exceptions.RequestException as e:
print(f"Error fetching data for coordinates ({row['lon']}, {row['lat']}): {e}")
return pd.DataFrame()
if __name__ == '__main__':
try:
df = pd.read_excel("./省市区县经纬度.xlsx")
print("Excel file loaded successfully.")
print("DataFrame shape:", df.shape)
if 'lon' not in df.columns or 'lat' not in df.columns:
raise ValueError("Excel file must contain 'lon' and 'lat' columns.")
except Exception as e:
print(f"Error loading Excel file: {e}")
df = pd.DataFrame()
if not df.empty:
# 使用 tqdm 显示进度条
with tqdm(total=df.shape[0]) as pbar:
results = []
for index, row in df.iterrows():
# 应用 starbucks 函数并获取结果
result_df = starbucks(row)
if not result_df.empty:
result_df['source_index'] = index # 添加原始索引作为参考
results.append(result_df)
pbar.update(1) # 更新进度条
# 将结果列表转换为一个DataFrame
content_list = pd.concat(results, ignore_index=True)
# 处理空值情况,如果 'address.streetAddressLine1' 为空,设置 'province' 列为 NaN
content_list['province'].fillna(value=pd.NA, inplace=True)
# 保存最终的DataFrame到Excel文件
curr_time = datetime.now()
timestamp = datetime.strftime(curr_time, '%Y-%m-%d %H-%M-%S')
content_list.to_excel(f"星巴克门店-{timestamp}.xlsx", index=False)
print("Excel file saved successfully.")
# 输出最终DataFrame的形状
print(f"Final DataFrame shape: {content_list.shape}")
else:
print("DataFrame is empty. Please check your Excel file.")
以下是数据
三、可视化
1、独自实现
pyecharts可以看看这个pyecharts这个文章里面也有文档的连接。
其实就是bar,line等方法调用,还有可视化效果,下面的代码都是一个图一个html,当然也可以实现放在一个html里面。
这是数据
import pandas as pd
from pyecharts.charts import Bar, Line, Grid
from pyecharts import options as opts
from pyecharts.globals import ThemeType
try:
# 读取数据
df = pd.read_excel("c:\Users\13441\星巴克门店-2024-07-13 13-09-13.xlsx")
ddf = pd.read_excel("f:\\可视化课设\\31省份2023GDP.xlsx")
except FileNotFoundError as e:
print(f"File not found: {e}")
exit()
# 数据清理
df.dropna(subset=["province"], inplace=True)
ddf.dropna(subset=["省份", "GDP(百亿元)", "人均可支配收入(元)"], inplace=True)
# 统计频率并排序
freq = df["province"].value_counts().sort_values(ascending=True)
province_list = freq.index.tolist()
count_list = freq.tolist()
# 确保GDP数据的省份名称与门店数据的省份名称匹配
ddf.rename(columns={'省份': 'province'}, inplace=True)
gdp_dict = ddf.set_index('province')['GDP(百亿元)'].to_dict()
income_dict = ddf.set_index('province')['人均可支配收入(元)'].to_dict()
# 根据省份列表生成GDP和人均可支配收入列表
gdp_list = [gdp_dict.get(province) for province in province_list if gdp_dict.get(province) is not None]
income_list = [income_dict.get(province) for province in province_list if income_dict.get(province) is not None]
# 计算GDP和人均可支配收入数据的实际最大值,用于设置Y轴的范围
gdp_max = max(gdp_list) if gdp_list else 1350 # 如果gdp_list为空,则使用1350作为最大值
income_max = max(income_list) if income_list else 100000 # 如果income_list为空,则使用100000作为最大值
# 创建各省星巴克门店数量的柱状图
bar_stores = (
Bar(init_opts=opts.InitOpts(theme=ThemeType.LIGHT, width='1200px', height='800px'))
.add_xaxis(province_list)
.add_yaxis("各省星巴克门店数量", count_list, label_opts=opts.LabelOpts(position="top"),
itemstyle_opts=opts.ItemStyleOpts(color="#008000",opacity=0.6)) # 设置柱状图透明度为0.6
.set_global_opts(title_opts=opts.TitleOpts(title="各省星巴克门店数量", subtitle="按省统计"),
xaxis_opts=opts.AxisOpts(name_rotate=60, axislabel_opts={"rotate": 45}),
yaxis_opts=opts.AxisOpts(name="星巴克门店数量"))
)
bar_stores.render("各省星巴克门店数量.html")
# 创建包含星巴克门店数量和人均可支配收入的混合图
bar_stores_with_income = (
Bar(init_opts=opts.InitOpts(theme=ThemeType.LIGHT, width='1200px', height='800px'))
.add_xaxis(province_list)
.add_yaxis("各省星巴克门店数量", count_list, label_opts=opts.LabelOpts(position="top"),
itemstyle_opts=opts.ItemStyleOpts(color="#008000",opacity=0.6)) # 设置柱状图透明度为0.6
)
line_income = (
Line()
.add_xaxis(province_list)
.add_yaxis("人均可支配收入(元)", income_list, yaxis_index=1,
label_opts=opts.LabelOpts(is_show=True), # 设置为显示结点数字
linestyle_opts=opts.LineStyleOpts(color="purple"), # 设置折线图颜色为紫色
itemstyle_opts=opts.ItemStyleOpts(color="purple")) # 设置点的颜色为紫色
.set_global_opts(
yaxis_opts=opts.AxisOpts(
name="人均可支配收入(元)",
is_scale=True,
max_=income_max,
min_=0, # 设置最小值为0
interval=income_max // 10 if income_max > 0 else 1 # 防止除以0
)
)
)
bar_stores_with_income.extend_axis(
yaxis=opts.AxisOpts(name="人均可支配收入(元)", type_="value", position="right", offset=20)
).overlap(line_income)
bar_stores_with_income.set_global_opts(
title_opts=opts.TitleOpts(title="各省星巴克门店数量和人均可支配收入", subtitle="按省统计"),
xaxis_opts=opts.AxisOpts(name_rotate=60, axislabel_opts={"rotate": 45}),
legend_opts=opts.LegendOpts(pos_top="5%")
)
bar_stores_with_income.render("各省星巴克门店数量和人均可支配收入.html")
# 创建包含星巴克门店数量和GDP的混合图
bar_stores_with_gdp = (
Bar(init_opts=opts.InitOpts(theme=ThemeType.LIGHT, width='1200px', height='800px'))
.add_xaxis(province_list)
.add_yaxis("各省星巴克门店数量", count_list, label_opts=opts.LabelOpts(position="top"),
itemstyle_opts=opts.ItemStyleOpts(color="#008000",opacity=0.6)) # 设置柱状图透明度为0.6
)
line_gdp = (
Line()
.add_xaxis(province_list)
.add_yaxis("GDP(百亿元)", gdp_list, yaxis_index=1,
label_opts=opts.LabelOpts(is_show=True), # 设置为显示结点数字
linestyle_opts=opts.LineStyleOpts(color="orange"), # 设置折线图颜色为橙色
itemstyle_opts=opts.ItemStyleOpts(color="orange")) # 设置点的颜色为橙色
.set_global_opts(
yaxis_opts=opts.AxisOpts(
name="GDP(百亿元)",
is_scale=True,
max_=gdp_max,
min_=0, # 设置最小值为0
interval=gdp_max // 10 if gdp_max > 0 else 1 # 防止除以0
)
)
)
bar_stores_with_gdp.extend_axis(
yaxis=opts.AxisOpts(name="GDP(百亿元)", type_="value", position="right", offset=20)
).overlap(line_gdp)
bar_stores_with_gdp.set_global_opts(
title_opts=opts.TitleOpts(title="各省星巴克门店数量和GDP", subtitle="按省统计"),
xaxis_opts=opts.AxisOpts(name_rotate=60, axislabel_opts={"rotate": 45}),
legend_opts=opts.LegendOpts(pos_top="5%")
)
bar_stores_with_gdp.render("各省星巴克门店数量和GDP.html")
import pandas as pd
from pyecharts.charts import Bar, Line,Grid
from pyecharts import options as opts
from pyecharts.globals import ThemeType
# 尝试读取数据
try:
df = pd.read_excel("C:\\Users\\13441\\city_data.xlsx")
ddf = pd.read_excel("f:\\可视化课设\\31省份2023GDP.xlsx")
except FileNotFoundError as e:
print(f"File not found: {e}")
exit()
# 数据清理
df.dropna(subset=["省份", "num"], inplace=True)
ddf.dropna(subset=["省份", "GDP(百亿元)", "人均可支配收入(元)"], inplace=True)
# 通过省份ID分组并计算每个省份的总数
df_grouped = df.groupby('省份')['num'].sum().reset_index()
df_grouped.rename(columns={'num': '省份总数'}, inplace=True)
# 确保GDP数据的省份名称与门店数据的省份名称匹配
ddf.rename(columns={'省份': '省份ID'}, inplace=True)
gdp_dict = ddf.set_index('省份ID')['GDP(百亿元)'].to_dict()
income_dict = ddf.set_index('省份ID')['人均可支配收入(元)'].to_dict()
# 对省份总数进行排序
df_grouped_sorted = df_grouped.sort_values(by='省份总数', ascending=True)
# 提取省份列表和各自的门店总数、GDP数据
province_list = df_grouped_sorted['省份'].tolist()
count_list = df_grouped_sorted['省份总数'].tolist()
gdp_list = [gdp_dict.get(province, 0) for province in province_list] # 处理缺失值,设为0或其他默认值
income_list = [income_dict.get(province, 0) for province in province_list]
# 计算GDP和人均可支配收入数据的实际最大值,用于设置Y轴的范围
gdp_max = max(gdp_list) if gdp_list else 1350 # 如果gdp_list为空,则使用1350作为最大值
income_max = max(income_list) if income_list else 100000 # 如果income_list为空,则使用100000作为最大值
# 创建各省蜜雪冰城门店数量的柱状图
bar_stores = (
Bar(init_opts=opts.InitOpts(theme=ThemeType.LIGHT, width='1200px', height='800px'))
.add_xaxis(province_list)
.add_yaxis("各省蜜雪冰城门店数量", count_list, label_opts=opts.LabelOpts(position="top"),
itemstyle_opts=opts.ItemStyleOpts(color="#FFC0CB",opacity=0.6)) # 设置柱状图透明度为0.6
.set_global_opts(title_opts=opts.TitleOpts(title="各省蜜雪冰城门店数量", subtitle="按省统计"),
xaxis_opts=opts.AxisOpts(name_rotate=60, axislabel_opts={"rotate": 45}),
yaxis_opts=opts.AxisOpts(name="蜜雪冰城门店数量"))
)
bar_stores.render("各省蜜雪冰城门店数量.html")
# 创建包含蜜雪冰城门店数量和人均可支配收入的混合图
bar_stores_with_income = (
Bar(init_opts=opts.InitOpts(theme=ThemeType.LIGHT, width='1200px', height='800px'))
.add_xaxis(province_list)
.add_yaxis("各省蜜雪冰城门店数量", count_list, label_opts=opts.LabelOpts(position="top"),
itemstyle_opts=opts.ItemStyleOpts(color="#FFC0CB",opacity=0.6)) # 设置柱状图透明度为0.6
)
line_income = (
Line()
.add_xaxis(province_list)
.add_yaxis("人均可支配收入(元)", income_list, yaxis_index=1,
label_opts=opts.LabelOpts(is_show=True), # 设置为显示结点数字
linestyle_opts=opts.LineStyleOpts(color="purple"), # 设置折线图颜色为紫色
itemstyle_opts=opts.ItemStyleOpts(color="purple")) # 设置点的颜色为紫色
.set_global_opts(
yaxis_opts=opts.AxisOpts(
name="人均可支配收入(元)",
is_scale=True,
max_=income_max,
min_=0, # 设置最小值为0
interval=income_max // 10 if income_max > 0 else 1 # 防止除以0
)
)
)
bar_stores_with_income.extend_axis(
yaxis=opts.AxisOpts(name="人均可支配收入(元)", type_="value", position="right", offset=20)
).overlap(line_income)
bar_stores_with_income.set_global_opts(
title_opts=opts.TitleOpts(title="各省蜜雪冰城门店数量和人均可支配收入", subtitle="按省统计"),
xaxis_opts=opts.AxisOpts(name_rotate=60, axislabel_opts={"rotate": 45}),
legend_opts=opts.LegendOpts(pos_top="5%")
)
bar_stores_with_income.render("各省蜜雪冰城门店数量和人均可支配收入.html")
# 创建包含蜜雪冰城门店数量和GDP的混合图
bar_stores_with_gdp = (
Bar(init_opts=opts.InitOpts(theme=ThemeType.LIGHT, width='1200px', height='800px'))
.add_xaxis(province_list)
.add_yaxis("各省蜜雪冰城门店数量", count_list, label_opts=opts.LabelOpts(position="top"),
itemstyle_opts=opts.ItemStyleOpts(color="#FFC0CB",opacity=0.6)) # 设置柱状图透明度为0.6
)
line_gdp = (
Line()
.add_xaxis(province_list)
.add_yaxis("GDP(百亿元)", gdp_list, yaxis_index=1,
label_opts=opts.LabelOpts(is_show=True), # 设置为显示结点数字
linestyle_opts=opts.LineStyleOpts(color="orange"), # 设置折线图颜色为橙色
itemstyle_opts=opts.ItemStyleOpts(color="orange")) # 设置点的颜色为橙色
.set_global_opts(
yaxis_opts=opts.AxisOpts(
name="GDP(百亿元)",
is_scale=True,
max_=gdp_max,
min_=0, # 设置最小值为0
interval=gdp_max // 10 if gdp_max > 0 else 1 # 防止除以0
)
)
)
bar_stores_with_gdp.extend_axis(
yaxis=opts.AxisOpts(name="GDP(百亿元)", type_="value", position="right", offset=20)
).overlap(line_gdp)
bar_stores_with_gdp.set_global_opts(
title_opts=opts.TitleOpts(title="各省蜜雪冰城门店数量和GDP", subtitle="按省统计"),
xaxis_opts=opts.AxisOpts(name_rotate=60, axislabel_opts={"rotate": 45}),
legend_opts=opts.LegendOpts(pos_top="5%")
)
bar_stores_with_gdp.render("各省蜜雪冰城门店数量和GDP.html")
import pandas as pd
from pyecharts.charts import Bar, Line, Grid
from pyecharts import options as opts
from pyecharts.globals import ThemeType
# 尝试读取数据
try:
df_mxbc = pd.read_excel("C:\\Users\\13441\\city_data.xlsx")
df_sbs = pd.read_excel("f:\\可视化课设\\星巴克门店-2023-12-28 -6947个门店.xlsx")
ddf = pd.read_excel("f:\\可视化课设\\31省份2023GDP.xlsx")
except FileNotFoundError as e:
print(f"File not found: {e}")
exit()
# 数据清理
df_mxbc.dropna(subset=["省份", "num"], inplace=True)
df_sbs.dropna(subset=["province"], inplace=True)
ddf.dropna(subset=["省份", "GDP(百亿元)", "人均可支配收入(元)"], inplace=True)
# 计算蜜雪冰城门店数量和星巴克门店数量
df_mxbc_grouped = df_mxbc.groupby('省份')['num'].sum().reset_index()
df_mxbc_grouped.rename(columns={'num': '蜜雪冰城门店数量'}, inplace=True)
sbs_count = df_sbs["province"].value_counts().sort_index() # 根据省份统计星巴克门店数量
# 确保GDP数据的省份名称与门店数据的省份名称匹配
ddf.rename(columns={'省份': '省份ID'}, inplace=True)
gdp_dict = ddf.set_index('省份ID')['GDP(百亿元)'].to_dict()
# 提取省份列表和各自的门店总数、GDP数据
province_list = df_mxbc_grouped['省份'].tolist()
mxbc_count_list = df_mxbc_grouped['蜜雪冰城门店数量'].tolist()
sbs_count_list = sbs_count.reindex(province_list).fillna(0).tolist() # 对齐数据,缺失值用0填充
gdp_list = [gdp_dict.get(province, 0) for province in province_list] # 处理缺失值,设为0
# 创建柱状图对象
bar = (
Bar(init_opts=opts.InitOpts(theme=ThemeType.LIGHT))
.add_xaxis(province_list)
.add_yaxis("蜜雪冰城门店数量", mxbc_count_list)
.add_yaxis("星巴克门店数量", sbs_count_list, itemstyle_opts=opts.ItemStyleOpts(color="#008000")) # 设置颜色
.set_global_opts(
title_opts=opts.TitleOpts(title="星巴克与蜜雪冰城门店数量对比"),
xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=45, interval=0)),
)
.set_series_opts(
label_opts=opts.LabelOpts(is_show=False),
bar_width='30%', # 调整柱体宽度
gap='20%', # 调整柱体间距
)
)
# 渲染图表到HTML文件中
bar.render("门店数量对比柱状图.html")
2、其他方法
这些是别人的实现(微信公众号叫程序员coding)
import pandas as pd
from pyecharts.charts import Bar
from pyecharts.charts import Pie
from pyecharts.charts import Line
from pyecharts.charts import Funnel
from pyecharts.charts import Geo
from pyecharts.globals import ChartType
from pyecharts.charts import BMap
from pyecharts.globals import BMapType
from pyecharts import options as opts
from pyecharts.globals import ThemeType
try:
# 读取数据
df = pd.read_excel("f:\\可视化课设\\星巴克门店-2023-12-28 -6947个门店.xlsx")
ddf = pd.read_excel("f:\\可视化课设\\31省份2023GDP.xlsx")
except FileNotFoundError as e:
print(f"File not found: {e}")
exit()
# 数据清理
df.dropna(subset=["province"], inplace=True)
ddf.dropna(subset=["省份", "GDP(百亿元)"], inplace=True)
# 统计频率并排序
freq = df["province"].value_counts().sort_values(ascending=True)
province_list = freq.index.tolist()
count_list = freq.tolist()
# 确保GDP数据的省份名称与门店数据的省份名称匹配
ddf.rename(columns={'省份': 'province'}, inplace=True)
gdp_dict = ddf.set_index('province')['GDP(百亿元)'].to_dict()
# 根据省份列表生成GDP列表,并记录每个省份在柱形图中的位置
gdp_list = [] # 存储GDP数据的列表
province_position = {} # 用于记录省份在柱形图中的位置
for i, province in enumerate(province_list):
gdp = gdp_dict.get(province) # 尝试获取GDP数据
if gdp is not None: # 检查GDP数据是否成功获取
gdp_list.append(gdp) # 添加GDP数据到列表
province_position[province] = i # 记录省份位置
else:
print(f"Warning: GDP data for {province} not found.") # 打印警告信息
# 计算GDP数据的实际最大值,用于设置Y轴的范围
gdp_max = max(gdp_list) if gdp_list else 1350 # 如果gdp_list为空,则使用1350作为最大值
# 热力图
heatmap = (
BMap(is_ignore_nonexistent_coord=True, # 忽略不存在的坐标
init_opts=opts.InitOpts(width='1800px', height='850px', page_title="各省星巴克门店数量热力图"))
.add_schema(baidu_ak="百度地图开放平台申请AK", center=[120.13066322374, 30.240018034923],
zoom=5, # 当前视角的缩放比例
is_roam=True # 是否开启鼠标缩放和平移漫游
)
.add(
"热力图", # 图例
data_pair=[list(data) for data in zip(city_list, city_count_list)],
type_="heatmap",
label_opts=opts.LabelOpts(formatter="{b}"),
)
.set_global_opts(
title_opts=opts.TitleOpts(title="各省星巴克门店数量热力图",
pos_left='center',
title_textstyle_opts=opts.TextStyleOpts(font_size=32)
),
legend_opts=opts.LegendOpts(pos_right='20%'),
visualmap_opts=opts.VisualMapOpts(pos_left='5%', pos_bottom='10%')
)
.add_control_panel(
copyright_control_opts=opts.BMapCopyrightTypeOpts(position=3),
maptype_control_opts=opts.BMapTypeControlOpts(
type_=BMapType.MAPTYPE_CONTROL_DROPDOWN
),
scale_control_opts=opts.BMapScaleControlOpts(),
overview_map_opts=opts.BMapOverviewMapControlOpts(is_open=True),
navigation_control_opts=opts.BMapNavigationControlOpts(),
geo_location_control_opts=opts.BMapGeoLocationControlOpts(),
)
.render("各省星巴克门店数量热力图.html")
)
# 漏斗图
funnel = (
Funnel(init_opts=opts.InitOpts(theme=ThemeType.LIGHT,
width='1000px', height='600px',
page_title="各省星巴克门店数量"))
.add("各省星巴克门店数量", [list(data) for data in zip(province_list, count_list)])
.set_global_opts(
title_opts=opts.TitleOpts(title="各省星巴克门店数量", subtitle="按省统计", pos_left="center"),
legend_opts=opts.LegendOpts(is_show=False, pos_left="right", orient="vertical")
)
.render("各省星巴克门店数量-漏斗图.html")
)
# 饼图
pie = (
# 初始化配置项,参考 `global_options.InitOpts`
Pie(init_opts=opts.InitOpts(theme=ThemeType.LIGHT,
width='1000px', height='600px',
page_title="各省星巴克门店数量"))
.add(series_name="各省星巴克门店数量", # 系列名称
data_pair=[list(data) for data in zip(province_list, count_list)], # 馈入数据
radius="35%", # 饼图半径比例
center=["50%", "50%"], # 饼图中心坐标
label_opts=opts.LabelOpts(is_show=False, position="center"), # 标签位置
)
.set_global_opts(title_opts=opts.TitleOpts(title="各省星巴克门店数量", subtitle="按省统计", pos_left="center"),
legend_opts=opts.LegendOpts(is_show=True, pos_left="right", orient="vertical"))
.set_series_opts(label_opts=opts.LabelOpts(formatter="{b}:{c} {d}%")) # {d}%为百分比
.render("各省星巴克门店数量-饼图.html")
)
from pyecharts.charts import Geo
from pyecharts.globals import ChartType
# geo_heatmap
geo_heatmap = (
Geo( init_opts=opts.InitOpts(width='1800px', height='850px', page_title="各省星巴克门店数量热力图"))
.add_schema(maptype="china")
.add(
"geo",
[list(data) for data in zip(province_list, count_list)],
type_=ChartType.HEATMAP,
)
.set_series_opts(label_opts=opts.LabelOpts(is_show=False))
.set_global_opts(
visualmap_opts=opts.VisualMapOpts(),
title_opts=opts.TitleOpts(title="Geo-HeatMap"),
)
.render("geo_heatmap.html")
)
四、最终效果(部分图表)