# -*- 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) """