使用-Streamlit-和-Python-展示飙升的森林火灾数量-一种强大的方法

使用 Streamlit 和 Python 展示飙升的森林火灾数量:一种强大的方法

towardsdatascience.com/showcasing-soaring-wildfire-counts-with-streamlit-and-python-a-powerful-approach-e759b3da1a77/

CL-415 在行动(在意大利)。图片由 Maarten Visser 提供 - 维基媒体共享

CL-415 在行动(在意大利)。图片由 Maarten Visser 提供 – 维基媒体共享

Python 的 Streamlit 非常适合从 GIS 数据集中创建交互式地图。

允许观众输入的交互式地图可用于更深入的分析和讲故事。

Python 的 Streamlit 是这项工作的正确工具。它可以与 pandas 一起使用,以便轻松创建和操作数据框。

让我们用一个关于一个非常紧迫的问题——看似日益严重的森林火灾——的深入和详细的数据集来测试这一点。加拿大自然资源部管理的网站上有一个出色的公共森林火灾数据集。

使用这个详细的数据集,让我们采取模块化的数据分析方法,创建:

  • 一个显示一段时间内(例如,特定年份)加拿大所有森林火灾的静态地图

  • 一个允许用户选择较短时间段(例如,按年份的下拉菜单)以查看更详细数据的交互式地图

  • 一个显示更多粒度——省级火灾数量的条形图

Python 的 Streamlit 能为我们做这件事吗?

让我们试试吧!

问题

北美在过去 10-15 年中森林火灾尤其具有破坏性。最近的加利福尼亚火灾加剧了公众对这一日益关注的问题的认识。

对于我们加拿大人来说,我们最近的夏天常常是在我们的露台上凝视着烟雾弥漫的雾霾,想知道风是否会转向,从而将我们的社区置于危险之中。

森林火灾烟雾在左侧,右侧为正常天气(作者摄影)

森林火灾烟雾在左侧,右侧为正常天气(作者摄影)

在对话中经常出现的一个问题是与过去相比,森林火灾的情况变得有多糟糕?

为了更好地回答这个问题,并拥有一个好的数据集,我们可以创建一系列可视化,讲述加拿大(我的家乡)森林火灾随时间(例如按年份)变化的数据故事。

我们可以将这些可视化作为地图和图表来分析和了解情况从那时到现在是如何变化的。

数据集

为了展示森林火灾的月度影响,我们可以使用数据集创建一系列数据可视化,显示随时间推移的森林火灾。

加拿大自然资源部(加拿大政府的一个部门)拥有过去 75 年森林火灾的非常详细公开数据集。

所有这些加拿大的历史火灾数据都存储在这里

在网站上,我们可以向下滚动查看和下载:

对于这个教程,我们可以下载包含超过 200 公顷火灾的数据集(作者截图)

对于这个教程,我们可以下载包含超过 200 公顷火灾的数据集(作者截图)

下载文件后,我们首先需要将其解压。

解压后,我们可以选择名为 NFDB_point_20240613_large_fires.txtThe 的文件

选择大型火灾 CSV 文件。(作者截图)

选择大型火灾 CSV 文件。(作者截图)

此文件是一个逗号分隔的文件(CSV)。为了方便使用,我将我的文件重命名为 NFDB_large_fires.csv

本数据集中的每条记录代表一次独特的森林火灾,并包含几个关键字段:

  • 年份:火灾发生的年份。

  • LATITUDELONGITUDE:火灾的地理坐标。

  • FIRE_ID:每个火灾的唯一标识符。

  • SIZE_HA:火灾的大小,单位为公顷。

  • FIRENAME:火灾的给定名称或最近的地理特征名称。

  • REP_DATE:火灾首次报告的日期。

  • OUT_DATE:火灾被扑灭的日期(如果有的话)。

  • CAUSE:火灾报告的原因(例如,自然原因如闪电或人为活动)。

了解可用的字段现在可以帮助我们提出一些关于数据的问题。

我们希望得到解答的问题

现在我们可以针对这个数据集可以回答的问题提出一些问题:

  1. 加拿大的野火发生在哪里?

  2. 加拿大每年有多少起火灾?

  3. 随着时间的推移,火灾数量是否逐年增加?

为了热身,让我们先创建一个简单的静态地图,展示 2023 年加拿大火灾的分布情况。

使用 Python 创建静态地图

首先,我们可以使用 Python 创建一个静态地图,具体使用的是 pydeck 库。

目标是生成一个显示 2023 年加拿大森林火灾的地图,并将其保存为可以在网页浏览器中查看的 HTML 文件。

注意:本教程中所有的代码和数据文件都可在 Github 上找到:这里

1. 加载数据和过滤

在这里,我们使用 pandas 库加载数据集,包含历史森林火灾数据:

import pandas as pd
import pydeck as pdk

# Load the forest fire data
fire_data_path = 'NFDB_large_fires.csv'
df_fires = pd.read_csv(fire_data_path)

# Filter data for the year 2023
df_fires_2023 = df_fires[df_fires['YEAR'] == 2023]

文件 NFDB_large_fires.csv 被读取到一个名为 df_fires 的 DataFrame 中。这个数据集可能包含诸如火灾位置(纬度,经度)、发生年份、火灾 ID 和公顷大小等信息。

由于我们只对可视化2023 年发生的森林火灾感兴趣,我们过滤 DataFrame,只包含YEAR列等于 2023 的行。这个过滤后的 DataFrame,df_fires_2023,将被用来在地图上叠加数据点。

2. 定义 PyDeck 图层

数字化设计地图需要创建在地图上显示的图层。对于这个地图,我们需要为我们的点数据创建一个图层。地图上的每场火灾都有一个经纬度,它定义了地图上火灾的确切位置:

layer = pdk.Layer(
    "ScatterplotLayer",
    data=df_fires_2023,
    get_position='[LONGITUDE, LATITUDE]',
    get_radius=10000,
    get_color=[255, 0, 0],
    pickable=True
)

此代码块使用pydeck.Layer创建一个散点图层。地图上的每场森林火灾都由一个红色圆形标记表示。

  • "ScatterplotLayer":指定要渲染的图层类型。在这里,我们使用散点图层来显示点。

  • data=df_fires_2023:将过滤后的 DataFrame 作为数据源传递。

  • get_position='[LONGITUDE, LATITUDE]':指定每个火灾点的坐标,使用 DataFrame 中的LONGITUDELATITUDE列。

  • get_radius=10000:设置每个标记的半径(以米为单位)。

  • get_color=[255, 0, 0]:以 RGB 格式定义标记的颜色。[255, 0, 0]对应红色。

  • pickable=True:启用与点的交互,允许当用户悬停在标记上时显示工具提示或弹出信息。

3. 设置地图的视图窗口

我们需要为地图视图的初始加载设置经纬度点:

view_state = pdk.ViewState(
    latitude=df_fires_2023['LATITUDE'].mean(),
    longitude=df_fires_2023['LONGITUDE'].mean(),
    zoom=3,
    pitch=0
)

view_state定义了地图加载时的初始视图。它指定相机的纬度经度缩放级别俯仰角(倾斜)。

  • latitudelongitude:将地图中心设置为过滤数据集中所有火灾点的平均纬度和经度。

  • zoom=3:设置缩放级别,确保地图覆盖加拿大的大部分地区。

  • pitch=0:将地图的俯仰角设置为 0,意味着视图是直接从上方查看。

4. 渲染和保存地图

r = pdk.Deck(layers=[layer], initial_view_state=view_state, tooltip={"text": "Location: {FIRENAME}nSize: {SIZE_HA} ha"})

html_file_path = "forest_fires_2023_map.html"
r.to_html(html_file_path)

这行代码创建了一个Deck对象,它结合了散点图层(layer)和视图窗口(view_state)。此外,还定义了一个工具提示,当用户将鼠标悬停在地图上的点上时显示信息。工具提示显示:

  • 位置:火灾的唯一标识符。

  • 大小:火灾的大小(公顷)。

最后,使用r.to_html()将地图保存为 HTML 文件。生成的文件forest_fires_2023_map.html包含一个功能齐全的静态地图,可以在任何网络浏览器中打开。这使用户能够交互式地探索森林火灾数据,而无需 Python 或 PyDeck。

详细结果:

由作者生成的 HTML 静态地图,在浏览器中查看。(截图由作者提供)

由作者生成的 HTML 静态地图,在浏览器中查看。(截图由作者提供)

好的,看起来 2023 年有相当多的火灾。然而,我们没有关于这些数据的进一步背景信息。例如:

  • 这个数字比其他年份多还是少?

  • 这是火灾的正常分布吗?

因此,以这个静态地图为基础,让我们尝试将 PyDeck 与 Streamlit 集成,以允许动态交互。例如,我们可以向用户提供一个包含多个年份数据的下拉菜单。

使用 Streamlit 创建动态地图

在这个阶段,让我们首先允许用户从下拉菜单中选择一个特定的年份。然后在该年份的地图上可视化火灾。

使用 Streamlit,我们可以创建一个动态的 Web 应用程序,允许用户从下拉菜单中选择一个特定的年份,并在交互式地图上可视化相应的森林火灾。

让我们一步一步地实现它!

1. 额外库

我们需要向我们的代码中添加 2 个库:

import streamlit as st
import plotly.express as px
  • streamlit: 允许我们创建交互式 Web 应用程序

  • plotly.express: 允许我们创建交互式图表和图形。

2. 添加年份选择下拉菜单

为了交互性,让我们添加一个下拉菜单,允许用户从一系列年份中选择一个年份。对于这个教程,让我们将年份范围设置为 2000 年到 2023 年:

# Dropdown menu for year selection
year_selected = st.selectbox("Select Year", options=list(range(2000, 2023)))

这行 Python 代码使用 st.selectbox 创建一个下拉菜单,允许用户从 2000 年到 2023 年中选择一个特定的年份。所选年份存储在变量 year_selected 中。

3. 根据所选年份过滤数据

一旦用户选择了一个年份,然后我们可以过滤 pandas 数据框,只使用该年的数据:

# Filter data to include only fires for the selected year, replace NaN with None
df_fires_selected = df_fires[df_fires['YEAR'] == year_selected].copy()
df_fires_selected = df_fires_selected.where(pd.notnull(df_fires_selected), None)

YEAR 列与所选年份匹配。数据集中的任何 NaN 值都替换为 None,以确保在渲染地图时正确的 JSON 格式。

4. 创建 PyDeck 图层

之前使用过 GIS 数据的人都知道,我们在地图上以图层的形式呈现数据。我们只为地图添加一个数据层,用于火灾点:

# Create the PyDeck layer for fire points
layer = pdk.Layer(
    "ScatterplotLayer",
    data=df_fires_selected,
    get_position='[LONGITUDE, LATITUDE]',
    get_radius=15000,
    get_color=[255, 0, 0],
    pickable=True
)

使用 pydeck.Layer 创建散点图层,用于在地图上显示火灾。关键参数包括:

  • "ScatterplotLayer": 指定图层类型。

  • data=df_fires_selected: 使用所选年份的筛选数据集。

  • get_position='[LONGITUDE, LATITUDE]': 指定每个火灾点的坐标。

  • get_radius=15000: 将标记半径设置为 15,000 米,以获得更好的可见性。

  • get_color=[255, 0, 0]: 将标记颜色设置为红色。

  • pickable=True: 启用交互,允许在悬停于点上方时显示工具提示信息。

5. 设置地图的视口

视口为我们提供了一个查看地图基础图层窗口。对于我们的地图,我们希望将其中心定位在加拿大,以便我们可以查看特定年份的所有火灾:

# Set the viewport to show all of Canada
view_state = pdk.ViewState(
    latitude=60.0, longitude=-100.0, zoom=2.6, pitch=0
)

使用 pdk.ViewState 配置视口,将地图中心定位在加拿大,缩放级别为 2.6,以显示整个国家。

6. 创建 Pydeck Deck.gl 地图

现在我们已经准备好了火灾数据图层,并且视图已设置为加拿大,我们可以加载基础地图:

# Create the deck.gl map
map_deck = pdk.Deck(
    layers=[layer],
    initial_view_state=view_state,
    map_style="mapbox://styles/mapbox/satellite-v9",
    tooltip={"text": "Province: {SRC_AGENCY} Date: {MONTH}/{DAY} Size: {SIZE_HA} ha"}
)
  • pdk.Deck: 创建对象以渲染地图。

  • layers: 从我们的火灾数据图层加载数据

  • map_style:设置为使用 Mapbox 样式的卫星视图。如果您想更改基础地图的类型,可以在这里进行更改。

  • tool_tip:显示每个火灾的省份、日期和大小(当用户将鼠标悬停在标记上时)

地图的介绍就到这里!现在让我们添加一个柱状图来讲述更详细的故事。

7. 为按省份统计的火灾数量生成柱状图

对于柱状图,让我们按省份分解当前所选年份的火灾数据:

# Generate fire counts by province
province_fire_counts_df = df_fires_selected['SRC_AGENCY'].value_counts().reset_index()
province_fire_counts_df.columns = ['SRC_AGENCY', 'Fire Count']

# Plot the bar chart using Plotly
fig = px.bar(
    province_fire_counts_df,
    orientation='h',
    x='Fire Count',
    y='SRC_AGENCY',
    labels={'Fire Count': 'Number of Fires', 'SRC_AGENCY': 'Province'},
    color_discrete_sequence=['red']
)

# Display map (70%) and bar chart (30%) side by side with custom width
col1, col2 = st.columns([7, 3])
with col1:
    st.pydeck_chart(map_deck)
with col2:
    st.plotly_chart(fig, use_container_width=True)

柱状图使用 Plotly 可视化按报告机构(省份)统计的火灾数量。

柱状图配置为显示水平条形,火灾数量在 x 轴上,省份代码在 y 轴上。

条形用红色着色,以匹配地图标记的红色主题。最后,我们添加了代码 (st.columns) 来将地图与图表并排显示。这种布局确保了干净直观的用户界面。

完美!

8. 显示总火灾数量

最后,在地图和图表下方,为了提供更多背景信息,让我们添加特定年份的火灾总数:

# Display total fire count below the map and chart
total_fire_count = df_fires_selected.shape[0]
st.markdown(f"### Total Fires for the Year {year_selected}: <span style='color:red; font-weight:bold;'>{total_fire_count}</span>", unsafe_allow_html=True)

火灾数量以粗体红色文本显示,以强调。

我们美丽的结果:

在浏览器中查看的交互式 Streamlit 地图(作者截图)

在浏览器中查看的交互式 Streamlit 地图(作者截图)

哇!这确实看起来很棒。用户可以从下拉菜单中选择年份,地图、柱状图和总数值都会根据所选年份更新。

现在,如果我们逐年查看图表和数字,这个数据集显示 2023 年对加拿大来说是一个真正的火灾灾难年。

为了验证这个观察结果,我们可以从这个数据集中创建一个图表,计算每年的总火灾数量:

展示 1945-2023 年总火灾数量的时间序列折线图(作者截图)

展示 1945-2023 年总火灾数量的时间序列折线图(作者截图)

此图表显示 2023 年是加拿大历史上最糟糕的野火年。

总计 923 场火灾(大于 200 公顷)超过了 1989 年创下的 775 场旧记录。

注意:此图表的代码也可在 Github 上找到(在此

就这样,故事结束了!

总结来说…

在这次练习之前,我不是 Streamlit 专家,我发现创建一个交互式的 Python Streamlit 仪表板的过程比我预期的要简单得多。

如果您像本文中概述的那样采用模块化方法,您可以使用 任何 包含 GIS 数据的 CSV 文件来应用它。

值得注意的是,我原本预计需要使用 JSON 或 geoJSON 文件来准确地在地图上显示点(即按国家和省份)。由于我们的数据集中包含“省份”字段,这极大地简化了数据的分类,尤其是对于柱状图。

尝试这段代码!并且请留下评论——让我知道进展如何!

GITHUB 仓库:这里

注意:本文中使用的火灾点数据集由加拿大开放政府许可下的 CWFIS 数据集分发。更多详情可以在这里找到这里


如果这种类型的故事正合你的胃口,并且你希望支持我作为一位作家,请订阅我的Substack

订阅深度数据

在 Substack 上,我发布的是双周通讯和文章,这些内容在其他我创作内容的平台上是找不到的。

posted @ 2026-03-28 09:54  布客飞龙I  阅读(6)  评论(0)    收藏  举报