Python/utils/pandas_extension.py

1490 lines
52 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
"""
扩展PANDAS常用功能
"""
# 加载模块
from pathlib import Path
import pandas
from prettytable import PrettyTable
from openpyxl import Workbook
from openpyxl.styles import Font, Alignment, Border, Side, PatternFill
from openpyxl.utils import get_column_letter
from pyecharts import __file__ as default_path, options
from pyecharts.components import Table
from pyecharts.charts import Scatter, Pie, Bar, Line, HeatMap
from pyecharts.globals import ThemeType, CurrentConfig
from jinja2 import Environment, FileSystemLoader
def open_csv(file_name):
"""打开并读取CSV文件返回数据体"""
# 创建路径对象
file_path = Path(file_name)
if not file_path.exists():
raise FileNotFoundError(f"路径({file_path})不存在")
if not file_path.is_file():
raise IsADirectoryError(f"路径({file_path})非文件")
if file_path.suffix.lower() != ".csv":
raise ValueError(f"文件名后缀({file_path.suffix.lower()})非CSV")
# 使用PANDAS读取CSV文件并返回
return pandas.read_csv(
filepath_or_buffer=file_path,
# 指定所有列数据类型为字符串(后续人工处理数据类型)
dtype=str,
encoding="utf-8",
# 若遇行解析错误则抛出PARSERERROR
on_bad_lines="error",
)
def traverse_directory(directory_path, suffixes):
"""遍历目录根据文件名后缀读取文件,返回数据体"""
# 创建路径对象
directory_path = Path(directory_path)
if not directory_path.exists():
raise FileNotFoundError(f"路径({directory_path})不存在")
if not directory_path.is_dir():
raise NotADirectoryError(f"路径({directory_path})非目录")
if not isinstance(suffixes, list):
raise TypeError("文件名后缀(suffixes)数据类型非列表")
dataframe = []
for file_path in directory_path.rglob("*"):
if file_path.is_file() and file_path.suffix.lower() in suffixes:
dataframe.append(
{
"文件名": file_path.name,
"父目录": file_path.parent.name,
"相对路径": str(file_path.relative_to(".")),
}
)
# 创建并返回数据体
return pandas.DataFrame(dataframe).sort_values(by=["父目录", "文件名"])
def save_as_workbook(worksheets, workbook_name):
"""将数据体保存为工作簿"""
# 创建工作簿
workbook = Workbook()
# 删除默认创建的工作表
for worksheet_name in workbook.sheetnames:
workbook.remove(workbook[worksheet_name])
for worksheet_name, worksheet_dataframe in worksheets:
# 若工作表数据体为空则跳过
if not worksheet_dataframe.empty:
# 创建工作表
worksheet = workbook.create_sheet(title=worksheet_name)
# 若为多层行索引则重置行索引(单层行索引则跳过)
if isinstance(worksheet_dataframe.index, pandas.MultiIndex):
worksheet_dataframe.reset_index(inplace=True)
# 遍历列索引层
for level in range(worksheet_dataframe.columns.nlevels):
# 添加列名行
worksheet.append(
worksheet_dataframe.columns.get_level_values(level).tolist()
) # 工作表一般包括标题行、列名行和数据行,在这里仅考虑列名行和数据行
# 初始化合并单元格开始列号
merge_start_column = 0
# 初始化上一个单元格值
previous_cell_value = worksheet.cell(
row=level + 1, column=merge_start_column + 1
).value
for column in range(
len(worksheet_dataframe.columns.get_level_values(level))
):
# 单元格值
value = worksheet.cell(
row=level + 1, column=column + 1
).value # 工作表数据体为PANDAS.DATAFRAME行号和列号从0开始工作表为OPENPYXL行号和列号从1开始
if value != previous_cell_value:
# 合并单元格结束列号
merge_end_column = column - 1
# 判断合并单元格结束列号和合并单元格开始列号的差值是否大于0
if (
previous_cell_value
and merge_end_column - merge_start_column > 0
):
# 合并单元格(同行合并)
worksheet.merge_cells(
start_row=level + 1,
end_row=level + 1,
start_column=merge_start_column + 1,
end_column=merge_end_column + 1,
)
# 重新赋值合并单元格开始列号
merge_start_column = column
# 重新赋值上一单元格值
previous_cell_value = value
# 若单元格值和上一个单元格值相同,若列号为最大值则合并单元格
else:
# 判断列号是否为最大值
if (
column
== len(worksheet_dataframe.columns.get_level_values(level))
- 1
):
# 重新赋值合并单元格结束列号
merge_end_column = column
# 合并单元格(同行合并)
worksheet.merge_cells(
start_row=level + 1,
end_row=level + 1,
start_column=merge_start_column + 1,
end_column=merge_end_column + 1,
)
# 若单元格值为空则同列合并
if not value:
# 合并单元格(同列合并)
worksheet.merge_cells(
start_row=level,
end_row=level + 1,
start_column=column + 1,
end_column=column + 1,
)
# 工作表列宽24磅
for column in range(worksheet_dataframe.shape[1]):
worksheet.column_dimensions[get_column_letter(column + 1)].width = 24
# 列名行行高24磅
for level in range(worksheet_dataframe.columns.nlevels):
worksheet.row_dimensions[level + 1].height = 24
# 列名行单元格样式
for column in range(
len(worksheet_dataframe.columns.get_level_values(level))
):
cell = worksheet.cell(level + 1, column + 1)
# 字体
cell.font = Font(bold=True, size=12, name="Arial", color="00FFFFFF")
# 对齐方式
cell.alignment = Alignment(horizontal="center", vertical="center")
# 边框
cell.border = Border(
left=Side(style="thin", color="00333333"),
right=Side(style="thin", color="00333333"),
top=Side(style="thin", color="00333333"),
bottom=Side(style="thin", color="00333333"),
)
# 填充
cell.fill = PatternFill(fill_type="solid", start_color="003366FF")
for row, row_data in worksheet_dataframe.iterrows():
# 数据行行高20磅
worksheet.row_dimensions[
row + worksheet_dataframe.columns.nlevels + 1
].height = 20
for column in range(worksheet_dataframe.shape[1]):
cell = worksheet.cell(
row + worksheet_dataframe.columns.nlevels + 1, column + 1
)
# 单元格值
cell.value = row_data.iloc[column]
# 字体
cell.font = Font(size=12, name="Arial", color="00333333")
# 对齐方式
cell.alignment = Alignment(horizontal="center", vertical="center")
cell.border = Border(
left=Side(style="thin", color="00333333"),
right=Side(style="thin", color="00333333"),
top=Side(style="thin", color="00333333"),
bottom=Side(style="thin", color="00333333"),
)
workbook.save(workbook_name)
def print_table(dataframe, index_as_the_first_column=False, title=None, remark=None):
"""将数据体打印为表格"""
dataframe_temporary = dataframe.copy()
table = PrettyTable()
# 若索引作为第一列,则重置数据集索引
if index_as_the_first_column:
# 重置索引
dataframe_temporary.reset_index(inplace=True)
# 修改第一列列名
dataframe_temporary.rename(columns={"index": ""}, inplace=True)
# 列标签
column_labels = dataframe_temporary.columns.tolist()
# 赋值给表格列标签
table.field_names = column_labels
# 行数据(若数据类型为浮点则保留两位小数并转为字符)
row_data = dataframe_temporary.map(
lambda x: f"{x:.2f}" if isinstance(x, float) else x
).to_numpy()
# 赋值给表格行数据
table.add_rows(row_data)
# 第一列左对齐,其余列右对齐
table.align[column_labels[0]] = "l"
for column_label in column_labels[1:]:
table.align[column_label] = "r"
# 若表格标题数据类型为字符则打印
if isinstance(title, str):
print(title)
print(table)
# 若表格备注数据类型为字符则打印
if isinstance(remark, str):
print(remark)
class DrawAsHTML:
def __init__(self):
"""绘制图表并保存为HTML"""
# 全局网页标题
CurrentConfig.PAGE_TITLE = "DrawAsHTML"
# 全局环境
CurrentConfig.GLOBAL_ENV = Environment(
loader=FileSystemLoader(
[
Path(default_path).parent / "render" / "templates", # 默认模版路径
Path(__file__).parent / "templates", # 自定义模版路径
]
)
)
@staticmethod
def table(dataframe: pandas.DataFrame, file_name: str) -> None:
"""表格"""
# 创建表格HTML标题默认为全局网页标题
chart = Table(page_title=CurrentConfig.PAGE_TITLE)
chart.add(
# 表格表头
headers=dataframe.columns.tolist(),
# 表格数据
rows=dataframe.values.tolist(),
)
chart.render(
template_name="table.html",
html_content=chart.html_content,
path=file_name,
)
# 直角坐标系图表
def scatter(
self,
dataframe: pandas.DataFrame,
file_name: str,
xaxis_opts_min: int | float | None = None,
xaxis_opts_max: int | float | None = None,
yaxis_opts_min: int | float | None = None,
yaxis_opts_max: int | float | None = None,
) -> None:
"""散点图"""
# 根据数据体列数匹配生成散点图方法
match dataframe.shape[1]:
# 默认为数据行第一个数据为系列名称、第二个数据为X坐标轴值、第三个数据为Y坐标轴值、第四个数据为标记大小
case 4:
pass
# 创建散点图(默认初始化配置项)
chart = Scatter(init_opts=self._init_opts())
chart.set_global_opts(
# 不显示图例
legend_opts=self._legend_opts(),
# 不显示提示框
tooltip_opts=self._tooltip_opts(),
# X坐标轴配置项默认第二个列名作为X坐标轴名称
xaxis_opts=self._axis_opts(
name=dataframe.columns[1], min_=xaxis_opts_min, max_=xaxis_opts_max
),
# Y坐标轴配置项默认第三个列名作为X坐标轴名称
yaxis_opts=self._axis_opts(
name=dataframe.columns[2], min_=yaxis_opts_min, max_=yaxis_opts_max
),
)
for series_name in dataframe[dataframe.columns[0]].unique():
chart.add_xaxis(
xaxis_data=dataframe.loc[
dataframe[dataframe.columns[0]] == series_name,
dataframe.columns[1],
].tolist()
)
# noinspection PyTypeChecker
chart.add_yaxis(
# 系列名称
series_name=series_name,
# 系列数据
y_axis=[
options.ScatterItem(
# 数据项名称
name=row.iloc[3],
# 数据项值
value=[row.iloc[1], row.iloc[2]],
# 标记大小经过MIN-MAX归一化至20~60
symbol_size=20
+ 40
* (row.iloc[3] - dataframe[dataframe.columns[3]].min())
/ (
dataframe[dataframe.columns[3]].max()
- dataframe[dataframe.columns[3]].min()
),
# 标签配置项
label_opts=self._label_opts(
# 标签的位置
position="right",
# 标签内容格式器,在散点图中:{a}(系列名称),{b}(数据名称),{c}(数值数组)
formatter="{a|{a}}\n{b|{b}}",
# 文字块富文本样式
rich={
"a": {"fontWeight": "bold"},
"b": {"lineHeight": 25},
},
),
)
for _, row in dataframe[
dataframe[dataframe.columns[0]] == series_name
].iterrows()
], # 使用散点图数据项设置系列数据
)
chart.render(file_name)
def bar(
self, dataframe: pandas.DataFrame, file_name: str, stack: bool = False
) -> None:
"""柱状图"""
# 创建柱状图(默认初始化配置项)
chart = Bar(init_opts=self._init_opts())
chart.set_global_opts(
# 显示图例
legend_opts=self._legend_opts(is_show=True if stack else False),
# 不显示提示框
tooltip_opts=self._tooltip_opts(),
# X坐标轴配置项默认第二个列名作为X坐标轴名称
xaxis_opts=self._axis_opts(
type_="category", name=dataframe.columns[1], axislabel_rotate=-30
),
# Y坐标轴配置项默认第三个列名作为Y坐标轴名称
yaxis_opts=self._axis_opts(
name=dataframe.columns[2],
),
)
chart.add_xaxis(
xaxis_data=dataframe[dataframe.columns[1]].unique().tolist()
) # 柱状图和散点图在新增X坐标轴方法不相同
for series_name in dataframe[dataframe.columns[0]].unique():
# noinspection PyTypeChecker
chart.add_yaxis(
# 系列名称
series_name=series_name,
# 系列数据
y_axis=[
options.BarItem(
# 数据项名称
name=None,
# 数据项值
value=[row.iloc[1], row.iloc[2]],
# 标签配置项
label_opts=self._label_opts(
# 标签内容格式器,在柱状图中:{a}(系列名称),{b}(数据名称),{c}(数值数组)
# 显示数据项值的第二个值
formatter="{value|{@[1]}}",
# 文字块富文本样式
rich={
"value": {"fontWeight": "bold"},
},
),
)
for _, row in dataframe[
dataframe[dataframe.columns[0]] == series_name
].iterrows()
], # 使用散点图数据项设置系列数据
# 数据堆叠
stack="bar_stacked" if stack else None,
)
chart.render(file_name)
def line(
self,
dataframe: pandas.DataFrame,
file_name: str,
yaxis_opts_min: int | float | None = None,
yaxis_opts_max: int | float | None = None,
) -> None:
"""折线图"""
# 创建折线图(默认初始化配置项)
chart = Line(init_opts=self._init_opts())
chart.set_global_opts(
# 不显示图例
legend_opts=self._legend_opts(is_show=True),
# 不显示提示框
tooltip_opts=self._tooltip_opts(),
# X坐标轴配置项默认第二个列名作为X坐标轴名称
xaxis_opts=self._axis_opts(
type_="category",
name=dataframe.columns[1],
boundary_gap=False,
axislabel_rotate=-30,
),
# Y坐标轴配置项默认第三个列名作为Y坐标轴名称
yaxis_opts=self._axis_opts(
name=dataframe.columns[2],
min_=yaxis_opts_min,
max_=yaxis_opts_max,
),
)
chart.add_xaxis(xaxis_data=dataframe[dataframe.columns[1]].unique().tolist())
for series_name in dataframe[dataframe.columns[0]].unique():
# noinspection PyTypeChecker
chart.add_yaxis(
series_name=series_name,
# 系列数据
y_axis=[
options.LineItem(
# 数据项值
value=[row.iloc[1], row.iloc[2]],
# 标签配置项
label_opts=self._label_opts(
# 标签的位置
position="right",
# 标签内容格式器,在折线图中:{a}(系列名称),{b}(数据名称),{c}(数值数组)
# 显示数据项值的第二个值
formatter="{value|{@[1]}}",
# 文字块富文本样式
rich={
"value": {"fontWeight": "bold"},
},
),
)
for _, row in dataframe[
dataframe[dataframe.columns[0]] == series_name
].iterrows()
], # 使用折线图数据项设置系列数据
# 是否平滑曲线
is_smooth=True,
# 线样式配置项默认线宽为2.5、线的颜色为跟随系列颜色
linestyle_opts=self._linestyle_opts(width=2.5, color=None),
)
chart.render(file_name)
def area(
self,
dataframe: pandas.DataFrame,
file_name: str,
yaxis_opts_min: int | float | None = None,
yaxis_opts_max: int | float | None = None,
) -> None:
"""面积图"""
# 创建面积图(默认初始化配置项)
chart = Line(init_opts=self._init_opts())
chart.set_global_opts(
# 不显示图例
legend_opts=self._legend_opts(),
# 不显示提示框
tooltip_opts=self._tooltip_opts(),
# X坐标轴配置项默认第一个列名作为X坐标轴名称
xaxis_opts=self._axis_opts(
type_="category",
name=dataframe.columns[0],
boundary_gap=False,
axislabel_rotate=-30,
),
# Y坐标轴配置项默认第二个列名作为Y坐标轴名称
yaxis_opts=self._axis_opts(
name=dataframe.columns[1],
min_=yaxis_opts_min,
max_=yaxis_opts_max,
),
)
chart.add_xaxis(xaxis_data=dataframe[dataframe.columns[0]].tolist())
# noinspection PyTypeChecker
chart.add_yaxis(
series_name="series_name",
# 系列数据
y_axis=[
options.LineItem(
# 数据项值
value=[row.iloc[0], row.iloc[1]],
# 标签配置项
label_opts=self._label_opts(
# 标签的位置
position="right",
# 标签内容格式器,在折线图中:{a}(系列名称),{b}(数据名称),{c}(数值数组)
# 显示数据项值的第二个值
formatter="{value|{@[1]}}",
# 文字块富文本样式
rich={
"value": {"fontWeight": "bold"},
},
),
)
for _, row in dataframe.iterrows()
], # 使用折线图数据项设置系列数据
# 是否平滑曲线
is_smooth=True,
# 线样式配置项默认线宽为2.5、线的颜色为跟随系列颜色
linestyle_opts=self._linestyle_opts(width=2.5, color=None),
# 填充区域配置项
areastyle_opts=self._areastyle_opts(),
)
chart.render(file_name)
# 基本图表
def pie(
self,
dataframe: pandas.DataFrame,
file_name: str,
rosetype: str | None = "area",
) -> None:
"""饼图"""
# 创建饼图(默认初始化配置项)
chart = Pie(init_opts=self._init_opts())
chart.set_global_opts(
# 不显示图例
legend_opts=self._legend_opts(),
# 不显示提示框
tooltip_opts=self._tooltip_opts(),
)
chart.add(
# 系列名称,默认为第一个列名
series_name=dataframe.columns.tolist()[0],
# 系列数据项,默认为数据行前两个数据
data_pair=[[row.iloc[0], row.iloc[1]] for _, row in dataframe.iterrows()],
# 是否展示成南丁格尔图,玫瑰图,扇区圆心角相同,通过半径区分组数据大小
# area所有扇区圆心角相同通过半径展现数据大小
# radius扇区圆心角展现数据的百分比半径展现数据的大小
# None不展示成南丁格尔图饼图
rosetype=rosetype,
# 中心坐标
center=["50%", "60%"],
# 饼图的扇区顺时针排布
is_clockwise=True,
# 标签配置项
label_opts=self._label_opts(
# 标签的位置默认为外部
position="outside",
# 标签内容格式器,在饼图中:{a}系列名称,{b}数据项名称,{c}数值,{d}百分比
formatter="{b|{b}}\n{c|{c}}",
# 文字块富文本样式
rich={"b": {"fontWeight": "bold"}, "c": {"lineHeight": 25}},
),
)
chart.render(file_name)
# 全局配置项(先总再分,先全局再系列)
# 初始化配置项
def _init_opts(
self,
# 图表画布宽度
width="500px",
# 图表画布高度
height="350px",
# 图表主题
theme=ThemeType.WALDEN,
# 网页标题,默认为全局网页标题
page_title=CurrentConfig.PAGE_TITLE,
# 图表动画初始化配置
animation_opts=None,
):
# 若图表动画初始化配置为空则为默认配置
if not animation_opts:
animation_opts = self._animation_opts()
return options.InitOpts(
width=width,
height=height,
theme=theme,
page_title=page_title,
animation_opts=animation_opts,
)
# 图表动画初始化配置
@staticmethod
def _animation_opts(
# 是否开启动画
animation=False,
):
return options.AnimationOpts(animation=animation)
# 图例配置项
def _legend_opts(
self,
# 图例字体样式默认为中黑、常规、12像素
textstyle_opts=None,
# 是否显示图例,默认为不显示
is_show=False,
# 组件距离容器左侧位置,默认为居中
pos_left="center",
# 组件距离容器上侧位置,默认为上侧
pos_top="top",
# 组件布局朝向,默认为水平
orient="horizontal",
# 组件对齐方式,默认为自动
align="auto",
# 组件内边距默认为5
padding=5,
# 图例标记的图形宽度默认为10
item_width=10,
# 图例标记的图形高度默认为10
item_height=10,
# 图例关闭时的颜色,默认为中黑
inactive_color="#86909C",
# 图例的边框线宽
border_width=0,
):
# 若文字样式配置项为空则为默认配置
if not textstyle_opts:
textstyle_opts = self._textstyle_opts()
return options.LegendOpts(
textstyle_opts=textstyle_opts,
is_show=is_show,
pos_left=pos_left,
pos_top=pos_top,
orient=orient,
align=align,
padding=padding,
item_width=item_width,
item_height=item_height,
inactive_color=inactive_color,
border_width=border_width,
)
# 提示框配置项
@staticmethod
def _tooltip_opts(
# 是否显示提示框,默认为不显示
is_show=False,
):
return options.TooltipOpts(is_show=is_show)
# 标题配置项
def _title_opts(
self,
# 标题的文字样式默认为黑、加粗、20
title_textstyle_opts=None,
# 副标题的文字样式默认为中黑、常规、14
subtitle_textstyle_opts=None,
# 否显示标题,默认为不显示
is_show=False,
# 标题的文本,默认为空
title=None,
# 副标题的文本,默认为空
subtitle=None,
# 组件距离容器左侧位置,默认为左侧
pos_left="left",
# 组件距离容器上侧位置,默认为上侧
pos_top="top",
# 组件内边距,默认为[上0、右0、下0、左60]
padding=None,
):
# 若标题的文字样式配置项为空则为默认配置
if not title_textstyle_opts:
title_textstyle_opts = self._textstyle_opts(
color="#1D2129", font_weight="bold", font_size=20
)
# 若标题的文字样式配置项为空则为默认配置
if not subtitle_textstyle_opts:
subtitle_textstyle_opts = self._textstyle_opts(font_size=14)
# 若组件内边距为空则为默认配置
if not padding:
padding = [0, 0, 0, 60]
return options.TitleOpts(
title_textstyle_opts=title_textstyle_opts,
subtitle_textstyle_opts=subtitle_textstyle_opts,
is_show=is_show,
title=title,
subtitle=subtitle,
pos_left=pos_left,
pos_top=pos_top,
padding=padding,
)
# 坐标轴配置项
def _axis_opts(
self,
# 坐标轴轴线配置项,默认为中黑
axisline_opts=None,
# 坐标轴刻度配置项,默认为中黑
axistick_opts=None,
# 坐标轴标签配置项-标签旋转
axislabel_rotate=None,
# 坐标轴名称文字样式默认为中黑、常规、12像素
name_textstyle_opts=None,
# 分割线配置项,默认为不显示
splitline_opts=None,
# 坐标轴类型默认为数值轴可选CATEGORY类目轴、TIME时间轴
type_="value",
# 坐标轴名称,默认为空
name=None,
# 是否显示坐标轴,默认为显示
is_show=True,
# 坐标轴名称与轴线之间的距离默认为15
name_gap=15,
# 强制设置坐标轴分割间隔
interval=None,
# 坐标轴的分割段数默认为5
split_number=5,
# 坐标轴两边留白策略
boundary_gap=None,
# 坐标轴刻度最小值
min_=None,
# 坐标轴刻度最大值
max_=None,
):
# 若坐标轴轴线配置项为空则为默认配置
if not axisline_opts:
axisline_opts = self._axisline_opts()
# 若坐标轴刻度配置项为空则为默认配置
if not axistick_opts:
axistick_opts = self._axistick_opts()
# 若坐标轴名称文字样式为空则为默认配置
if not name_textstyle_opts:
name_textstyle_opts = self._textstyle_opts()
if not splitline_opts:
splitline_opts = self._splitline_opts()
return options.AxisOpts(
axisline_opts=axisline_opts,
axistick_opts=axistick_opts,
name_textstyle_opts=name_textstyle_opts,
splitline_opts=splitline_opts,
type_=type_,
name=name,
is_show=is_show,
name_gap=name_gap,
interval=interval,
split_number=split_number,
boundary_gap=boundary_gap,
min_=min_,
max_=max_,
axislabel_opts=self._label_opts(rotate=axislabel_rotate),
)
# 坐标轴轴线配置项
def _axisline_opts(
self,
# 是否显示轴线,默认显示
is_show=True,
# 线样式配置项,默认为中黑
linestyle_opts=None,
):
# 若线样式配置项为空则为默认配置
if not linestyle_opts:
linestyle_opts = self._linestyle_opts()
return options.AxisLineOpts(is_show=is_show, linestyle_opts=linestyle_opts)
# 坐标轴刻度配置项
def _axistick_opts(
self,
# 是否显示坐标轴刻度,默认为否
is_show=False,
# 线样式配置项,默认为中黑
linestyle_opts=None,
# 坐标轴刻度是否朝内,默认为朝内
is_inside=True,
):
# 若线样式配置项为空则为默认配置
if not linestyle_opts:
linestyle_opts = self._linestyle_opts()
return options.AxisTickOpts(
is_show=is_show, is_inside=is_inside, linestyle_opts=linestyle_opts
)
# 视觉映射配置项
def _visualmap_opts(
self,
# 指定visualMapPiecewise组件的最大值
max_=1,
# 如何放置visualMap组件
orient="horizontal",
# visualMap组件离容器左侧的距离
pos_left="center",
# visualMap组件离容器上侧的距离
pos_top="top",
# 数据展示的小数精度
precision=2,
# 文字样式配置项
textstyle_opts=None,
):
# 若文字样式配置项为空则为默认配置
if not textstyle_opts:
textstyle_opts = self._textstyle_opts()
return options.VisualMapOpts(
max_=max_,
orient=orient,
pos_left=pos_left,
pos_top=pos_top,
precision=precision,
textstyle_opts=textstyle_opts,
)
# 系列配置项
# 图元样式配置项
@staticmethod
def _itemstyle_opts(
# 图形透明度
opacity=0.2,
):
return options.ItemStyleOpts(opacity=opacity)
# 文本样式配置项
@staticmethod
def _textstyle_opts(
# 文字颜色,默认为中黑
color="#86909C",
# 文字字体的风格,默认为常规
font_style="normal",
# 文字字体的粗细,默认为常规
font_weight="normal",
# 文字字体的系列,默认为苹方
font_family="PingFang SC",
# 文字字体的大小默认为12
font_size=12,
):
return options.TextStyleOpts(
color=color,
font_style=font_style,
font_weight=font_weight,
font_family=font_family,
font_size=font_size,
)
# 标签配置项
@staticmethod
def _label_opts(
# 是否显示标签
is_show=True,
# 标签的位置
position="inside",
# 文字的颜色,默认为中黑
color="#86909C",
# 文字的字体大小默认为12
font_size=12,
# 文字的字体粗细,默认为常规
font_weight="normal",
# 文字的字体系列,默认为苹方
font_family="PingFang SC",
# 标签旋转
rotate=None,
# 标签内容格式器,默认为空
formatter=None,
# 文字块背景色,默认为无
background_color=None,
# 文字块边框颜色
border_color=None,
# 文字块边框宽度
border_width=None,
# 文字块圆角
border_radius=None,
# 文字块富文本样式
rich=None,
):
return options.LabelOpts(
is_show=is_show,
position=position,
color=color,
font_size=font_size,
font_weight=font_weight,
font_family=font_family,
rotate=rotate,
formatter=formatter,
background_color=background_color,
border_color=border_color,
border_width=border_width,
border_radius=border_radius,
rich=rich,
)
# 线样式配置项
@staticmethod
def _linestyle_opts(
# 线宽默认为1
width=1,
# 线的类型,默认为实线
type_="solid",
# 线的颜色,默认为中黑
color="#86909C",
):
return options.LineStyleOpts(width=width, type_=type_, color=color)
# 分割线配置项
def _splitline_opts(
self,
# 线样式配置项,默认为虚线、浅黑
linestyle_opts=None,
# 是否显示分割线,默认为不显示
is_show=False,
):
# 若线样式配置项为空则为默认配置
if not linestyle_opts:
# 线样式配置项,默认为虚线、浅黑
linestyle_opts = self._linestyle_opts(type_="dashed", color="#E5E6EB")
return options.SplitLineOpts(is_show=is_show, linestyle_opts=linestyle_opts)
# 标记线配置项
def _markline_opts(
self,
# 标记线数据
data,
# 响应和触发鼠标事件,默认为不响应
is_silent=False,
# 标签配置项
label_opts=None,
# 线样式配置项
linestyle_opts=None,
):
# 若标签配置项为空则默认配置
if not label_opts:
label_opts = self._label_opts()
# 线样式配置项为空则默认配置
if not linestyle_opts:
# 线样式配置项,默认为虚线、浅黑
linestyle_opts = self._linestyle_opts(type_="dashed", color="#E5E6EB")
return options.MarkLineOpts(
is_silent=is_silent,
data=data,
label_opts=label_opts,
linestyle_opts=linestyle_opts,
)
# 区域填充样式配置项
@staticmethod
def _areastyle_opts(
# 图形透明度0为透明1为不透明默认为0.2
opacity=0.2,
):
return options.AreaStyleOpts(opacity)
"""
# 条柱和折线图,左侧为条柱图纵坐标,右侧为折线图纵坐标
case "bar+line":
# 先生成柱状图再叠加折线图
bar = (
Bar(init_opts=self._init_opts())
.set_global_opts(
# 显示图例
legend_opts=self.LegendOpts(is_show=True),
# 不显示提示框
tooltip_opts=self.TooltipOpts(),
# X轴配置项
xaxis_opts=self.AxisOpts(
# 以第一个列名作为坐标轴名称
name=dataframe.columns[0],
# 坐标轴轴线配置项
axisline_opts=self.AxisLineOpts(),
# 显示刻线
axistick_opts=self.AxisTickOpts(is_show=True),
# 不显示分割线
splitline_opts=self.SplitLineOpts(),
# 坐标轴名称配置项加粗、14像素
name_textstyle_opts=self.TextStyleOpts(
font_weight="bold", font_size="14px"
),
# 坐标轴标签配置项
axislabel_opts=self.LabelOpts(
# 标签旋转30度逆时针
rotate=30
),
),
# 不显示轴线
yaxis_opts=self.AxisOpts(
# 不显示坐标轴
is_show=False
),
)
# 第一列作为X轴
.add_xaxis(
# 将X轴数值转为字符
xaxis_data=dataframe[dataframe.columns[0]]
.astype("str")
.tolist()
)
# 第二列作为左侧Y轴
.add_yaxis(
# 系列名称
series_name=dataframe.columns[1],
# Y轴数据
y_axis=dataframe[dataframe.columns[1]].tolist(),
# 使用的Y轴的索引存在多个Y轴的时候有用
yaxis_index=0,
# 系列颜色
color="#165DFF",
# 同一系列的柱间距离
category_gap="35%",
# 不同系列的柱间距离
gap="15%",
# 标签配置项
label_opts=self.LabelOpts(
# 标签位置,左侧
position="left",
# 文字字体的粗细,加粗
font_weight="bold",
),
)
.extend_axis(
yaxis=self.AxisOpts(
# 不显示坐标轴
is_show=False
)
)
)
line = (
Line(init_opts=self._init_opts()).add_xaxis(
xaxis_data=dataframe[dataframe.columns[0]]
.astype("str")
.tolist()
)
# 第三列作为右侧Y轴
.add_yaxis(
series_name=dataframe.columns[-1],
y_axis=dataframe[dataframe.columns[-1]].tolist(),
yaxis_index=1,
# 系列颜色,碧涛青
color="#14C9C9",
# 标记的图形
symbol="circle",
# 标记的大小
symbol_size=6,
# 是否平滑曲线
is_smooth=True,
# 折线图在柱状图上
z_level=1,
# 标签配置项
label_opts=self.LabelOpts(
# 标签位置,顶部
position="top",
# 文字字体的粗细,加粗
font_weight="bold",
),
)
)
bar.overlap(line).render(path)
# 双折线图
case "line+line":
# 先生成左侧折线图再叠加右侧折线图
line_left = (
Line(init_opts=self._init_opts())
.set_global_opts(
# 显示图例
legend_opts=self.LegendOpts(is_show=True),
# 不显示提示框
tooltip_opts=self.TooltipOpts(),
# X轴配置项
xaxis_opts=self.AxisOpts(
# 以第一个列名作为坐标轴名称
name=dataframe.columns[0],
# 坐标轴轴线配置项
axisline_opts=self.AxisLineOpts(),
# 显示刻线
axistick_opts=self.AxisTickOpts(is_show=True),
# 不显示分割线
splitline_opts=self.SplitLineOpts(),
# 坐标轴名称配置项加粗、14像素
name_textstyle_opts=self.TextStyleOpts(
font_weight="bold", font_size="14px"
),
# 坐标轴标签配置项
axislabel_opts=self.LabelOpts(
# 标签旋转30度逆时针
rotate=30
),
),
# 不显示轴线
yaxis_opts=self.AxisOpts(
# 不显示坐标轴
is_show=False
),
)
# 第一列作为X轴
.add_xaxis(
# 将X轴数值转为字符
xaxis_data=dataframe[dataframe.columns[0]]
.astype("str")
.tolist()
)
# 第二列作为左侧Y轴
.add_yaxis(
# 系列名称
series_name=dataframe.columns[1],
y_axis=dataframe[dataframe.columns[1]].tolist(),
# 使用的Y轴的索引存在多个Y轴的时候有用
yaxis_index=0,
# 系列颜色
color="#165DFF",
# 标记的图形
symbol="circle",
# 标记的大小
symbol_size=6,
# 是否平滑曲线
is_smooth=True,
# 标签配置项
label_opts=self.LabelOpts(
# 标签位置,左侧
position="left",
# 文字字体的粗细,加粗
font_weight="bold",
),
)
.extend_axis(
yaxis=self.AxisOpts(
# 不显示坐标轴
is_show=False
)
)
)
line_right = (
Line(init_opts=self._init_opts()).add_xaxis(
xaxis_data=dataframe[dataframe.columns[0]]
.astype("str")
.tolist()
)
# 第三列作为右侧Y轴
.add_yaxis(
series_name=dataframe.columns[-1],
y_axis=dataframe[dataframe.columns[-1]].tolist(),
yaxis_index=1,
# 系列颜色,碧涛青
color="#14C9C9",
# 标记的图形
symbol="circle",
# 标记的大小
symbol_size=6,
# 是否平滑曲线
is_smooth=True,
# 折线图在柱状图上
z_level=1,
# 标签配置项
label_opts=self.LabelOpts(
# 标签位置,顶部
position="top",
# 文字字体的粗细,加粗
font_weight="bold",
),
)
)
line_left.overlap(line_right).render(path)
# 热力图
case "heatmap":
chart = (
HeatMap(init_opts=self.InitOpts())
.set_global_opts(
# 不显示图例
legend_opts=self.LegendOpts(),
# 不显示提示框
tooltip_opts=self.TooltipOpts(),
# X轴配置项
xaxis_opts=self.AxisOpts(
# 坐标轴名称
name=None,
# 坐标轴轴线配置项
axisline_opts=self.AxisLineOpts(),
# 显示刻线
axistick_opts=self.AxisTickOpts(is_show=True),
# 不显示分割线
splitline_opts=self.SplitLineOpts(),
# 坐标轴名称配置项加粗、14像素
name_textstyle_opts=self.TextStyleOpts(
font_weight="bold", font_size="14px"
),
# 坐标轴标签配置项
axislabel_opts=self.LabelOpts(
# 标签旋转30度逆时针
rotate=30
),
),
# Y轴配置项
yaxis_opts=self.AxisOpts(
# 坐标轴名称
name=None,
# 坐标轴轴线配置项
axisline_opts=self.AxisLineOpts(),
# 显示刻线
axistick_opts=self.AxisTickOpts(is_show=True),
# 不显示分割线
splitline_opts=self.SplitLineOpts(),
# 坐标轴名称配置项加粗、14像素
name_textstyle_opts=self.TextStyleOpts(
font_weight="bold", font_size="14px"
),
# 坐标轴标签配置项
axislabel_opts=self.LabelOpts(
# 标签旋转30度逆时针
rotate=30
),
),
visualmap_opts=self.VisualMapOpts(),
)
.add_xaxis(
# 索引作为X轴
xaxis_data=dataframe.index.tolist()
)
.add_yaxis(
# 第三个列名作为系列名称
series_name=dataframe.columns[2],
# 列名作为Y轴
yaxis_data=dataframe.columns.tolist(),
# 系列数据项
value=[
[i, j, formatter(dataframe.iloc[i, j])]
for i in range(dataframe.shape[0])
for j in range(dataframe.shape[1])
],
# 标签配置项
label_opts=self.LabelOpts(
# 标签的位置默认为内部
position="inside"
),
)
)
chart.render(path)
# 堆积柱形图
case "stacked_bar":
chart = (
Bar(init_opts=self._init_opts()).set_global_opts(
# 显示图例
legend_opts=self.LegendOpts(is_show=True),
# 不显示提示框
tooltip_opts=self.TooltipOpts(),
# X轴配置项
xaxis_opts=self.AxisOpts(
# 以第一个列名作为坐标轴名称
name=None,
# 坐标轴轴线配置项
axisline_opts=self.AxisLineOpts(),
# 显示刻线
axistick_opts=self.AxisTickOpts(is_show=True),
# 不显示分割线
splitline_opts=self.SplitLineOpts(),
# 坐标轴名称配置项加粗、14像素
name_textstyle_opts=self.TextStyleOpts(
font_weight="bold", font_size="14px"
),
# 坐标轴标签配置项
axislabel_opts=self.LabelOpts(
# 标签旋转30度逆时针
rotate=30
),
),
# 不显示轴线
yaxis_opts=self.AxisOpts(
# 不显示坐标轴
is_show=False
),
)
# 第一列作为X轴
.add_xaxis(
# 将X轴数值转为字符
xaxis_data=dataframe[dataframe.columns[0]]
.astype("str")
.tolist()
)
# 颜色配置项
.set_colors(paint(dataframe.shape[1] - 1))
)
for column in dataframe.columns[1:]:
chart.add_yaxis(
# 系列名称
series_name=column,
# Y轴数据
y_axis=dataframe[column].tolist(),
stack="stacked_bar",
# 同一系列的柱间距离
category_gap="35%",
# 不同系列的柱间距离
gap="15%",
# 标签配置项
label_opts=self.LabelOpts(
# 标签位置,左侧
position="left",
# 文字字体的粗细,加粗
font_weight="bold",
),
))
chart.render(path)
"""