首页 > 编程语言 >[python] 基于PyWaffle库绘制华夫饼图

[python] 基于PyWaffle库绘制华夫饼图

时间:2024-04-30 21:44:18浏览次数:33  
标签:华夫 python Waffle plt values fig rows PyWaffle 方块

华夫饼图Waffle chart是一种独特而直观的图表,用于表示分类数据。它采用网格状排列的等大小方格或矩形,每个方格或矩形分配不同的颜色或阴影来表示不同的类别。这种可视化方法有效地传达了每个类别在整个数据集中的相对比例。本文介绍如何使用基于Python的PyWaffle库绘制华夫饼图。PyWaffle开源仓库地址见:PyWaffle,PyWaffle官方文档见:PyWaffle-docs。本文代码下载地址:Python-Study-Notes.

PyWaffle安装方式如下:

pip install -U pywaffle

# PyWaffle库依赖于matplotlib绘制图片
import matplotlib.pyplot as plt
from pywaffle import Waffle

目录

1 使用简介

1.1 基本使用

1.1.1 基础绘图

PyWaffle库通过其Waffle类提供了一种便捷的方式来创建华夫饼图。每个类别的方格占比可以通过参数values进行设置,数据可以是列表、字典和Pandas.DataFrame结构。然后可以使用rows和columns参数来定制行数和列数。只需要指定其中一个参数,另一个参数可以根据values的总和推断出来。

# 新建绘图图像
fig = plt.figure(
    FigureClass=Waffle,
    rows=5,
    # columns=10,
    values=[30, 16, 4]
)
# 保存结果
# fig.savefig("plot.png", bbox_inches="tight")
plt.show()

png

如果value参数输入的是比例值,则必须设置rows和columns属性来定义值的显示方式。

fig = plt.figure(
    FigureClass=Waffle,
    rows=2,
    columns=5,
    values=[0.2, 0.5, 0.3]
)
plt.show()

png

如果values总和与rows*columns的结果不一致,Waffle类将values解释为各部分的相对比例。

fig = plt.figure(
    FigureClass=Waffle,
    rows=5,
    columns=5,
    values=[30, 16, 4]
)
plt.show()

png

当字典传递给values时,键将用作标签并显示在图例中,注意图例会默认显示。

plt.figure(
    FigureClass=Waffle,
    rows=5,
    values={'Cat': 30, 'Dog': 16, 'Bird': 4},
    legend={'loc': 'upper left', 'bbox_to_anchor': (1, 1), 'frameon': False} # 修改图例
)
plt.show()

png

不显示图例。

fig = plt.figure(
    FigureClass=Waffle,
    rows=5,
    values={'Cat': 30, 'Dog': 16, 'Bird': 4},
    legend={'loc': 'upper left', 'bbox_to_anchor': (1, 1), 'frameon': False} # 修改图例
)

# 移除legend
fig.axes[0].get_legend().remove()

plt.show()

png

1.1.2 类别比例设置

如果values的总和与rows*columns的结果不一致,Waffle类将解释values为各部分的相对比例。如下所示各个类别方块数输出为小数,小数位的取舍会导致不同的展示效果:

# nearest, ceil or floor

import numpy as np

target = {'Cat': 48, 'Dog': 46, 'Bird': 3, 'Fish': 9}

values = np.array(list(target.values()))

values_ratio = values /sum(values)
# 华夫饼图结构
row = 2
col = 5
# 各个类别方块数
values_ratio * row *col
array([4.52830189, 4.33962264, 0.28301887, 0.8490566 ])

为此,Waffle类通过rounding_rule参数来处理小数位取舍问题。rounding_rule可选值为nearest(默认), ceil或floor。具体如下:

nearest

nearest表示将方块的数值四舍五入到最接近的整数。

# 各个类别方块数
np.round(values_ratio * row *col, 0).astype(int)
array([5, 4, 0, 1])
np.round(values_ratio*10,0).astype(int)

plt.figure(
    FigureClass=Waffle,
    rows=2,
    columns=5,
    values=target,
    rounding_rule='nearest', # 默认值
    legend={'loc': 'upper left', 'bbox_to_anchor': (1, 1), 'frameon': False} # 修改图例
)
plt.show()

png

ceil

ceil表示将方块的数值向上舍入到最接近的整数。然而,在nearest和ceil模式下,方块的总和可能会超过华夫饼图的预设长度,超出预设长度的方块将不会显示。这也意味着,输入类别位置越靠后,其方块显示的数值越可能不准确。

# 各个类别方块数
np.ceil(values_ratio * row *col).astype(int)
array([5, 5, 1, 1])
np.round(values_ratio*10,0).astype(int)

plt.figure(
    FigureClass=Waffle,
    rows=2,
    columns=5,
    values=target,
    rounding_rule='ceil', # 默认值
    legend={'loc': 'upper left', 'bbox_to_anchor': (1, 1), 'frameon': False} # 修改图例
)
plt.show()

png

floor

floor表示将方块的数值向下舍入到最接近的整数,由于这种舍入方式的特性,绘制的方块数可少于预设的数量。

# 各个类别方块数
np.floor(values_ratio * row *col).astype(int)
array([4, 4, 0, 0])
np.round(values_ratio*10,0).astype(int)

plt.figure(
    FigureClass=Waffle,
    rows=2,
    columns=5,
    values=target,
    rounding_rule='floor', # 默认值
    legend={'loc': 'upper left', 'bbox_to_anchor': (1, 1), 'frameon': False} # 修改图例
)
plt.show()

png

如果想要在华夫饼图中完整显示所有类别,但又不希望方块数量过多,一种有效的方法是只设定rows或columns中的一个参数,然后将每个类别的数值除以最小类别的数值,以确保最少类别的方块数量为1,同时将其他类别的数值显示为相对于最少类别的比例。

target_ = target.copy()
# 找到最小值
min_value = min(target_.values())

# 更新字典中每个键对应的值
target_ = {key: int(value / min_value) for key, value in target.items()}
print(target_)
{'Cat': 16, 'Dog': 15, 'Bird': 1, 'Fish': 3}
np.round(values_ratio*10,0).astype(int)

plt.figure(
    FigureClass=Waffle,
    rows=4,
    values=target_,
    legend={'loc': 'upper left', 'bbox_to_anchor': (1, 1), 'frameon': False} # 修改图例
)
plt.show()

png

1.2 样式设置

1.2.1 展示效果设置

标题、标签和图例

data = {'Cat': 30, 'Dog': 16, 'Bird': 4}
fig = plt.figure(
    FigureClass=Waffle,
    rows=5,
    columns=10,
    values = data,
    title={
        'label': 'Example plot',
        'loc': 'center',
        'fontdict': {
            'fontsize': 20
        }
    },
    labels=[f"{k} ({int(v / sum(data.values()) * 100)}%)" for k, v in data.items()],
    legend={
        #'labels': [f"{k} ({v}%)" for k, v in data.items()],  # labels可以在legend中设置
        'loc': 'lower left',
        'bbox_to_anchor': (0, -0.2),
        'ncol': len(data),
        'framealpha': 0,
        'fontsize': 12
    },
    # 设置颜色条,支持Pastel1, Pastel2, Paired, Accent, Dark2, Set1, Set2, Set3, tab10, tab20, tab20b, tab20c
    cmap_name="Accent",
    facecolor='#DDDDDD',  # 绘图背景色

    # 也可以单独设置颜色
    # colors=["#232066", "#983D3D", "#DCB732", "F12F34"]

)

png

方块宽高比设置

参数block_aspect_ratio通过更改块的宽度与块的高度之比来控制方块的形状。默认block_aspect_ratio为1,也就是每个方块都是正方形。

fig = plt.figure(
    FigureClass=Waffle,
    rows=5,
    values=[30, 16, 4],
    block_aspect_ratio=1.618
)

png

方块间距控制

参数interval_ratio_x和interval_ratio_y分别负责调节块之间的水平和垂直间距。具体来说,interval_ratio_x代表了块之间的水平间距与块宽度的比率,而interval_ratio_y则反映了块之间的垂直间距与块高度的比率。

fig = plt.figure(
    FigureClass=Waffle,
    rows=5,
    values=[30, 16, 4],
    interval_ratio_x=1,
    interval_ratio_y=0.5
)

png

起始方块绘图位置

starting_location用于设置起始方块的位置。这个参数可输入包括 "NW(西北角、左上角)"、"SW(默认值,西南角,右下角)"、"NE(东北角,右上角)" 和 "SE(东南角,右下角)"。

fig = plt.figure(
    FigureClass=Waffle,
    rows=5,
    values=[30, 16, 4],
    starting_location='SE'
)

png

绘图方向

默认情况下PyWaffle逐列绘制方块,因此类别是按水平方向排列绘制的,如果按行绘制方块,则设置vertical参数为True。

fig = plt.figure(
    FigureClass=Waffle,
    rows=5,
    values=[30, 16, 4],
    vertical=True
)

png

类别方块排列方式

block_arranging_style参数用于控图中各类别方块的排列方式,具体如下:

  • normal: 默认方式,方块会按照常规的网格模式进行排列。
  • new-line: 每个类别的方块都会从新的一行开始排列。这意味着,如果你有多个类别,每行只有一个类别的图标。
  • snake: 方块会按照“蛇形”或“之字形”的模式进行排列。
# new-line排列
fig = plt.figure(
    FigureClass=Waffle,
    rows=5,
    values=[12, 22, 20, 4],
    vertical=True,
    block_arranging_style='new-line'
)

png

# snake排列
fig = plt.figure(
    FigureClass=Waffle,
    rows=5,
    values=[12, 22, 20, 4],
    block_arranging_style='snake'
)

png

1.2.2 绘图框架自定义

子图绘制

PyWaffle可以通过添加子图的方式,实现在同一张图中显示多个华夫饼图。以下是示例:

import pandas as pd
# 创建一个产量表格
data = pd.DataFrame(
    {
        'labels': ['Car', 'Truck', 'Motorcycle'],
        'Factory A': [44183, 12354, 3246],
        'Factory B': [29198, 7678, 2556],
        'Factory C': [9013, 4079, 996],
    },
).set_index('labels')
fig = plt.figure(
    FigureClass=Waffle,
    plots={
        311: { # 311数字对应于 matplotlib中subplot 的位置编码方式,311表示一个三行一列的布局中的第一个子图
            'values': data['Factory A'] / 1000,   # 将实际数字转换为合理的块数
            'labels': [f"{k} ({v})" for k, v in data['Factory A'].items()],
            'legend': {'loc': 'upper left', 'bbox_to_anchor': (1.05, 1), 'fontsize': 8},
            'title': {'label': 'Vehicle Production of Factory A', 'loc': 'left', 'fontsize': 12}
        },
        312: {
            'values': data['Factory B'] / 1000,
            'labels': [f"{k} ({v})" for k, v in data['Factory B'].items()],
            'legend': {'loc': 'upper left', 'bbox_to_anchor': (1.2, 1), 'fontsize': 8},
            'title': {'label': 'Vehicle Production of Factory B', 'loc': 'left', 'fontsize': 12}
        },
        313: {
            'values': data['Factory C'] / 1000,
            'labels': [f"{k} ({v})" for k, v in data['Factory C'].items()],
            'legend': {'loc': 'upper left', 'bbox_to_anchor': (1.3, 1), 'fontsize': 8},
            'title': {'label': 'Vehicle Production of Factory C', 'loc': 'left', 'fontsize': 12}
        },
    },
    rows=5,   # 应用于所有子图的外部参数,与下面相同
    cmap_name="Paired", 
    rounding_rule='ceil', 
    figsize=(5, 5)
)

fig.suptitle('Vehicle Production by Vehicle Type', fontsize=14, fontweight='bold')
# 说明1个方块代表多少车辆
fig.supxlabel('1 block = 1000 vehicles', fontsize=8, ha='right')
Text(0.5, 0.01, '1 block = 1000 vehicles')

png

现有轴绘图

Pywaffle提供Waffle.make_waffle方法来在现有的matplotlib轴上绘图,而不需要重新初始化Waffle实例。

fig = plt.figure()
ax = fig.add_subplot(111)


ax.set_title("Axis Title", loc= "right")
ax.set_aspect(aspect="equal")

Waffle.make_waffle(
    ax=ax,  # 利用现有绘图对象
    rows=5, 
    columns=10, 
    values=[30, 16, 4], 
    title={"label": "Waffle Title", "loc": "left"}
)

png

此外可以利用make_waffle与已定义坐标轴来显示多个数据结果。

import matplotlib.patches as mpatches 

data = {  
    2021: [175, 139, 96],  
    2022: [232, 187, 126],  
    2023: [345, 278, 195],  
    2024: [456, 389, 267]  
}  
  
df = pd.DataFrame(data,index=['cat', 'dog', 'cow'])  
  
number_of_bars = len(df.columns)
colors = ["darkred", "red", "darkorange"]

# Init the whole figure and axes
fig, axs = plt.subplots(nrows=1,
                        ncols=number_of_bars,
                        figsize=(8,6),)

# Iterate over each bar and create it
for i,ax in enumerate(axs):
    
    col_name = df.columns[i]
    values = df[col_name]/1000
    Waffle.make_waffle(
        ax=ax, 
        rows=20,
        columns=5,
        values=values,
        title={"label": col_name, "loc": "left"},
        colors=colors,
        vertical=True,
        font_size=12
    )
    
fig.suptitle('Animal Type Data Display',
             fontsize=14, fontweight='bold')

legend_labels = df.index
legend_elements = [mpatches.Patch(color=colors[i],
                                  label=legend_labels[i]) for i in range(len(colors))]

fig.legend(handles=legend_elements,
           loc="upper right",
           title="Animal Types",
           bbox_to_anchor=(1.04, 0.5),
           framealpha = 0,
           fontsize=12)
plt.subplots_adjust(right=0.85)
plt.show()

png

添加其他matplotlib绘图组件

fig = plt.figure(
    FigureClass=Waffle,
    rows=5,
    values=[30, 16, 4]
)
fig.text(
    x=0.5,
    y=0.5,
    s="hello world!",
    ha="center",
    va="center",
    rotation=30,
    fontsize=40,
    color='black',
    alpha=0.3,
    bbox={
        'boxstyle': 'square', 
        'lw': 3, 
        'ec': 'gray', 
        'fc': (0.9, 0.9, 0.9, 0.5), 
        'alpha': 0.3
    }
)
Text(0.5, 0.5, 'hello world!')

png

1.2.3 方格样式变换

Pywaffle支持字符和图标来变换方格样式。

字符

Pywaffe允许一个或多个Unicode字符来替换华夫饼图中方格的样式。可用的Unicode字符见:unicode-table

fig = plt.figure(
    FigureClass=Waffle,
    rows=5,
    values=[30, 16, 4],
    colors=["#4C8CB5", "#B7CBD7", "#C0C0C0"],
    characters='

标签:华夫,python,Waffle,plt,values,fig,rows,PyWaffle,方块
From: https://www.cnblogs.com/luohenyueji/p/18168746

相关文章

  • python+requests爬取B站视频保存到本地
    importosimportdatetimefromdjango.testimportTestCase#Createyourtestshere.importrequestsimportreimportjsonimportsubprocessfromconcurrent.futuresimportThreadPoolExecutordefdownload_video(url):#file_path='django3+dr......
  • 《最新出炉》系列入门篇-Python+Playwright自动化测试-42-强大的可视化追踪利器Trace
    1.简介在我们日常执行自动化测试工作的过程中,经常会遇到一些偶发性的bug,但是因为bug是偶发性的,我们不一定每次执行都能复现,所以我们在测试执行的时候,追踪用例执行就变得非常重要了。playwright提供了一个PlaywrightTraceViewer工具来追踪测试执行,这是一个GUI工具,我们可以通过它......
  • python airllm调用本地大模型
    官方demohttps://github.com/lyogavin/Anima/blob/main/air_llm/examples/run_all_types_of_models.ipynb这里使用百川本地fromairllmimportAirLLMLlama2,AutoModelMAX_LENGTH=128#默认会去抱脸网下载,很慢,而且需要xx上网#model=AutoModel.from_pretrained("baichua......
  • python匿名函数、内置函数以及各类高阶函数等
    【一】匿名函数【1】函数的分类#【1】正规函数deflogin():...login()#【2】匿名函数(无名函数)#语法:lambda参数:表达式#lambda:匿名函数的关键字#参数可以放位置参数以及关键字参数...#表达式:其实本质上是返回值【2】定义匿名函数Python使用lamb......
  • C/C++、Java 与 Python 中未初始化变量的处理比较
    在C/C++中,未初始化的变量的值是不确定的,可能是随机的。 在Python中,如果直接使用未初始化的变量,会引发NameError异常。Python要求变量在使用前必须进行赋值或初始化。 而在Java中,直接使用未初始化的局部变量会导致编译错误,必须先对变量进行初始化。 C++和Java在字......
  • Python调用Graylog APi 分析401错误登录日志
    ret_lst处理完成后是一个list,内容如下:[{'c_ip':'10.10.202.139','uname':'ee'},{'c_ip':'10.10.202.139','uname':'tt'},{'c_ip':'192.168.195.131','uname......
  • Python: unZip
     importosimportsocketimportstructfromunidecodeimportunidecodeimportreimportjsonimportrequestsfrombs4importBeautifulSoupimportgzipimportzipfilefrompathlibimportPathfromzipfileimportZipFileclassCzip:"""......
  • python
                                搜索复制......
  • pip debug —— 查看当前版本的python解释器支持的wheel包类型
    在pip安装依赖时,我们可能会遇到依赖包无法下载成功的情况解决办法:去寻找对应版本的wheel包下载到本地搜寻wheel包网址:以python-ladp为例格式示例:python_ldap-2.5.1-cp27-cp27m-win32.whl2.5.1代表版本号cp27代表支持python27版本win32代表支持系统位数可以通过pipdeb......
  • 关于JSON转字符串后前端与python得到的结果不一致的问题,及对象按key排序
    背景:哈希码参数校验防参数篡改。前端下发接口时对参数按约定秘钥和逻辑进行加密,后端在获取到请求后对请求参数以同样的秘钥和逻辑加密计算得出哈希值,再与请求的哈希值对比,如果不一致则证明参数被篡改。前端代码:对json对象进行了按key排序1letdataTmp=this.de......