在模仿中精进数据可视化04:旧金山街道树木分布可视化

简介: 在模仿中精进数据可视化04:旧金山街道树木分布可视化

1 简介

开门见山,今天我们要模仿的数据可视化作品来自 「#TidyTuesday」 活动于2020年1月28日发布的「旧金山街道树木数据集」下的众多参赛作品中,由Philippe Massicotte创作的(如图1所示)非常受欢迎的 「Street trees of San Francisco」

图1

原作者使用的工具是R语言,而今天的文章内容,我就将带大家学习如何在Python中模仿图1的风格进行类似数据信息的可视化展示(其实原作品有一些令人困惑的瑕疵,因此我在下文中在一些地方采用了与原作者不同的分析方式,因此最终的成品与原作品有一些不同之处)。

2 模仿过程

今天我们要模仿的这张图,咋一看上去似乎略复杂,但如果你曾经阅读过我的「基于geopandas的空间数据分析」系列文章,就一下子可以在脑中将此图构成进行分解:

2.1 过程分解

我们仔细观察原作品,可以get到其主要视觉元素是将统计出的数值映射到每个社区面色彩之上,且外围的轮廓描边,很明显是整个地区对应整体的向外缓冲区,再辅以道路网,使得整张图看起来显得很“精密”。

结合我们手头的数据:旧金山社区「面」数据、有登记的街道树木「点」数据,至于道路网「线」数据我们则可以利用第三方库osmnx进行获取(建议利用conda install -c conda-forge osmnx进行安装)。

将过程拆分为下列步骤:

  • 「数据准备」

首先我们需要读入已有的数据并进行相应的矢量化:

图2

而路网数据我们则可以利用osmnx进行在线获取,只需传入我们的旧金山面数据bbox范围,配合

osmnx进行获取即可:

图3

接着我们在上述数据基础上对每个社区面内部的街道树木数量进行统计并对数据进行分箱,配上预设区间的色彩值:

# 统计每个社区内部的树木数量
sf_trees = \
(
    gpd
    # 空间连接
    .sjoin(left_df=sf,
           right_df=trees,
           op='contains',
           how='left')
    # 按照name分组计数(这里未连接到任何数的社区被
    # 记为1本质上是错误的,但我们绘图分段后这一点不影响)
    .groupby('name')
    .agg({
        'name': 'count',
        'geometry': 'first'
    })
    .rename(columns={'name': '数量'})
    .reset_index(drop=False)
    # 直接转为GeoDataFrame
    .pipe(gpd.GeoDataFrame, crs='EPSG:4326')
)
sf_trees['颜色'] = (
    pd
    .cut(sf_trees['数量'], 
         bins=[0, 2500, 5000, 7500, 10000, max(sf_trees['数量'])], 
         labels=['#e4f1e1', '#c0dfd1', '#67a9a2', '#3b8383', '#145e64'])
)

最后别忘记了我们作为轮廓的缓冲区生成:

# 生成轮廓缓冲区
sf_bounds = gpd.GeoSeries([sf.buffer(0.001).unary_union], crs='EPSG:4326')
  • 「主要视觉元素绘制」

做好这些准备后我们直接就可以先将图像的主体元素绘制出来:

import matplotlib.pyplot as plt
from matplotlib import font_manager as fm
# 设置全局默认字体
plt.rcParams['font.sans-serif'] = ['Times New Roman']
fig, ax = plt.subplots(figsize=(6, 6))
# 设置背景色
ax.set_facecolor('#333333')
fig.set_facecolor('#333333')
# 图层1:缓冲区轮廓
ax = (
    sf_bounds
    .plot(ax=ax, facecolor='none', edgecolor='#cccccc', linewidth=1)
)
# 图层2:带有树木统计信息的社区面
ax = (
    sf_trees
    .plot(color=sf_trees['颜色'], edgecolor='#333333',
          linewidth=0.5, ax=ax)
)
# 图层3:osm路网
ax = (
    roads
    .plot(linewidth=0.05, edgecolor='#3c3d3d',
          ax=ax)
)
# 设置x轴
ax.set_xticks([-122.5, -122.45, -122.4, -122.35])
ax.set_xticklabels(['122.5°W', '122.45°W', '122.4°W', '122.35°W'])
# 设置y轴
ax.set_yticks([37.72, 37.74, 37.76, 37.78, 37.8, 37.82])
ax.set_yticklabels(['37.72°N', '37.74°N', '37.76°N', '37.78°N', '37.8°N', '37.82°N'])
# 设置坐标轴样式
ax.tick_params(axis='both', labelcolor='#737373', color='none', labelsize=8)
# 隐藏周围的spines线条
ax.spines['left'].set_color('none')
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.spines['bottom'].set_color('none')
# 导出图像
fig.savefig('图4.png', dpi=600, bbox_inches='tight')

图4

  • 「辅助视觉元素的添加」

接下来我们只需要补充上各种点睛之笔的小元素即可,其中值得一提的是下方的图例我们用inset_axes()插入子图的方式灵活实现。

并且外部字体文件的使用也是很添彩的,我们这里就分别在「标题」「刻度标签」处使用到了两种特殊的字体(你可以在开头的Github仓库找到我用到的所有字体文件):

fig, ax = plt.subplots(figsize=(6, 6))
# 设置背景色
ax.set_facecolor('#333333')
fig.set_facecolor('#333333')
# 图层1:缓冲区轮廓
ax = (
    sf_bounds
    .plot(ax=ax, facecolor='none', edgecolor='#cccccc', linewidth=1)
)
# 图层2:带有树木统计信息的社区面
ax = (
    sf_trees
    .plot(color=sf_trees['颜色'], edgecolor='#333333',
          linewidth=0.5, ax=ax)
)
# 图层3:osm路网
ax = (
    roads
    .plot(linewidth=0.05, edgecolor='#3c3d3d',
          ax=ax)
)
# 设置x轴
ax.set_xticks([-122.5, -122.45, -122.4, -122.35])
ax.set_xticklabels(['122.5°W', '122.45°W', '122.4°W', '122.35°W'])
# 设置y轴
ax.set_yticks([37.72, 37.74, 37.76, 37.78, 37.8, 37.82])
ax.set_yticklabels(['37.72°N', '37.74°N', '37.76°N', '37.78°N', '37.8°N', '37.82°N'])
# 设置坐标轴样式
ax.tick_params(axis='both', labelcolor='#737373', color='none', labelsize=8)
# 隐藏周围的spines线条
ax.spines['left'].set_color('none')
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.spines['bottom'].set_color('none')
# 以插入子图的方式添加下方图例
ax_bar = ax.inset_axes((0.25, -0.12, 0.5, 0.015))
ax_bar.set_facecolor('#333333')
ax_bar.spines['left'].set_color('none')
ax_bar.spines['right'].set_color('none')
ax_bar.spines['top'].set_color('none')
ax_bar.spines['bottom'].set_color('none')
ax_bar.bar(range(5), [1]*5, width=0.975, color=['#e4f1e1', '#c0dfd1', '#67a9a2', '#3b8383', '#145e64'])
ax_bar.set_yticks([])
ax_bar.set_xticks([i+0.5 for i in range(4)])
ax_bar.set_xticklabels(['2500', '5000', '7500', '10000'], 
                       fontdict={'fontproperties': fm.FontProperties(fname="RobotoCondensed-Regular.ttf")})
ax_bar.tick_params(color='none', labelcolor='#ffffff', labelsize=8, pad=0)
ax.set_title('Street trees of San Francisco', 
             fontsize=24,
             color='#ffffff',
             pad=40,
             fontproperties=fm.FontProperties(fname="Amaranth-Bold.ttf"))
ax.text(0.5, 1.08, '''There are a total of 192987 trees in San Francisco regrouped into 571 species.
The district with the most number of trees is Mission whereas the one with
the least number of trees is LincoLn Park / Ft. Miley.''', transform=ax.transAxes, ma='center',
        ha='center', va='top', color='#ffffff')
ax.text(0.5, -0.22, 'Visualization by CNFeffery', fontsize=8,
        color='#737373', ha='center', transform=ax.transAxes)
# 导出图像
fig.savefig('图5.png', dpi=600, bbox_inches='tight')

图5

目录
相关文章
|
存储 异构计算 Windows
ps2023汉化版百度网盘下载photoshop2023自带神经滤镜安装包
最近,ps迎来了2023的版本,这次的版本提升针对windows11做了特别优化,启动速度比win10快了很多。期盼已久的Win版 PS 2023 终于来了,小编也是通过特殊渠道搞来的,本期带来的WIN版本支持一键安装激活,一次安装永久免费使用 众所周知,版本越高,需要的电脑配置也就越来越高。下面放一下2023版本的配置供大家参考。需要注意的是这些版本不再支持windows7系统,仅支持win10及以上的操作系统。
10670 3
|
缓存 Java Shell
Android 内存泄露,怎样查找,怎么产生的内存泄露?
Android 内存泄露,怎样查找,怎么产生的内存泄露?
198 0
|
数据采集 关系型数据库 MySQL
python-协程(async、await关键字与asyncio)
python-协程(async、await关键字与asyncio)
1573 0
|
前端开发 算法 安全
软件开发过程详解
【8月更文第20天】在当今数字化时代,软件开发已成为企业和组织获取竞争优势的关键。一个高效的软件开发过程不仅能够确保最终产品的质量,还能有效控制成本和时间。本文将详细介绍软件开发的各个阶段,包括需求分析、设计、编码与实现、测试与质量保证以及维护与升级,并通过实例帮助读者更好地理解这些概念。
1246 0
|
设计模式 开发者 Python
Python编程中的设计模式:工厂方法模式###
本文深入浅出地探讨了Python编程中的一种重要设计模式——工厂方法模式。通过具体案例和代码示例,我们将了解工厂方法模式的定义、应用场景、实现步骤以及其优势与潜在缺点。无论你是Python新手还是有经验的开发者,都能从本文中获得关于如何在实际项目中有效应用工厂方法模式的启发。 ###
|
数据采集 DataWorks 数据挖掘
提升数据分析效率:DataWorks在企业级数据治理中的应用
【8月更文第25天】本文将探讨阿里巴巴云的DataWorks平台如何通过建立统一的数据标准、规范以及实现数据质量监控和元数据管理来提高企业的数据分析效率。我们将通过具体的案例研究和技术实践来展示DataWorks如何简化数据处理流程,减少成本,并加速业务决策。
926 54
设计模式-工厂模式 Factory Pattern(简单工厂、工厂方法、抽象工厂)
这篇文章详细解释了工厂模式,包括简单工厂、工厂方法和抽象工厂三种类型。每种模式都通过代码示例展示了其应用场景和实现方法,并比较了它们之间的差异。简单工厂模式通过一个工厂类来创建各种产品;工厂方法模式通过定义一个创建对象的接口,由子类决定实例化哪个类;抽象工厂模式提供一个创建相关或依赖对象家族的接口,而不需要明确指定具体类。
设计模式-工厂模式 Factory Pattern(简单工厂、工厂方法、抽象工厂)
|
JavaScript 前端开发 测试技术
精通Selenium:从基础到高级的网页自动化测试策略
【10月更文挑战第6天】随着Web应用变得越来越复杂,手动进行功能和兼容性测试变得既耗时又容易出错。自动化测试因此成为了现代软件开发不可或缺的一部分。Selenium是一个强大的工具集,它支持多种编程语言(包括Python),允许开发者编写脚本来模拟用户与Web页面的交互。本文将带领读者从Selenium的基础知识出发,逐步深入到高级的应用场景,通过丰富的代码示例来展示如何高效地进行网页自动化测试。
1982 5
|
移动开发 资源调度 前端开发
React Router V6 useRoutes的使用
【8月更文挑战第29天】 React Router V6 useRoutes的使用
583 3
|
SQL druid Java
JDBC和数据库连接池-两个工具类-JDBCUtilsByDruid和BasicDAO
JDBC和数据库连接池-两个工具类-JDBCUtilsByDruid和BasicDAO
540 0