首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >利用折线图探索伦敦不同行政区动物救助变化趋势

利用折线图探索伦敦不同行政区动物救助变化趋势

作者头像
HsuHeinrich
发布2025-11-20 11:42:59
发布2025-11-20 11:42:59
80
举报
文章被收录于专栏:HsuHeinrichHsuHeinrich

利用折线图探索伦敦不同行政区动物救助变化趋势

代码语言:javascript
复制
import matplotlib.patches as patches
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

from matplotlib.lines import Line2D
# 默认字体
plt.rcParams.update({"font.family": "Fira Sans Compressed"})

数据探索

以下数据如果有需要的同学可关注公众号HsuHeinrich,回复【数据可视化】自动获取~

代码语言:javascript
复制
# 导入数据
animal_rescues = pd.read_csv("https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2021/2021-06-29/animal_rescues.csv")

# animal_group_parent大写处理
animal_rescues["animal_group_parent"] = animal_rescues["animal_group_parent"].str.capitalize()

animal_rescues.head()
image-20240129181733785
image-20240129181733785

image-20240129181733785

animal_group_parent:动物种群 cal_year:救助年份 borough_code:行政区码

代码语言:javascript
复制
gb_london_boroughs_grid = pd.read_csv("https://raw.githubusercontent.com/hafen/grid-designer/master/grids/gb_london_boroughs_grid.csv")

borough_names = gb_london_boroughs_grid.rename(columns={"code_ons": "borough_code"})
borough_names.head()
image-20240129181804267
image-20240129181804267

image-20240129181804267

borough_code:行政区代码 name:行政区名称

代码语言:javascript
复制
# 数据预处理

# 保留2021年之前的数据
rescues_borough = animal_rescues.query("cal_year < 2021").reset_index()

# 区分猫与非猫
rescues_borough["animal_group_parent"] = np.where(
    rescues_borough["animal_group_parent"] == "Cat", "Cat", "Not_Cat"
)

# 汇总统计
rescues_borough = (
    rescues_borough.groupby(["cal_year", "borough_code", "animal_group_parent"])
    .size()
    .to_frame("n")
    .reset_index()
)

# 数据透视
rescues_borough = rescues_borough.pivot(
    index=["cal_year", "borough_code"], 
    columns="animal_group_parent", 
    values="n"
).reset_index()

# 匹配borough_names
rescues_borough = pd.merge(rescues_borough, borough_names, how="left", on="borough_code")
rescues_borough = rescues_borough.dropna(subset=["name"])

# 将行政区的row/col各减1,因为python是按照0开始索引的
rescues_borough["row"] -= 1
rescues_borough["col"] -= 1

# 创建行政区的位置信息
df_idxs = rescues_borough[["row", "col", "name"]].drop_duplicates()
NAMES = df_idxs["name"].values
ROWS = df_idxs["row"].values.astype(int)
COLS = df_idxs["col"].values.astype(int)

基本折线图

代码语言:javascript
复制
# 自定义基础变量
BLUE = "#3D85F7"
BLUE_LIGHT = "#5490FF"
PINK = "#C32E5A"
PINK_LIGHT = "#D34068"
GREY40 = "#666666"
GREY25 = "#404040"
GREY20 = "#333333"
BACKGROUND = "#F5F4EF"
# 绘制一张子图看看效果

# 初始布局
fig, ax = plt.subplots(figsize=(8, 5))

# 选择一个行政区 
df = rescues_borough[rescues_borough["name"] == "Enfield"]

# YEAR 为x轴
YEAR = df["cal_year"].values
# 猫的救援数量、非猫的救援数量作为y轴
CAT = df["Cat"].values
NOT_CAT = df["Not_Cat"].values

# 绘制折线图
ax.plot(YEAR, CAT, color=BLUE)
ax.plot(YEAR, NOT_CAT, color=PINK)

# 区域填充

# 当CAT > NOT_CAT时填充BLUE_LIGHT
ax.fill_between(
    YEAR, CAT, NOT_CAT, where=(CAT > NOT_CAT), 
    interpolate=True, color=BLUE_LIGHT, alpha=0.3
)

# 当CAT <= NOT_CAT时填充PINK_LIGHT
ax.fill_between(
    YEAR, CAT, NOT_CAT, where=(CAT <= NOT_CAT),
    interpolate=True, color=PINK_LIGHT, alpha=0.3
);
output_12_0
output_12_0
代码语言:javascript
复制
# 将上述的子图过程封装成函数
def single_plot(x, y1, y2, name, ax):
    '''
    x: year数组
    y1: 猫的救援数量数组
    y2: 非猫的救援数量数组
    name: 行政区名称
    ax: 子图所在位置
    '''
    ax.plot(x, y1, color=BLUE)
    ax.plot(x, y2, color=PINK)

    ax.fill_between(
        x, y1, y2, where=(y1 > y2), 
        interpolate=True, color=BLUE_LIGHT, alpha=0.3
    )

    ax.fill_between(
        x, y1, y2, where=(y1 <= y2),
        interpolate=True, color=PINK_LIGHT, alpha=0.3
    );

    ax.set_facecolor(BACKGROUND)
    fig.set_facecolor(BACKGROUND)


    xticks = [2010, 2015, 2020]
    ax.set_xticks(xticks)
    ax.set_xticks([2012.5, 2017.5], minor=True)
    # added a 'size' argument
    ax.set_xticklabels(xticks, color=GREY40, size=10)

    yticks = [0, 10, 20]
    ax.set_yticks(yticks)
    ax.set_yticks([5, 15, 25], minor=True)
    ax.set_yticklabels(yticks, color=GREY40, size=10)
    ax.set_ylim((-1, 26))

    ax.grid(which="minor", lw=0.4, alpha=0.4)
    ax.grid(which="major", lw=0.8, alpha=0.4)
    
    ax.yaxis.set_tick_params(which="both", length=0)
    ax.xaxis.set_tick_params(which="both", length=0)
    
    ax.spines["left"].set_color("none")
    ax.spines["bottom"].set_color("none")
    ax.spines["right"].set_color("none")
    ax.spines["top"].set_color("none")
    ax.set_title(name, weight="bold", size=9, color=GREY20)

多面板子图

代码语言:javascript
复制
# 自定义变量
NROW = len(rescues_borough["row"].unique())
NCOL = len(rescues_borough["col"].unique())

# 绘制多子图
fig, axes = plt.subplots(NROW, NCOL, figsize=(12, 10), sharex=True, sharey=True)

for i, name in enumerate(NAMES):
    # 选择指定name
    df = rescues_borough[rescues_borough["name"] == name]
    
    # 获取坐标轴
    ax = axes[ROWS[i], COLS[i]]
    
    # 获取对应的x、y1、y2
    YEAR = df["cal_year"].values
    CAT = df["Cat"].values
    NOT_CAT = df["Not_Cat"].values
    
    # 调用函数绘制
    single_plot(YEAR, CAT, NOT_CAT, name, ax)
output_15_0
output_15_0
代码语言:javascript
复制
# 移除空面板的子图
for i in range(7):
    for j in range(8):
        # 子图包含线条(即含有数据)
        if axes[i, j].lines:
            continue
        # 否则删除该子图
        else:
            axes[i, j].remove()
fig
output_16_0
output_16_0
代码语言:javascript
复制
# 自定义刻度标签

# 每一行的第一个非空子图显示y轴标签
for i in range(7):
    first_in_row = True
    for j in range(8):
        if first_in_row and axes[i, j].lines:
            axes[i, j].yaxis.set_tick_params(labelleft=True)
            first_in_row = False


# 每一列的最后一个非空子图显示x轴标签
for j in range(8):
    first_in_col = True
    for i in reversed(range(7)): # note the 'reversed()'
        if first_in_col and axes[i, j].lines:
            axes[i, j].xaxis.set_tick_params(labelbottom=True)
            first_in_col = False

fig
output_17_0
output_17_0

额外的信息

代码语言:javascript
复制
# 创建图例样式
handles = [
    Line2D([], [], c=color, lw=1.2, label=label)
    for label, color in zip(["cats", "other"], [BLUE, PINK])
]

# 将图例添加至fig中
fig.legend(
    handles=handles,
    loc=(0.75, 0.94),
    ncol=2,           # 图例为两列,即1行*2列展示
    columnspacing=1,  # 图例的列间距
    handlelength=1.2, # 险段长度
    frameon=False     # 无框
)


# 创建填充式图例样式
cats = patches.Patch(facecolor=BLUE_LIGHT, alpha=0.3, label="more cats")
other = patches.Patch(facecolor=PINK_LIGHT, alpha=0.3, label="more other")

fig.legend(
    handles=[cats, other],
    loc=(0.75, 0.9),
    ncol=2,         
    columnspacing=1, 
    handlelength=2,
    handleheight=2, 
    frameon=False,   
)

# 标题
fig.text(
    x=0.05, y=0.975, s="Rescues of\ncats vs other animals by\nthe London fire brigade\n2009-2020",
    color=GREY25, fontsize=26, fontfamily="KyivType Sans", fontweight="bold",
    ha="left", 
    va="top", 
    ma="left"  # 多行左对齐
)

# 著作信息
fig.text(
    x=0.95, y=0.025, s="Source: London.gov · Graphic: Georgios Karamanis", 
    fontsize=11, 
    ha="right",  
    va="baseline" 
)

# 自定义子图空间边距
fig.subplots_adjust(left=0.05, right=0.95, bottom=0.05, top=0.95, hspace=0.3, wspace=0.08)
fig
output_19_0
output_19_0

参考:Time series with filled area and custom facetting in Matplotlib[1]

共勉~

参考资料

[1]

Time series with filled area and custom facetting in Matplotlib: https://python-graph-gallery.com/web-time-series-and-facetting-with-matplotlib/

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-11-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 HsuHeinrich 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 利用折线图探索伦敦不同行政区动物救助变化趋势
    • 数据探索
    • 基本折线图
    • 多面板子图
    • 额外的信息
      • 参考资料
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档