如何使用Seaborn和Matplotlib创建美丽的条形图(包括动画)

图表教程

Python数据可视化教程

Graph created by the author

你好,欢迎来到我的第一个Matplotlib和Seaborn教程。今天,我将向您展示如何将默认条形图转换为具有图标和动画的令人惊叹的可视化效果。

如果你喜欢这种内容,请告诉我。如果是这种情况,我可以创作更多类似的内容! 🙂

您可以在这个仓库中找到代码和预处理数据:simple-bar-chart-tutorial。

让我们开始吧。

第一步:创建默认的条形图

对于这个教程,我创建了一个简单的数据集,其中包含三个流行的开源深度学习框架(TensorflowPyTorchKeras)随时间的总星数。

Screenshot by the author

这里有一个简单的函数,用于为DataFrame中的一行创建简单的条形图。

def create_bar_chart(row, color):        return sns.barplot(        y=row.index.str.capitalize().values,        x=row.values,        orient="h",        saturation=1,        color=color,        width=0.75,    )

为了创建第一个可视化效果,我像这样运行上面的代码。

row = df.iloc[-1]fig = plt.figure(figsize=(12, 7))ax = create_bar_chart(row, color=bar_color)plt.title("GitHub上的总星数")plt.tight_layout()plt.show()

如果我为数据集中的最后一行使用橙色运行此函数,我会得到以下默认条形图。

Bar chart created by the author

在接下来的步骤中,我将创建其他函数,我们可以一起运行代码来改进图表。

让我们试着创建更美观的东西。

第二步:创建主题

首先,我想创建一个函数,以便我可以测试我的图表的不同颜色和字体。

Matplotlib和Seaborn中有大量的方法可以让您修改图形的外观。

我喜欢使用sns.set_style()首先创建一个主题。下面是我用来快速创建新主题的代码片段。

def set_seaborn_style(font_family, background_color, grid_color, text_color):    sns.set_style({        "axes.facecolor": background_color,        "figure.facecolor": background_color,        "grid.color": grid_color,        "axes.edgecolor": grid_color,        "axes.grid": True,        "axes.axisbelow": True,                "axes.labelcolor": text_color,        "text.color": text_color,        "font.family": font_family,        "xtick.color": text_color,        "ytick.color": text_color,        "xtick.bottom": False,        "xtick.top": False,        "ytick.left": False,        "ytick.right": False,        "axes.spines.left": False,        "axes.spines.bottom": True,        "axes.spines.right": False,        "axes.spines.top": False,    })

sns.set_style()还有更多选项,您可能希望以不同的方式使用它。但这是对我最有效的设置。

我经常去colorhunt.co或canva.com寻找灵感来创建配色方案。当我有喜欢的基本颜色时,我会去coolors.co找到需要的颜色。

这是我为本教程创建的配色方案(是的,我喜欢紫色)。

作者的截图

我选择的字体是“PT Mono”,因此我可以运行以下代码。

background_color = "#2F195F"grid_color = "#582FB1"bar_color = "#835ED4"text_color = "#eee"set_seaborn_style(font_family, background_color, grid_color, text_color)

现在,如果我运行创建条形图的原始代码,我会得到以下结果。

作者创建的条形图

这是一个明显的改进,但还不够好。接下来,让我们继续格式化标题文本和轴标签。

步骤3:文本格式化

我注意到的第一件事是轴标签需要更大。图表中显示的所有信息都应该很容易一眼看清。

我不喜欢x轴上的数字外观。我想要写成75K而不是75000,以获得更少的威胁感。

这就是为什么我创建了这个函数。

def format_axes(ax):    ax.tick_params("x", labelsize=20, pad=16)    ax.tick_params("y", labelsize=20, pad=8)        plt.xticks(        ticks=ax.get_xticks()[:-1],        labels=["{}K".format(int(x / 1000)) for x in ax.get_xticks()[:-1]]    )

对于标题,我添加了参数以增加字体大小并调整位置。

以下是使用新修改创建图表的代码。

row = df.iloc[-1]fig = plt.figure(figsize=(12, 7))ax = create_bar_chart(row, color=bar_color)# 新函数format_axes(ax)plt.title("GitHub上的星总数", fontsize=34, y=1.2, x=0.46)plt.tight_layout()plt.show()

以下是结果。

作者创建的条形图

看起来已经很不错了,但现在是时候添加一些图标魔法了。

步骤4:添加图标

将图像和图标添加到图表中很有趣,但也很棘手。并不总是直接将它们放在完美的位置或使用理想的大小。

以下函数通过使用xycoords="data"和我的DataFrame中的值,在我的图表中为每个条形末尾添加图标。

bboxprops内的boxstyle参数创建一个白色圆形背景。

def add_bar_icons(ax, row, background_color, zoom, pad):    for index, (name, value) in enumerate(row.items()):         icon = plt.imread("./icons/{}.png".format(name.lower()))        image = OffsetImage(icon, zoom=zoom, interpolation="lanczos", resample=True, visible=True)        image.image.axes = ax        ax.add_artist(AnnotationBbox(            image, (value, index), frameon=True,            xycoords="data",            bboxprops={                "facecolor": "#fff",                "linewidth": 3,                "edgecolor": background_color,                "boxstyle": "circle, pad={}".format(pad),            }        ))

我想把图标放在白色圆圈上,并在与图表背景相同的深紫色边框中添加边框。

到目前为止,我还没有找到处理zoom参数的好方法,所以我手动调整它以获得正确的尺寸。

现在我创建图表的代码如下。

row = df.iloc[-1]fig = plt.figure(figsize=(12, 7))ax = create_bar_chart(row, color=bar_color)# 新功能format_axes(ax)add_bar_icons(ax, row, background_color, zoom=0.09, pad=0.9)plt.title("GitHub上的总星数", fontsize=34, y=1.2, x=0.46)plt.tight_layout()plt.show()

这就是我得到的结果。

作者创建的柱状图

为了添加星号,我创建了另一个函数,使用xycoords="axes fraction"在图表上的任何位置添加自定义图标。

def add_icon(ax, icon_name, x, y, zoom):    icon = plt.imread("./icons/{}.png".format(icon_name))    image = OffsetImage(icon, zoom=zoom, interpolation="lanczos", resample=True, visible=True)    image.image.axes = ax    ax.add_artist(AnnotationBbox(        image, (x, y), frameon=False,        xycoords="axes fraction",    ))

在下一个操作中,我通过在标题中添加额外的空格并调整xy参数来为星标图标腾出空间,以便将它放在正确的位置。

row = df.iloc[-1]fig = plt.figure(figsize=(12, 7))ax = create_bar_chart(row, color=bar_color)# 新功能format_axes(ax)add_bar_icons(ax, row, background_color, zoom=0.09, pad=0.9)add_icon(ax, "star", x=0.46, y=1.26, zoom=0.13)plt.title("GitHub上的总星数", fontsize=34, y=1.2, x=0.46)plt.tight_layout()plt.show()

现在,我们的柱状图看起来像这样,我们已经接近完成了。

作者创建的柱状图

看起来不错,但现在我想将图表转换为更通用的格式。

并且让我们通过添加一些填充来解决那种挤压的外观。

第5步:将图表转换为图像

对于这一部分,我创建了一个函数,它将图形转换为PIL图像。PIL图像在我们教程的最后几步中更易于使用。

def create_image_from_figure(fig):    plt.tight_layout()    fig.canvas.draw()    data = np.frombuffer(fig.canvas.tostring_rgb(), dtype=np.uint8)    data = data.reshape((fig.canvas.get_width_height()[::-1]) + (3,))    plt.close()         return Image.fromarray(data)

我还创建了以下函数,以在图表周围添加填充。

def add_padding_to_chart(chart, left, top, right, bottom, background):    size = chart.size    image = Image.new("RGB", (size[0] + left + right, size[1] + top + bottom), background)    image.paste(chart, (left, top))    return image

这是一份新版本的生成图表的代码,我用我们的新方法替换了标准的plt.show()

row = df.iloc[-1]fig = plt.figure(figsize=(12, 7))ax = create_bar_chart(row, color=bar_color)\plt.title("GitHub上的总星数", fontsize=34, y=1.2, x=0.46)# 新功能format_axes(ax)add_bar_icons(ax, row, background_color)add_icon(ax, "star", 0.46, 1.26)image = create_image_from_figure(fig)image = add_padding_to_chart(image, 0, 10, 10, 10, background_color)

这里是结果。

作者创建的条形图

太棒了!

我们已经将默认的条形图变成了更美丽的形式。

让我们以奖励部分结束。

奖励:创建动画

因为我们将代码分成了可重用的函数,所以创建动画相当容易。

我们所要做的就是使用不同的值多次运行代码,并将帧拼合在一起。

这里是一个 for 循环,我取了数据集中最后的2000行中的200行。我还通过设置 xlim=(0, 185000) 来固定 x 轴,以避免闪烁的值。

images = []for i in tqdm.tqdm(range(1, 2000, 10)):    row = df.iloc[-i]    fig = plt.figure(figsize=(12, 7))    ax = create_bar_chart(row, color=bar_color)    ax.set(xlim=(0, 185000))    plt.title("GitHub上的总星数", fontsize=34, y=1.2, x=0.46)    format_axes(ax)    add_bar_icons(ax, row, background_color, zoom=0.09, pad=0.9)    add_icon(ax, "star", 0.46, 1.26)    image = create_image_from_figure(fig)    image = add_padding_to_chart(image, 20, 20, 40, 0, background_color)    images.append(image)    images.reverse()

接下来,我使用 imageio 创建了一个 GIF。

# 添加最后一帧的几个重复项,以创建延迟images = images + [images[-1] for _ in range(20)]imageio.mimwrite('./animation.gif', images, duration=50)

这是结果。

作者创建的可视化

这标志着本教程的结束,希望您喜欢它。如果您喜欢,请分享这个故事并订阅我的频道。

您还应该在 Twitter 上关注我:@oscarle3o

谢谢您的阅读。

下次见!:)