这是我我的代码,我想要实现通过折线图的legendopts,也就是折线图的图例配置项来同时控制玫瑰图,从而实现图表之间的交互,请给我一个合理的解决方案,并且给我完整代码from pyecharts.charts import Pie, Timeline, Line, Grid, Page
from pyecharts import options as opts
from pyecharts.commons.utils import JsCode
from pyecharts.components import Table
import pandas as pd
# 读取 CSV 数据
df = pd.read_csv("年度数据.csv")
# 获取指标和年份
indicators = df['指标'].unique()
years = sorted(df.columns[1:].tolist()) # 对年份进行排序
# 创建一个字典来存储清洗后的数据
cleaned_data = {'年份': years}
# 处理每个教育程度和性别
for indicator in indicators:
if "人口数" not in indicator:
continue
# 提取教育程度和性别
if "男性" in indicator:
education_level = indicator.replace('6岁及6岁以上', '').replace('男性人口数(人口抽样调查)(人)', '').strip()
gender = '男性'
elif "女性" in indicator:
education_level = indicator.replace('6岁及6岁以上', '').replace('女性人口数(人口抽样调查)(人)', '').strip()
gender = '女性'
else:
continue # 跳过不相关的指标
# 获取数据并排序
data = df[df['指标'] == indicator].values[0][1:].tolist()
# 根据排序后的年份重新排列数据
sorted_data = [x for _, x in sorted(zip(df.columns[1:], data))]
# 存储数据
cleaned_data[f"{gender}_{education_level}"] = sorted_data
# 将清洗后的数据转换为 DataFrame
cleaned_df = pd.DataFrame(cleaned_data)
# 修改颜色生成函数 - 男性(蓝色系跨度更大)
def generate_blue_colors(n):
colors = []
# 从浅蓝到深蓝的更大跨度渐变
r_values = [200, 150, 100, 50, 0] # 红色分量递减
g_values = [230, 180, 130, 80, 30] # 绿色分量递减
b_values = [255, 255, 255, 255, 255] # 蓝色保持高值
for i in range(n):
# 确保索引不越界
idx = min(i, len(r_values) - 1)
color = f"rgb({r_values[idx]},{g_values[idx]},{b_values[idx]})"
colors.append(color)
return colors
# 修改颜色生成函数 - 女性(以深粉色为基准进行渐变)
def generate_soft_pink_colors(n):
colors = []
# 以 #C2185B (194,24,91) 为基准的渐变
base_r, base_g, base_b = 194, 24, 91
# 生成从浅粉到深粉的更大跨度渐变
for i in range(n):
# 计算渐变因子 (0到1之间)
factor = i / (n - 1) if n > 1 else 0
# 从浅色 (接近白色) 到深粉色渐变
r = int(base_r * factor + 255 * (1 - factor))
g = int(base_g * factor + 220 * (1 - factor))
b = int(base_b * factor + 230 * (1 - factor))
color = f"rgb({r},{g},{b})"
colors.append(color)
return colors
# 创建玫瑰图
def create_pie_chart(year_index, years, cleaned_df):
current_year = years[year_index]
year_data = cleaned_df.iloc[year_index]
# 教育程度列表
education_levels = ['未上过学', '小学', '初中', '高中', '大专及以上']
# 计算各教育程度人口数
total_male = 0
total_female = 0
male_education_counts = []
female_education_counts = []
for level in education_levels:
male_col = f"男性_{level}"
female_col = f"女性_{level}"
if male_col in cleaned_df.columns:
male_count = year_data[male_col]
male_education_counts.append(male_count)
total_male += male_count
if female_col in cleaned_df.columns:
female_count = year_data[female_col]
female_education_counts.append(female_count)
total_female += female_count
# 计算占比,保留两位小数
if total_male != 0:
male_percentages = [round(count / total_male * 100, 2) for count in male_education_counts]
else:
male_percentages = [0] * len(education_levels)
if total_female != 0:
female_percentages = [round(count / total_female * 100, 2) for count in female_education_counts]
else:
female_percentages = [0] * len(education_levels)
# 生成蓝色系和浅粉色系颜色列表
num_colors = len(education_levels)
blue_colors = generate_blue_colors(num_colors)
pink_colors = generate_soft_pink_colors(num_colors)
# 创建玫瑰图
pie = (
# Pie(init_opts=opts.InitOpts(width='800px', height='600px', bg_color='white'))
# Pie(init_opts=opts.InitOpts())
Pie(init_opts=opts.InitOpts(width='100%', height='100%', bg_color='white'))
)
# 添加男性系列并设置蓝色系颜色
pie.add(
series_name="男性教育程度占比",
data_pair=[(education_levels[i], male_percentages[i]) for i in range(len(education_levels))],
radius=["30%", "55%"],
center=["30%", "50%"],
rosetype="radius",
label_opts=opts.LabelOpts(formatter="{b}: {c}%", position="outside"),
itemstyle_opts=opts.ItemStyleOpts(color=JsCode(
"""function(params) {
var colors = %s;
return colors[params.dataIndex];
}""" % blue_colors
))
)
# 添加女性系列并设置浅粉色系颜色
pie.add(
series_name="女性教育程度占比",
data_pair=[(education_levels[i], female_percentages[i]) for i in range(len(education_levels))],
radius=["30%", "55%"],
center=["70%", "50%"],
rosetype="radius",
label_opts=opts.LabelOpts(formatter="{b}: {c}%", position="outside"),
itemstyle_opts=opts.ItemStyleOpts(color=JsCode(
"""function(params) {
var colors = %s;
return colors[params.dataIndex];
}""" % pink_colors
))
)
# 设置提示框样式 - 与折线图相似
tooltip_style = {
"trigger": "item",
"border_width": 2,
"border_color": "#FFEB3B", # 亮黄色边框
"background_color": "rgba(255, 255, 255, 0.8)", # 半透明白色背景
"textstyle_opts": opts.TextStyleOpts(color="black", font_size=12) # 黑色文字
}
# 设置全局选项
pie.set_global_opts(
title_opts=opts.TitleOpts(
title=f"{current_year}教育程度占比(男左女右)",
pos_left='center',
pos_top='20',
title_textstyle_opts=opts.TextStyleOpts(color='black', font_size=16)
),
legend_opts=opts.LegendOpts(is_show=False),
tooltip_opts=opts.TooltipOpts(**tooltip_style) # 应用提示框样式
)
# 设置系列选项
pie.set_series_opts(
label_opts=opts.LabelOpts(formatter="{b}: {c}%")
)
return pie
# 创建时间轴 - 玫瑰图
timeline_pie = Timeline()
timeline_pie.add_schema(
play_interval=1000,
is_auto_play=True,
is_loop_play=False,
)
# 为每个年份生成玫瑰图并添加到时间轴
for i in range(len(years)):
pie_chart = create_pie_chart(i, years, cleaned_df)
timeline_pie.add(pie_chart, str(years[i]))
# 准备绘图数据
male_education = {}
female_education = {}
# 提取教育程度数据
for col in cleaned_df.columns:
if col == '年份':
continue
gender, education_level = col.split('_', 1)
if gender == '男性':
if education_level != "": # 不要提取总人口数
male_education[education_level] = cleaned_df[col].values.tolist()
elif gender == '女性':
if education_level != "": # 不要提取总人口数
female_education[education_level] = cleaned_df[col].values.tolist()
# 设置颜色主题
# 男性颜色主题(蓝色系)
male_colors = [
"#1E88E5", # 蓝色
"#0D47A1", # 深蓝色
"#42A5F5", # 浅蓝色
"#64B5F6", # 更浅的蓝色
"#90CAF9" # 最浅的蓝色
]
# 女性颜色主题(粉色系)
female_colors = [
"#EC407A", # 粉红色
"#C2185B", # 深粉色
"#F48FB1", # 浅粉色
"#F8BBD0", # 更浅的粉色
"#FFCDD2" # 最浅的粉色
]
# 创建一个函数来生成单个年份的图表组合
def create_combined_chart(year_index, years, male_education, female_education):
current_year = years[year_index]
start = max(0, year_index - 4)
end = year_index + 1
current_years = years[start:end]
# 设置男性提示框样式 - 亮黄色边框
male_tooltip_style = {
"trigger": "axis",
"border_width": 2,
"border_color": "#FFEB3B", # 亮黄色边框
"background_color": "rgba(255, 255, 255, 0.8)", # 背景颜色和透明度
"textstyle_opts": opts.TextStyleOpts(color="black", font_size=12) # 文本样式
}
# 设置女性提示框样式 - 亮粉色边框
female_tooltip_style = {
"trigger": "axis",
"border_width": 2,
"border_color": "#FF4081", # 亮粉色边框
"background_color": "rgba(255, 255, 255, 0.8)", # 背景颜色和透明度
"textstyle_opts": opts.TextStyleOpts(color="black", font_size=12) # 文本样式
}
# 创建男性教育程度折线图
male_line = (
Line()
.add_xaxis(current_years)
.set_global_opts(
legend_opts=opts.LegendOpts(pos_top="2%", pos_left="center"),
tooltip_opts=opts.TooltipOpts(**male_tooltip_style),
yaxis_opts=opts.AxisOpts(name="人口数"),
title_opts=opts.TitleOpts(title="男性人口受教育程度", pos_left="left")
)
)
# 为男性教育程度设置不同的颜色
for index, (level, data) in enumerate(male_education.items()):
current_data = data[start:end]
male_line.add_yaxis(
series_name=level,
y_axis=current_data,
is_symbol_show=True,
is_smooth=True,
stack="stack1",
itemstyle_opts=opts.ItemStyleOpts(color=male_colors[index % len(male_colors)]), # 设置线条颜色
areastyle_opts=opts.AreaStyleOpts(
color=male_colors[index % len(male_colors)],
opacity=0.5
),
label_opts=opts.LabelOpts(is_show=False), # 隐藏教育程度线条上的标签
)
# 添加男性人口总数线条,修改样式使其更有趣
if '男性_' in cleaned_df.columns:
male_total = cleaned_df['男性_'].values.tolist() # 直接从清洗后的数据中获取
current_male_total = male_total[start:end]
male_line.add_yaxis(
series_name="男性人口",
y_axis=current_male_total,
is_symbol_show=True,
symbol="circle", # 使用圆形标记点
symbol_size=10, # 增大标记点大小
is_smooth=True,
linestyle_opts=opts.LineStyleOpts(
width=4, # 增加线条宽度
type_="dashed", # 使用虚线样式
color="#efb6d3", # 设置线条颜色为粉色
),
label_opts=opts.LabelOpts(
is_show=False, # 隐藏平滑折线图上的标签
),
itemstyle_opts=opts.ItemStyleOpts(
color="rgba(239, 182, 211, 0.8)", # 修改气泡颜色为半透明的粉色
border_color="#efb6d3", # 设置气泡边框颜色为粉色
border_width=2 # 设置气泡边框宽度
),
markpoint_opts=opts.MarkPointOpts( # 添加特殊标记点
data=[
opts.MarkPointItem(type_="max", name="最大值"),
opts.MarkPointItem(type_="min", name="最小值")
],
label_opts=opts.LabelOpts(
font_size=14, # 增大文字大小
color="black", # 设置文字颜色
background_color="rgba(255, 255, 255, 0.8)", # 设置文字背景颜色,
)
),
markline_opts=opts.MarkLineOpts( # 添加趋势线
data=[opts.MarkLineItem(type_="average", name="平均值")],
label_opts=opts.LabelOpts(
font_size=14, # 增大文字大小
color="black", # 设置文字颜色
background_color="rgba(255, 255, 255, 0.8)", # 设置文字背景颜色,
# formatter=lambda x: "{:.0f}".format(x)
)
),
z=5,
)
# 创建女性教育程度折线图
female_line = (
Line()
.add_xaxis(current_years)
.set_global_opts(
legend_opts=opts.LegendOpts(pos_top="50%", pos_left="center"),
tooltip_opts=opts.TooltipOpts(**female_tooltip_style),
yaxis_opts=opts.AxisOpts(name="人口数"),
title_opts=opts.TitleOpts(title="女性人口受教育程度", pos_left="left", pos_top="50%")
)
)
# 为女性教育程度设置不同的颜色
for index, (level, data) in enumerate(female_education.items()):
current_data = data[start:end]
female_line.add_yaxis(
series_name=level,
y_axis=current_data,
is_symbol_show=True,
is_smooth=True,
stack="stack2",
itemstyle_opts=opts.ItemStyleOpts(color=female_colors[index % len(female_colors)]), # 使用女性颜色主题
areastyle_opts=opts.AreaStyleOpts(
color=female_colors[index % len(female_colors)], # 使用女性颜色主题
opacity=0.5
),
label_opts=opts.LabelOpts(is_show=False), # 隐藏教育程度线条上的标签
)
# 添加女性人口总数线条,修改样式使其更有趣
if '女性_' in cleaned_df.columns:
female_total = cleaned_df['女性_'].values.tolist() # 直接从清洗后的数据中获取
current_female_total = female_total[start:end]
female_line.add_yaxis(
series_name="女性人口",
y_axis=current_female_total,
is_symbol_show=True,
symbol="diamond", # 使用菱形标记点
symbol_size=10, # 增大标记点大小
is_smooth=True,
linestyle_opts=opts.LineStyleOpts(
width=4, # 增加线条宽度
type_="dotted", # 使用点划线样式
color="#2196F3", # 更鲜艳的颜色
),
label_opts=opts.LabelOpts(
is_show=False, # 隐藏平滑折线图上的标签
),
itemstyle_opts=opts.ItemStyleOpts(
color="rgba(33, 150, 243, 0.8)", # 修改气泡颜色为半透明的蓝色
border_color="#2196F3", # 设置气泡边框颜色
border_width=2 # 设置气泡边框宽度
),
markpoint_opts=opts.MarkPointOpts( # 添加特殊标记点
data=[
opts.MarkPointItem(type_="max", name="最大值"),
opts.MarkPointItem(type_="min", name="最小值")
],
label_opts=opts.LabelOpts(
font_size=14, # 增大文字大小
color="black", # 设置文字颜色
background_color="rgba(255, 255, 255, 0.8)", # 设置文字背景颜色,
)
),
markline_opts=opts.MarkLineOpts( # 添加趋势线
data=[opts.MarkLineItem(type_="average", name="平均值")],
label_opts=opts.LabelOpts(
font_size=14, # 增大文字大小
color="black", # 设置文字颜色
background_color="rgba(255, 255, 255, 0.8)", # 设置文字背景颜色,
# formatter=lambda x: "{:.0f}".format(x)
)
),
z=5,
)
# 将两个图表组合在一起,并调整它们之间的距离
grid = (
Grid()
.add(male_line, grid_opts=opts.GridOpts(pos_bottom="60%")) # 调整底部位置
.add(female_line, grid_opts=opts.GridOpts(pos_top="64%")) # 调整顶部位置
)
return grid
# 创建时间轴 - 折线图
timeline_line = Timeline()
timeline_line.add_schema(
play_interval=1000,
is_auto_play=True,
is_loop_play=False,
)
# 为每个年份生成图表并添加到时间轴
for i in range(len(years)):
combined_chart = create_combined_chart(i, years, male_education, female_education)
timeline_line.add(combined_chart, str(years[i]))
# ====== 创建组合页面 ======
page = Page(
layout=Page.DraggablePageLayout,
page_title="教育程度数据可视化"
)
# 设置页面背景色
page.add(
timeline_pie,
timeline_line
)
# 添加使用说明表格
instruction_table = Table()
instruction_table.add(
["图表说明"],
[
["<b>左侧图表:</b>分性别教育程度占比玫瑰图(随时间轴播放)"],
["<b>右侧图表:</b>分性别教育程度人口数变化折线图(随时间轴播放)"],
["<b>交互操作:</b>"],
["1. 拖动图表标题栏可移动位置"],
["2. 拖动图表右下角可调整大小"],
["3. 点击时间轴播放按钮可播放/暂停"],
["4. 鼠标悬停在图表上可查看详细数据"],
["5. 调整布局后点击右上角'保存配置'按钮可保存布局"]
]
)
instruction_table.set_global_opts(
title_opts=opts.ComponentTitleOpts(
title="使用说明",
subtitle="操作指南和图表说明"
)
)
# 添加表格到页面
page.add(instruction_table)
# 渲染页面
page.render("教育程度数据可视化.html")
print("组合图表已生成,文件名为 '教育程度数据可视化.html'")