Merge commit '47296980495f8bbfc9493e93de85dd62de6fa6b9' as 'paste-framework'
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
import numpy as np
|
||||
import os
|
||||
import traceback
|
||||
from paste.chart.bar import (
|
||||
gen_vertical_bars,
|
||||
gen_horizontal_stacked_bars,
|
||||
gen_percent_stacked_bars
|
||||
)
|
||||
|
||||
|
||||
class ChartBarExample:
|
||||
"""
|
||||
图表测试管理器:封装对 paste.chart.bar 中三个函数的调用。
|
||||
不修改任何参数结构,仅提供清晰的调用封装与输出管理。
|
||||
"""
|
||||
|
||||
def __init__(self, output_directory="./charts"):
|
||||
"""
|
||||
初始化测试器,定义所有测试数据。
|
||||
数据结构完全匹配原始函数调用方式。
|
||||
"""
|
||||
self.output_directory = output_directory
|
||||
os.makedirs(self.output_directory, exist_ok=True)
|
||||
|
||||
# 纵向堆叠柱形图数据(直接对应 gen_vertical_bars 参数)
|
||||
self.primary_vals = [10, 20, 15, 25, 18]
|
||||
self.nested_vals = [5, 8, 3, 10, 6]
|
||||
self.x_labels_vert = ['产品1', '产品2', '产品3', '产品4', '产品5']
|
||||
self.group_labels_vert = ['销售量', '退货量']
|
||||
|
||||
# 横向堆叠柱形图数据(直接对应 gen_horizontal_stacked_bars 参数)
|
||||
self.data_matrix = np.array([
|
||||
[10, 20, 15],
|
||||
[15, 12, 18],
|
||||
[8, 16, 10],
|
||||
[12, 14, 13]
|
||||
])
|
||||
self.x_labels_hori = ['线上销售', '门店销售', '批发销售']
|
||||
self.y_labels_hori = ['北京', '上海', '广州', '深圳']
|
||||
self.y_data_unit_hori = '万元'
|
||||
self.title_hori = '销售构成'
|
||||
|
||||
# 百分比堆叠柱形图数据(直接对应 gen_percent_stacked_bars 参数)
|
||||
self.data_percent = {
|
||||
'A组': [10, 20, 15, 18],
|
||||
'B组': [5, 10, 5, 8],
|
||||
'C组': [3, 7, 10, 4]
|
||||
}
|
||||
self.x_labels_percent = ['Q1', 'Q2', 'Q3', 'Q4']
|
||||
self.title_percent = '季度占比'
|
||||
|
||||
def generate_vertical_bars(self) -> str:
|
||||
"""调用 gen_vertical_bars,参数完全一致"""
|
||||
try:
|
||||
return gen_vertical_bars(
|
||||
self.primary_vals,
|
||||
self.nested_vals,
|
||||
self.x_labels_vert,
|
||||
self.group_labels_vert
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"纵向堆叠柱形图生成失败: {e}")
|
||||
traceback.print_exc()
|
||||
raise
|
||||
|
||||
def generate_horizontal_stacked_bars(self) -> str:
|
||||
"""调用 gen_horizontal_stacked_bars,参数完全一致"""
|
||||
try:
|
||||
return gen_horizontal_stacked_bars(
|
||||
self.data_matrix,
|
||||
self.x_labels_hori,
|
||||
self.y_labels_hori,
|
||||
self.y_data_unit_hori,
|
||||
self.title_hori
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"横向堆叠柱形图生成失败: {e}")
|
||||
traceback.print_exc()
|
||||
raise
|
||||
|
||||
def generate_percent_stacked_bars(self) -> str:
|
||||
"""调用 gen_percent_stacked_bars,参数完全一致"""
|
||||
try:
|
||||
return gen_percent_stacked_bars(
|
||||
self.data_percent,
|
||||
self.x_labels_percent,
|
||||
self.title_percent
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"百分比堆叠柱形图生成失败: {e}")
|
||||
traceback.print_exc()
|
||||
raise
|
||||
|
||||
def save_svg(self, svg_data: str, filename: str) -> None:
|
||||
"""
|
||||
将 SVG 的 base64 Data URL 写入文件(保留原始 SVG 格式)。
|
||||
注意:svg_data 是 "data:image/svg+xml;base64,...",需提取真实 SVG 内容。
|
||||
"""
|
||||
if not svg_data or not isinstance(svg_data, str):
|
||||
print(f"生成的 SVG 数据无效(为空或非字符串): {filename}")
|
||||
return
|
||||
|
||||
# 提取 base64 编码部分(去除 data URL 前缀)
|
||||
if svg_data.startswith("data:image/svg+xml;base64,"):
|
||||
base64_content = svg_data[len("data:image/svg+xml;base64,"):]
|
||||
try:
|
||||
# 解码 base64 得到原始 SVG 字符串
|
||||
import base64
|
||||
svg_content = base64.b64decode(base64_content).decode('utf-8')
|
||||
except Exception as e:
|
||||
print(f"解码 base64 失败: {e}")
|
||||
svg_content = svg_data # 退化为直接写入
|
||||
else:
|
||||
# 如果不是标准格式,直接写入(兼容调试)
|
||||
svg_content = svg_data
|
||||
|
||||
filepath = os.path.join(self.output_directory, filename)
|
||||
with open(filepath, 'w', encoding='utf-8') as f:
|
||||
f.write(svg_content)
|
||||
print(f"已保存: {filepath}")
|
||||
|
||||
def run(self) -> None:
|
||||
"""按顺序执行所有图表生成与保存"""
|
||||
print("开始生成图表...")
|
||||
try:
|
||||
print("生成纵向堆叠柱形图...")
|
||||
svg1 = self.generate_vertical_bars()
|
||||
self.save_svg(svg1, "vertical_bars.svg")
|
||||
|
||||
print("生成横向堆叠柱形图...")
|
||||
svg2 = self.generate_horizontal_stacked_bars()
|
||||
self.save_svg(svg2, "horizontal_stacked_bars.svg")
|
||||
|
||||
print("生成百分比堆叠柱形图...")
|
||||
svg3 = self.generate_percent_stacked_bars()
|
||||
self.save_svg(svg3, "percent_stacked_bars.svg")
|
||||
|
||||
print("\n所有图表已成功生成。")
|
||||
print(f"输出目录: {self.output_directory}")
|
||||
print("文件列表:")
|
||||
print(" - vertical_bars.svg")
|
||||
print(" - horizontal_stacked_bars.svg")
|
||||
print(" - percent_stacked_bars.svg")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n测试失败: {e}")
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
# 程序入口
|
||||
if __name__ == "__main__":
|
||||
tester = ChartBarExample()
|
||||
tester.run()
|
||||
@@ -0,0 +1,110 @@
|
||||
import os
|
||||
import traceback
|
||||
from paste.chart.pie import gen_pie
|
||||
|
||||
|
||||
class ChartPieExample:
|
||||
"""
|
||||
环形图测试管理器:封装对 paste.chart.pie.gen_pie 的调用。
|
||||
数据结构严格匹配函数参数要求,支持扩展更多测试用例。
|
||||
"""
|
||||
|
||||
def __init__(self, output_directory="./charts"):
|
||||
"""
|
||||
初始化测试器,定义所有测试数据。
|
||||
数据结构完全匹配 gen_pie 函数的参数要求。
|
||||
"""
|
||||
self.output_directory = output_directory
|
||||
os.makedirs(self.output_directory, exist_ok=True)
|
||||
|
||||
# 构造符合 gen_pie 要求的 DataFrame 数据(模拟真实业务场景)
|
||||
# 假设业务场景:网络设备统计(服务器、交换机、路由器等)
|
||||
import pandas as pd
|
||||
self.data_df = pd.DataFrame({
|
||||
'device_count': [35, 28, 22, 15, 10], # value_column
|
||||
'percentage': ['35.2%', '28.1%', '22.0%', '15.0%', '9.7%'], # percentage_column
|
||||
'device_type': ['服务器', '交换机', '路由器', '防火墙', 'AP'] # legend_labels
|
||||
})
|
||||
|
||||
# 测试参数
|
||||
self.value_column = 'device_count'
|
||||
self.percentage_column = 'percentage'
|
||||
self.legend_labels = 'device_type'
|
||||
self.color_palette = 'BuPu' # 可尝试 'viridis', 'Set3', 'plasma'
|
||||
self.dpi = 128
|
||||
|
||||
# 输出文件名
|
||||
self.filename = "pie_chart.svg"
|
||||
|
||||
def generate_pie_chart(self) -> str:
|
||||
"""
|
||||
调用 gen_pie 函数,参数完全一致。
|
||||
注意:gen_pie 接收的是 pandas.DataFrame,不是列表。
|
||||
"""
|
||||
try:
|
||||
svg_data = gen_pie(
|
||||
data_df=self.data_df,
|
||||
value_column=self.value_column,
|
||||
percentage_column=self.percentage_column,
|
||||
legend_labels=self.legend_labels,
|
||||
color_palette=self.color_palette,
|
||||
dpi=self.dpi
|
||||
)
|
||||
if not svg_data or not isinstance(svg_data, str):
|
||||
raise ValueError("gen_pie 返回的 SVG 数据为空或类型错误")
|
||||
return svg_data
|
||||
except Exception as e:
|
||||
print(f"环形图生成失败: {e}")
|
||||
traceback.print_exc()
|
||||
raise
|
||||
|
||||
def save_svg(self, svg_data: str, filename: str) -> None:
|
||||
"""
|
||||
将 SVG 的 base64 Data URL 写入文件(保留原始 SVG 格式)。
|
||||
注意:svg_data 是 "data:image/svg+xml;base64,...",需提取真实 SVG 内容。
|
||||
"""
|
||||
if not svg_data or not isinstance(svg_data, str):
|
||||
print(f"生成的 SVG 数据无效(为空或非字符串): {filename}")
|
||||
return
|
||||
|
||||
# 提取 base64 编码部分(去除 data URL 前缀)
|
||||
if svg_data.startswith("data:image/svg+xml;base64,"):
|
||||
base64_content = svg_data[len("data:image/svg+xml;base64,"):]
|
||||
try:
|
||||
# 解码 base64 得到原始 SVG 字符串
|
||||
import base64
|
||||
svg_content = base64.b64decode(base64_content).decode('utf-8')
|
||||
except Exception as e:
|
||||
print(f"解码 base64 失败: {e}")
|
||||
svg_content = svg_data # 退化为直接写入
|
||||
else:
|
||||
# 如果不是标准格式,直接写入(兼容调试)
|
||||
svg_content = svg_data
|
||||
|
||||
filepath = os.path.join(self.output_directory, filename)
|
||||
with open(filepath, 'w', encoding='utf-8') as f:
|
||||
f.write(svg_content)
|
||||
print(f"已保存: {filepath}")
|
||||
|
||||
def run(self) -> None:
|
||||
"""按顺序执行图表生成与保存"""
|
||||
print("开始生成环形图...")
|
||||
try:
|
||||
print("正在生成环形图...")
|
||||
svg_data = self.generate_pie_chart()
|
||||
self.save_svg(svg_data, self.filename)
|
||||
|
||||
print(f"\n环形图已成功生成。")
|
||||
print(f"输出目录: {self.output_directory}")
|
||||
print(f"文件列表:")
|
||||
print(f" - {self.filename}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n测试失败: {e}")
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
# 程序入口
|
||||
if __name__ == "__main__":
|
||||
tester = ChartPieExample()
|
||||
tester.run()
|
||||
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Charts</title>
|
||||
</head>
|
||||
<body>
|
||||
<img src="./horizontal_stacked_bars.svg" alt="">
|
||||
<img src="./lines.svg" alt="">
|
||||
<img src="./percent_stacked_bars.svg" alt="">
|
||||
<img src="./pie_chart.svg" alt="">
|
||||
<img src="./splines.svg" alt="">
|
||||
<img src="./vertical_bars.svg" alt="">
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 50 KiB |
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 36 KiB |
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 34 KiB |
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 37 KiB |
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 36 KiB |
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 30 KiB |
@@ -0,0 +1,124 @@
|
||||
import pandas as pd
|
||||
import os
|
||||
from paste.chart.line import gen_lines, gen_splines
|
||||
|
||||
|
||||
class LineChartExample:
|
||||
"""
|
||||
折线图与平滑曲线测试管理器。
|
||||
封装 gen_lines 和 gen_splines 的调用,统一输出管理。
|
||||
"""
|
||||
|
||||
def __init__(self, output_directory="./charts"):
|
||||
"""
|
||||
初始化测试器,定义所有测试数据。
|
||||
使用 'ME' 替代 'M' 以兼容 pandas >=2.0。
|
||||
"""
|
||||
self.output_directory = output_directory
|
||||
os.makedirs(self.output_directory, exist_ok=True)
|
||||
|
||||
# =========================
|
||||
# gen_lines 测试数据(使用 'ME' 替代废弃的 'M')
|
||||
# =========================
|
||||
# 时间序列数据:3个年份,每月最后一个交易日(12个月)
|
||||
dates_2022 = pd.date_range('2022-01-01', periods=12, freq='ME')
|
||||
dates_2023 = pd.date_range('2023-01-01', periods=12, freq='ME')
|
||||
dates_2024 = pd.date_range('2024-01-01', periods=12, freq='ME')
|
||||
|
||||
self.data_dict_lines = {
|
||||
'2022': pd.Series([100 + i * 2 for i in range(12)], index=dates_2022),
|
||||
'2023': pd.Series([110 + i * 1.5 for i in range(12)], index=dates_2023),
|
||||
'2024': pd.Series([120 + i * 1 for i in range(12)], index=dates_2024),
|
||||
}
|
||||
|
||||
self.color_palette_lines = 'BuPu'
|
||||
self.dpi_lines = 128
|
||||
|
||||
# =========================
|
||||
# gen_splines 测试数据(数值索引,无频率问题)
|
||||
# =========================
|
||||
self.data_dict_splines = {
|
||||
'A组': pd.Series([10, 15, 12, 18, 16], index=[0, 1, 2, 3, 4]),
|
||||
'B组': pd.Series([12, 14, 13, 17, 15], index=[0, 1, 2, 3, 4]),
|
||||
'C组': pd.Series([8, 20, 10, 22, 11], index=[0, 1, 2, 3, 4]),
|
||||
}
|
||||
|
||||
self.total_splines = pd.Series({
|
||||
'A组': 71,
|
||||
'B组': 71,
|
||||
'C组': 71
|
||||
})
|
||||
|
||||
self.color_palette_splines = 'viridis'
|
||||
self.dpi_splines = 128
|
||||
self.smooth_points = 100
|
||||
self.spline_k = 3
|
||||
self.markevery = 30
|
||||
|
||||
def generate_lines(self) -> str:
|
||||
"""调用 gen_lines,参数顺序完全匹配原始函数"""
|
||||
return gen_lines(
|
||||
self.data_dict_lines,
|
||||
self.color_palette_lines,
|
||||
self.dpi_lines
|
||||
)
|
||||
|
||||
def generate_splines(self) -> str:
|
||||
"""调用 gen_splines,参数顺序完全匹配原始函数"""
|
||||
return gen_splines(
|
||||
self.data_dict_splines,
|
||||
self.total_splines,
|
||||
None,
|
||||
self.color_palette_splines,
|
||||
self.dpi_splines
|
||||
)
|
||||
|
||||
def save_svg(self, svg_data: str, filename: str) -> None:
|
||||
"""
|
||||
将 SVG 的 base64 Data URL 写入文件(保留原始 SVG 格式)。
|
||||
注意:svg_data 是 "data:image/svg+xml;base64,...",需提取真实 SVG 内容。
|
||||
"""
|
||||
if not svg_data or not isinstance(svg_data, str):
|
||||
print(f"生成的 SVG 数据无效(为空或非字符串): {filename}")
|
||||
return
|
||||
|
||||
# 提取 base64 编码部分(去除 data URL 前缀)
|
||||
if svg_data.startswith("data:image/svg+xml;base64,"):
|
||||
base64_content = svg_data[len("data:image/svg+xml;base64,"):]
|
||||
try:
|
||||
# 解码 base64 得到原始 SVG 字符串
|
||||
import base64
|
||||
svg_content = base64.b64decode(base64_content).decode('utf-8')
|
||||
except Exception as e:
|
||||
print(f"解码 base64 失败: {e}")
|
||||
svg_content = svg_data # 退化为直接写入
|
||||
else:
|
||||
# 如果不是标准格式,直接写入(兼容调试)
|
||||
svg_content = svg_data
|
||||
|
||||
filepath = os.path.join(self.output_directory, filename)
|
||||
with open(filepath, 'w', encoding='utf-8') as f:
|
||||
f.write(svg_content)
|
||||
print(f"已保存: {filepath}")
|
||||
|
||||
def run(self) -> None:
|
||||
"""按顺序生成并保存所有图表"""
|
||||
print("生成折线图...")
|
||||
svg1 = self.generate_lines()
|
||||
self.save_svg(svg1, "lines.svg")
|
||||
|
||||
print("生成平滑曲线图...")
|
||||
svg2 = self.generate_splines()
|
||||
self.save_svg(svg2, "splines.svg")
|
||||
|
||||
print("\n所有图表已生成。")
|
||||
print(f"输出目录: {self.output_directory}")
|
||||
print("文件列表:")
|
||||
print(" - lines.svg")
|
||||
print(" - splines.svg")
|
||||
|
||||
|
||||
# 程序入口
|
||||
if __name__ == "__main__":
|
||||
tester = LineChartExample()
|
||||
tester.run()
|
||||
Reference in New Issue
Block a user