如何使用matplotlib创建口红图
#教程 #python #matplotlib #datavisualization

今天,我将向您展示如何创建口红图表,以可视化值越低的指标进度,

越好。

我已经准备了一个关于死亡率和疾病的简单数据集,因此您可以专注于创建可视化。

数据来自World Bank,如果您想了解更多信息,我已经在新的免费新闻Abiaoqian中写了有关可视化的文章。

让我们开始。


步骤1-导入库

第一个也是最简单的部分是导入所需的库,例如pandas和matplotlib。

import numpy as np
import pandas as pd

import seaborn as sns
import matplotlib.pyplot as plt

from PIL import Image
from matplotlib.lines import Line2D

恭喜,您刚刚完成了步骤1! ð¥³


步骤2-创建海洋风格

接下来,我想创建一个颜色和选择字体。在寻找美丽的颜色时,例如CoolorsColorhunt等网站是很棒的资源。

这是我为本教程创建海洋风格的代码和设置。

FONT_FAMILY = "serif"
BACKGROUND_COLOR = "#FAE8E0"
TEXT_COLOR = "#33261D"
BAR_COLOR = "#EF7C8E"

sns.set_style({
    "axes.facecolor": BACKGROUND_COLOR,
    "figure.facecolor": BACKGROUND_COLOR,

    "text.color": TEXT_COLOR,
    "font.family": FONT_FAMILY,

    "xtick.bottom": False,
    "xtick.top": False,
    "ytick.left": False,
    "ytick.right": False,

    "axes.spines.left": False,
    "axes.spines.bottom": False,
    "axes.spines.right": False,
    "axes.spines.top": False,
})

我正在删除所有的刻度和线条以创建一个干净的可视化,并且网格不会在我们的口红图中添加任何有价值的信息。


步骤3-阅读数据

您可以像我在下面的代码中一样直接从URL读取CSV。

df = pd.read_csv(
    "https://raw.githubusercontent.com/oscarleoo/matplotlib-tutorial-data/main/mortality-and-decease.csv"
)

这是数据框的外观。

Screenshot of the dataframe

大多数值除了per外,它都是自称的,它显示了每行的比例。例如,最新的“产妇死亡率”价值为100,000个出生中的223个。


步骤4-添加条

现在是时候添加一些数据了。

我正在为2000和最新值添加条。由于我的目标是显示每个值的相对减小,因此我将每一行除以其2000值。

这意味着2000的每个栏将达到1,因此它只是一个视觉助手,不会添加任何其他信息。

这是我添加条的功能。

def add_bars(ax, x, width, alpha, label):
    sns.barplot(
        ax=ax, x=x, y=[i for i in range(len(x))], label=label,
        width=width, alpha=alpha,
        color=BAR_COLOR,
        edgecolor=TEXT_COLOR,
        orient="h"
    )

我创建一个图形并像这样运行add_bars()函数。

fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(18, 2.7 * len(df)))

add_bars(
    ax=ax, x=df["2000"] / df["2000"],
    width=0.55, alpha=0.2, label="2000"
)

add_bars(
    ax=ax, x=df["latest_value"] / df["2000"],
    width=0.7, alpha=1, label="Latest"
)

我们到目前为止的代码的结果。

A first bar chart

让我们继续。


步骤5-格式化轴

每行的名称在没有线路破坏的情况下可以使用。这就是为什么我创建以下功能将\n添加到几个地方的字符串的原因。

def split_name(name, limit=20):
    split = name.split()
    s = ""

    for s_ in split:
        if len(s.split("\n")[-1] + s_) > limit:
            s += "\n" + s_
        else:
            s += " " + s_

    return s.strip()

我还希望增加字体大小并删除不必要的信息以使图表可读。现在创建可视化的代码现在看起来像这样。

fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(18, 2.7 * len(df)))
...

ax.set(xlabel=None, ylabel=None, xticks=[])
ax.tick_params("y", labelsize=28, pad=32)
ax.tick_params("x", labelsize=20, pad=16)

ax.set_yticks(
    ticks=[i for i in range(len(df))],
    labels=[split_name(n, limit=19) for n in df["indicator_name"]],
    linespacing=1.7, va="center"
)

这是更新的结果。

Barchart with formatted axes

让我们添加一些其他信息。


步骤5-添加有用的信息

您始终想确保用户了解他们在看什么。现在,我们没有这样的信息。

对于启动器,我想添加当前值,我使用以下功能进行。

def add_info_text(ax, row, index):
    value = round(row["latest_value"], 1)
    per = row["per"]
    year = row["latest_year"]
    text = "{:,} out of\n{:,} ({})".format(value, per, year)

    ax.annotate(
        text=text, 
        xy=(0.02, index), 
        color="#fff", 
        fontsize=24,
        va="center", 
        linespacing=1.7
    )

,并且由于目的是显示每个度量的相对减小与2000年相比其值,所以我有另一个函数显示每行的变化。

def add_change_text(ax, row, index):
    change = round(100 * row["change"], 1)
    text = "{:,}%".format(change)
    x = row["latest_value"] / row["2000"] + 0.02

    ax.annotate(
        text="{:,}%".format(change), xy=(x, index), fontsize=22,
        va="center",  linespacing=1.7
    )

我在循环下添加两个功能。

fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(18, 2.7 * len(df)))
...

for index, row in df.reset_index().iterrows():
    add_info_text(ax, row, index)
    add_change_text(ax, row, index)

这是输出。

Lipstick chart with added information

它开始看起来不错。


步骤6-添加标题和传奇

在此步骤中,我只是使用一些内置的matplotlib函数来添加标题和传奇。由于我们在add_bars()中定义了label,因此许多样式都是自动的。

除了定义标题和传奇外,我还使用Line2D添加了一个边框以进行视觉效果。

fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(18, 2.7 * len(df)))
...

line = Line2D([-0.33, 1.0], [-0.9, -0.9], color=TEXT_COLOR)
line.set_clip_on(False)
ax.add_artist(line)

title = "Lipstick Chart - Relative\nDecreases Compared\nto 2000"
plt.title(title, x=-0.32, y=1.11, fontsize=58, ha="left", linespacing=1.6)
plt.legend(bbox_to_anchor=(0.75, 1.14), loc='lower center', borderaxespad=0, ncol=1, fontsize=44, edgecolor="#FAE8E0")

这是图表现在的样子。

Lipstick chart with title and legend


步骤7-创建图像并添加填充

图表看起来有些局促,因此最后一步是添加一些填充。我是通过将图形变成具有以下功能的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

我们现在编写了创建我们针对的数据可视化所需的所有代码。

这是使用所有功能创建最终口红图的完整代码段。

fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(18, 2.7 * len(df)))

add_bars(
    ax=ax, x=df["2000"] / df["2000"],
    width=0.55, alpha=0.2, label="2000"
)

add_bars(
    ax=ax, x=df["latest_value"] / df["2000"],
    width=0.7, alpha=1, label="Latest"
)

ax.set(xlabel=None, ylabel=None, xticks=[])
ax.tick_params("y", labelsize=28, pad=32)
ax.tick_params("x", labelsize=20, pad=16)

ax.set_yticks(
    ticks=[i for i in range(len(df))],
    labels=[split_name(n, limit=20) for n in df["indicator_name"]],
    linespacing=1.7, va="center"
)

for index, row in df.reset_index().iterrows():
    add_info_text(ax, row, index)
    add_change_text(ax, row, index)

line = Line2D([-0.35, 1.0], [-0.9, -0.9], color=TEXT_COLOR)
line.set_clip_on(False)
ax.add_artist(line)

title = "Lipstick Chart - Relative\nDecreases Compared\nto 2000"
plt.title(title, x=-0.32, y=1.11, fontsize=58, ha="left", linespacing=1.6)
plt.legend(bbox_to_anchor=(0.75, 1.14), loc='lower center', borderaxespad=0, ncol=1, fontsize=44, edgecolor="#FAE8E0")

image = create_image_from_figure(fig)
image = add_padding_to_chart(image, 20, 50, 10, 50, BACKGROUND_COLOR)

这是成品。

Final lipstick chart

我们完成了!


结论

感谢您阅读本教程;希望您学会了一些技巧,可以重用数据可视化项目。

如果您想查看更多教程和美丽的数据可视化,请在此处关注我,订阅Data Wonder,并在Twitter上订阅oscarl3o

下次见。