|
|
|
@ -1,14 +1,26 @@
|
|
|
|
|
from pyecharts.charts import Bar
|
|
|
|
|
from pyecharts.charts import Bar, Pie
|
|
|
|
|
from pyecharts import options as opts
|
|
|
|
|
from pyecharts.commons.utils import JsCode
|
|
|
|
|
import os
|
|
|
|
|
|
|
|
|
|
def generate_bar_chart(_data, title: str, x_columns: list, y_columns: list, output_file: str = "bar_chart.html"):
|
|
|
|
|
def generate_chart(
|
|
|
|
|
_data,
|
|
|
|
|
chart_type: str, # 图表类型:'bar' 或 'pie'
|
|
|
|
|
title: str, # 图表标题
|
|
|
|
|
category_columns: list, # 分类数据列(X 轴或饼图标签)
|
|
|
|
|
value_column: str, # 数值数据列(Y 轴或饼图值)
|
|
|
|
|
output_file: str = "chart.html" # 输出文件路径
|
|
|
|
|
):
|
|
|
|
|
"""
|
|
|
|
|
根据结果集生成柱状图并保存为 HTML 文件。
|
|
|
|
|
统一生成柱形图或饼形图。
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
result: 结果集,通常是一个列表(字典格式)。
|
|
|
|
|
x_columns (list): 作为 X 轴的列名列表。
|
|
|
|
|
y_columns (list): 作为 Y 轴的列名列表。
|
|
|
|
|
output_file (str): 输出 HTML 文件名(默认 "bar_chart.html")。
|
|
|
|
|
_data: 结果集,通常是一个列表(字典格式)。
|
|
|
|
|
chart_type (str): 图表类型,'bar' 或 'pie'。
|
|
|
|
|
title (str): 图表标题。
|
|
|
|
|
category_columns (list): 分类数据列(X 轴或饼图标签)。
|
|
|
|
|
value_column (str): 数值数据列(Y 轴或饼图值)。
|
|
|
|
|
output_file (str): 输出 HTML 文件名(默认 "chart.html")。
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
# 如果结果集为空,直接返回
|
|
|
|
@ -16,131 +28,86 @@ def generate_bar_chart(_data, title: str, x_columns: list, y_columns: list, outp
|
|
|
|
|
raise ValueError("结果集为空,无法生成图表!")
|
|
|
|
|
|
|
|
|
|
# 检查列名是否存在
|
|
|
|
|
for col in x_columns + y_columns:
|
|
|
|
|
for col in category_columns + [value_column]:
|
|
|
|
|
if col not in _data[0]:
|
|
|
|
|
raise ValueError(f"列名 '{col}' 不存在!")
|
|
|
|
|
|
|
|
|
|
# 提取 X 轴数据(动态组合多列)
|
|
|
|
|
x_axis_data = [" - ".join(str(row[col]) for col in x_columns) for row in _data]
|
|
|
|
|
# 提取 Y 轴数据
|
|
|
|
|
y_axis_data = [row[y_columns[0]] for row in _data] # 目前只支持一个 Y 轴列
|
|
|
|
|
# 提取分类数据(动态组合多列)
|
|
|
|
|
categories = [" - ".join(str(row[col]) for col in category_columns) for row in _data]
|
|
|
|
|
# 提取数值数据
|
|
|
|
|
values = [row[value_column] for row in _data]
|
|
|
|
|
|
|
|
|
|
# 为每个柱状图设置不同颜色
|
|
|
|
|
# 颜色配置
|
|
|
|
|
colors = [
|
|
|
|
|
"#5470C6", "#91CC75", "#EE6666", "#73C0DE", "#3BA272", "#FC8452", "#9A60B4", "#EA7CCC"
|
|
|
|
|
] # 自定义颜色列表
|
|
|
|
|
]
|
|
|
|
|
color_js = JsCode(f'''
|
|
|
|
|
function(params) {{
|
|
|
|
|
var colorList = {colors};
|
|
|
|
|
return colorList[params.dataIndex % colorList.length];
|
|
|
|
|
}}
|
|
|
|
|
''')
|
|
|
|
|
|
|
|
|
|
# 生成柱形图
|
|
|
|
|
if chart_type == 'bar':
|
|
|
|
|
chart = Bar()
|
|
|
|
|
chart.add_xaxis(categories)
|
|
|
|
|
chart.add_yaxis(
|
|
|
|
|
series_name=value_column,
|
|
|
|
|
y_axis=values,
|
|
|
|
|
bar_width="30%",
|
|
|
|
|
itemstyle_opts=opts.ItemStyleOpts(color=color_js)
|
|
|
|
|
)
|
|
|
|
|
chart.set_global_opts(
|
|
|
|
|
title_opts=opts.TitleOpts(title=title),
|
|
|
|
|
xaxis_opts=opts.AxisOpts(
|
|
|
|
|
name=" - ".join(category_columns),
|
|
|
|
|
axislabel_opts=opts.LabelOpts(rotate=45),
|
|
|
|
|
boundary_gap=True,
|
|
|
|
|
splitline_opts=opts.SplitLineOpts(is_show=False)
|
|
|
|
|
),
|
|
|
|
|
yaxis_opts=opts.AxisOpts(name=value_column),
|
|
|
|
|
legend_opts=opts.LegendOpts(is_show=False)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# 创建柱状图并配置布局
|
|
|
|
|
bar = Bar()
|
|
|
|
|
bar.add_xaxis(x_axis_data)
|
|
|
|
|
bar.add_yaxis(
|
|
|
|
|
series_name=y_columns[0], # 使用 y_columns 的第一个元素作为系列名称
|
|
|
|
|
y_axis=y_axis_data,
|
|
|
|
|
bar_width="30%", # 控制柱宽
|
|
|
|
|
itemstyle_opts=opts.ItemStyleOpts(
|
|
|
|
|
color=JsCode(f'''
|
|
|
|
|
function(params) {{
|
|
|
|
|
var colorList = {colors};
|
|
|
|
|
return colorList[params.dataIndex % colorList.length];
|
|
|
|
|
}}
|
|
|
|
|
''')
|
|
|
|
|
# 生成饼形图
|
|
|
|
|
elif chart_type == 'pie':
|
|
|
|
|
data_pairs = list(zip(categories, values))
|
|
|
|
|
chart = Pie()
|
|
|
|
|
chart.add(
|
|
|
|
|
series_name="占比",
|
|
|
|
|
data_pair=data_pairs,
|
|
|
|
|
radius=["30%", "55%"],
|
|
|
|
|
label_opts=opts.LabelOpts(
|
|
|
|
|
formatter="{b}\n{d}%",
|
|
|
|
|
position="outside",
|
|
|
|
|
font_size=14
|
|
|
|
|
),
|
|
|
|
|
itemstyle_opts=opts.ItemStyleOpts(color=color_js)
|
|
|
|
|
)
|
|
|
|
|
chart.set_global_opts(
|
|
|
|
|
title_opts=opts.TitleOpts(
|
|
|
|
|
title=title,
|
|
|
|
|
subtitle="数据维度:" + " → ".join(category_columns)
|
|
|
|
|
),
|
|
|
|
|
legend_opts=opts.LegendOpts(is_show=False)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
bar.set_global_opts(
|
|
|
|
|
title_opts=opts.TitleOpts(title=title), # 添加标题
|
|
|
|
|
xaxis_opts=opts.AxisOpts(
|
|
|
|
|
name=" - ".join(x_columns),
|
|
|
|
|
axislabel_opts=opts.LabelOpts(rotate=45),
|
|
|
|
|
boundary_gap=True, # 开启分类间隙
|
|
|
|
|
splitline_opts=opts.SplitLineOpts(is_show=False)
|
|
|
|
|
),
|
|
|
|
|
yaxis_opts=opts.AxisOpts(name=y_columns[0]), # 使用 y_columns 的第一个元素作为 Y 轴名称
|
|
|
|
|
legend_opts=opts.LegendOpts(is_show=False) # 隐藏图例
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
raise ValueError("不支持的图表类型!")
|
|
|
|
|
|
|
|
|
|
# 确保目标目录存在
|
|
|
|
|
if not os.path.exists(os.path.dirname(output_file)):
|
|
|
|
|
os.makedirs(os.path.dirname(output_file))
|
|
|
|
|
os.makedirs(os.path.dirname(output_file), exist_ok=True)
|
|
|
|
|
|
|
|
|
|
# 保存为 HTML 文件
|
|
|
|
|
bar.render(output_file)
|
|
|
|
|
print(f"柱状图已保存为 {output_file}")
|
|
|
|
|
chart.render(output_file)
|
|
|
|
|
print(f"图表已保存为 {output_file}")
|
|
|
|
|
|
|
|
|
|
# 检查文件是否存在
|
|
|
|
|
if os.path.exists(output_file):
|
|
|
|
|
print("文件生成成功!")
|
|
|
|
|
else:
|
|
|
|
|
print("文件生成失败!")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"生成柱状图时发生错误: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
from pyecharts import options as opts
|
|
|
|
|
from pyecharts.charts import Pie
|
|
|
|
|
from pyecharts.commons.utils import JsCode
|
|
|
|
|
import os
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def generate_pie_chart(
|
|
|
|
|
_data,
|
|
|
|
|
title: str,
|
|
|
|
|
category_columns: list, # 改为列表接收多列(如["学段","科目"])
|
|
|
|
|
value_column: str,
|
|
|
|
|
output_file: str = "pie_chart.html"
|
|
|
|
|
):
|
|
|
|
|
"""
|
|
|
|
|
支持多列组合的饼图生成器
|
|
|
|
|
|
|
|
|
|
参数说明:
|
|
|
|
|
category_columns : 分类标签的多个字段名
|
|
|
|
|
value_column : 数值字段名
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
# 数据预处理:生成组合标签
|
|
|
|
|
combined_data = {}
|
|
|
|
|
for item in _data:
|
|
|
|
|
# 生成组合键(例:"小学 - 语文")
|
|
|
|
|
key = " - ".join(str(item[col]) for col in category_columns)
|
|
|
|
|
combined_data[key] = combined_data.get(key, 0) + item[value_column]
|
|
|
|
|
|
|
|
|
|
# 转换为饼图数据格式
|
|
|
|
|
data_pairs = list(combined_data.items())
|
|
|
|
|
|
|
|
|
|
# 颜色配置(与柱状图一致)
|
|
|
|
|
color_js = JsCode("""
|
|
|
|
|
function(params) {
|
|
|
|
|
var colors = ['#5470C6','#91CC75','#EE6666','#73C0DE'];
|
|
|
|
|
return colors[params.dataIndex % colors.length];
|
|
|
|
|
}
|
|
|
|
|
""")
|
|
|
|
|
|
|
|
|
|
# 构建饼图
|
|
|
|
|
pie = Pie()
|
|
|
|
|
pie.add(
|
|
|
|
|
series_name="占比",
|
|
|
|
|
data_pair=data_pairs,
|
|
|
|
|
radius=["30%", "55%"], # 环形布局优化
|
|
|
|
|
label_opts=opts.LabelOpts(
|
|
|
|
|
formatter="{b}\n{d}%", # 换行显示标签和比例
|
|
|
|
|
position="outside",
|
|
|
|
|
font_size=14
|
|
|
|
|
),
|
|
|
|
|
itemstyle_opts=opts.ItemStyleOpts(color=color_js)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# 全局配置优化
|
|
|
|
|
pie.set_global_opts(
|
|
|
|
|
title_opts=opts.TitleOpts(
|
|
|
|
|
title=title,
|
|
|
|
|
subtitle="数据维度:" + " → ".join(category_columns)
|
|
|
|
|
),
|
|
|
|
|
legend_opts=opts.LegendOpts(is_show=False) # 不显示图例
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# 输出文件
|
|
|
|
|
os.makedirs(os.path.dirname(output_file), exist_ok=True)
|
|
|
|
|
pie.render(output_file)
|
|
|
|
|
print(f"组合饼图已生成: {output_file}")
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"生成失败: {str(e)}")
|
|
|
|
|
print(f"生成图表时发生错误: {e}")
|