森林野火故事2.0:一眼看穿!使用 Panel 和 hvPlot 視覺化 ⛵

ShowMeAI發表於2022-11-22
森林野火故事2.0:一眼看穿!使用 Panel 和 hvPlot 視覺化 ⛵

? 作者:韓信子@ShowMeAI
? 資料分析實戰系列https://www.showmeai.tech/tutorials/40
? 本文地址https://www.showmeai.tech/article-detail/335
? 宣告:版權所有,轉載請聯絡平臺與作者並註明出處
? 收藏ShowMeAI檢視更多精彩內容

森林野火故事2.0:一眼看穿!使用 Panel 和 hvPlot 視覺化 ⛵

?Panel 是 Python 中一個非常棒的可以用作製作資料儀表板的工具庫,基於它可以輕鬆構建資料視覺化看板。

森林野火故事2.0:一眼看穿!使用 Panel 和 hvPlot 視覺化 ⛵

在本篇內容中,ShowMeAI 綜合 Python 視覺化與呈現技能,使用 Panel 製作一個儀表盤看板,可以互動檢視美國野火記錄的資訊。

森林野火故事2.0:一眼看穿!使用 Panel 和 hvPlot 視覺化 ⛵

? 匯入工具庫

我們本次需要用到的工具庫包括資料庫工具、Python 資料處理工具、視覺化工具和看板工具,我們先把這些工具庫匯入,程式碼如下:

# 資料庫
import sqlite3

# 資料處理
import numpy as np
import pandas as pd

# 視覺化與儀表盤
import holoviews as hv 
import colorcet as cc
import panel as pn

from holoviews.element.tiles import EsriImagery
from datashader.utils import lnglat_to_meters
import hvplot.pandas
hv.extension('bokeh')

? 資料準備

用於本次視覺化的資料集包含 1992 年至 2015 年間在美國發生的超過 180 萬起野火。?美國野火資料集可以在 ShowMeAI 的百度網盤地址下載。

? 實戰資料集下載(百度網盤):公✦眾✦號『ShowMeAI研究中心』回覆『實戰』,或者點選 這裡 獲取本文 [28]基於Panel和hvPlot的視覺化互動看板實戰案例美國野火 FPA_FOD_20170508.sqlite 資料集

ShowMeAI官方GitHubhttps://github.com/ShowMeAI-Hub

我們希望構建一個資料儀表板,幫助我們更好地瞭解導致這些野火的原因、發生地點等資訊。我們匯入資料並選出需要的資訊,程式碼如下:

# 連線資料庫
conn = sqlite3.connect('../data/FPA_FOD_20170508.sqlite')

# 選取資訊:經緯度、原因描述、火勢大小與等級、日期、所在州、年份 等
df = pd.read_sql_query("SELECT LATITUDE, LONGITUDE, STAT_CAUSE_DESCR, FIRE_SIZE, FIRE_SIZE_CLASS, DISCOVERY_DATE, CONT_DATE, STATE, FIRE_YEAR FROM fires", conn)

# 資料中有一些不在美國的記錄,刪除它們
df = df.loc[(df.loc[:,'STATE']!='AK') & (df.loc[:,'STATE']!='HI') & (df.loc[:,'STATE']!='PR')]

# 計算野火持續時間
df['BURN_TIME'] = df['CONT_DATE'] - df['DISCOVERY_DATE']

# 檢視資料
df.head()
森林野火故事2.0:一眼看穿!使用 Panel 和 hvPlot 視覺化 ⛵

? 野火地圖

我們先把所有歷史火災繪製成熱度地圖,這樣我們可以直觀看到空間分佈。藉助 ?hvPlot 可以很容易完成(它利用 Datashader 來柵格化我們的 180 萬個點,使得它們更易於渲染)。

森林野火故事2.0:一眼看穿!使用 Panel 和 hvPlot 視覺化 ⛵

具體的程式碼如下:

map_tiles = EsriImagery().opts(alpha=0.5, width=700, height=480, bgcolor='black')
plot = df.hvplot(
    'LONGITUDE', 
    'LATITUDE', 
    geo=True,
    kind='points', 
    rasterize=True, 
    cmap=cc.fire, 
    cnorm='eq_hist',  
    colorbar=True).opts(colorbar_position='bottom', xlabel='', ylabel='')
map_tiles * plot
森林野火故事2.0:一眼看穿!使用 Panel 和 hvPlot 視覺化 ⛵

簡單的一組程式碼即可實現上述視覺化結果,hvPlot 是一個非常棒的空間視覺化工具庫,它利用了其他 ?Holoviz 庫——?Holoviews、?Geoviews、?Datashader 和 ?Colorcet,所以可以極大簡化建立大型資料集的互動式地圖所需步驟。

森林野火故事2.0:一眼看穿!使用 Panel 和 hvPlot 視覺化 ⛵

? 帶時間滑塊的儀表板

Panel 的小部件讓我們可以訪問各種方法來操作和切分我們的資料子集視覺化,例如對時間序列資料可切分:加入選擇年份的滑塊

使用 Panel 製作這種儀表板,分為3個步驟:

  • ① 定義一個小部件,例如用於選擇年份或下拉選單的整數滑塊。
  • ② 定義一個繪圖函式,將滑塊中的年份值作為輸入。
  • ③ 佈局和渲染我們的儀表板。
# 為時間序列構建滑動小元件
year = pn.widgets.IntSlider(name='Year Slider', width=300,
                                 start=1992, end=2015, value=(1993),
                                 step=1,value_throttled=(1993))

# 展示當前年份
@pn.depends(year.param.value_throttled)
def year_selected(year):
    return '## Wildfires Across the US in {}'.format(year)

# 實際繪圖函式
@pn.depends(year.param.value_throttled)
def plot_map(year):
    year_df = df[df['FIRE_YEAR'] == year].copy()
    plot = year_df.hvplot(
        'LONGITUDE', 
        'LATITUDE', 
        geo=True,
        kind='points', 
        rasterize=True, 
        cmap=cc.fire, 
        cnorm='eq_hist',  
        colorbar=True).opts(colorbar_position='bottom', xlabel='', ylabel='')
    return map_tiles * plot


dashboard = pn.WidgetBox(pn.Column(pn.Row(year_selected, year),
                         pn.Row(pn.bind(plot_map, year)), align="start",
                         sizing_mode="stretch_width"))
森林野火故事2.0:一眼看穿!使用 Panel 和 hvPlot 視覺化 ⛵

? 更簡單的構建方式

當我們的繪圖只有一個小部件要顯示時,有一種更簡單的方式:我們可以改為使用.interactive製作我們的 DataFrame 和資料管道的互動式副本。

下面我們用一個示例來演示如何使用這個方法:這次我們的條件是『火災的原因』,我們讓地圖只顯示每個原因下的火災。

select_cause = pn.widgets.Select(
    options = df['STAT_CAUSE_DESCR'].value_counts().index.tolist(),
    name = 'Cause')

# 動態互動
dfi = df.interactive


iplot = dfi[dfi['STAT_CAUSE_DESCR']==select_cause].hvplot(
    'LONGITUDE', 
    'LATITUDE', 
    geo=True,
    kind='points', 
    rasterize=True, 
    cmap=cc.fire, 
    cnorm='eq_hist',  
    colorbar=True).opts(colorbar_position='bottom', xlabel='', ylabel='', title='Wildfires by Cause from 1992-2015') 


map_tiles.opts(level='underlay') * iplot
森林野火故事2.0:一眼看穿!使用 Panel 和 hvPlot 視覺化 ⛵

? 組合儀表板

我們可以構建更強大的組合儀表盤,它讓我們能夠同時檢視資料的多個維度資訊。下面我們建立一些視覺化圖例,然後使用 Panel 將它們組合在一起。

? 火勢大小

我們先繪製每個規模等級發生的火災數量:野火按燃燒區域的大小進行分類,A 級最小,G 級最大。

@pn.depends(year.param.value_throttled)
def plot_class(year):
    year_df = df[df['FIRE_YEAR'] == year].copy()
    count_df = pd.DataFrame(year_df.groupby('FIRE_SIZE_CLASS').size(), columns=['Count'])
    count_df['Fire Class'] = count_df.index
    return count_df.hvplot.bar(x='Fire Class', y='Count', c='Fire Class', cmap='fire', 
                legend=False).opts(xlabel="Fire Size Class", ylabel="Number of Fires",
                title="Wildfires in {} Grouped by Size Classification".format(year))
              
plot_class(2006)
森林野火故事2.0:一眼看穿!使用 Panel 和 hvPlot 視覺化 ⛵

注意到上述繪圖函式將『年份』作為引數,這樣它可以在滑塊值更改時對資料進行子集切分和呈現。拿到對應的資料子集後,我們把它按大小分類進行分組,並使用.size()計算每組的火災次數。

? 起火原因 & 持續時長

下面我們對『起火原因和對應的持續時長』進行分析視覺化(注意,這裡和上面的模組一樣,也是傳入年份作為引數,這樣我們最後的組合繪圖,可以有統一的資料子集切分方式)。

@pn.depends(year.param.value_throttled)
def plot_cause_burn(year):
    year_df = df[df['FIRE_YEAR'] == year].copy()
    caused_df = pd.DataFrame(year_df.groupby('STAT_CAUSE_DESCR')[['BURN_TIME']].mean().sort_values('BURN_TIME'))
    caused_df['Fire Cause'] = caused_df.index
    return caused_df.hvplot.barh(x='Fire Cause', y='BURN_TIME', 
                                 c='Fire Cause', cmap='fire_r', legend=False).opts(
                                 ylabel="Burn Time (Days)",
                                 title="Wildfire Burn time Grouped by Cause in {}".format(year))
                
                
plot_cause_burn(2003)
森林野火故事2.0:一眼看穿!使用 Panel 和 hvPlot 視覺化 ⛵

? 起火原因 & 火災數量

下面我們對『起火原因和對應的數量』進行分析視覺化。

@pn.depends(year.param.value_throttled)
def plot_cause_count(year):
    year_df = df[df['FIRE_YEAR'] == year].copy()
    caused_df = pd.DataFrame(year_df.groupby('STAT_CAUSE_DESCR').size(), columns=['Count']).sort_values('Count')
    caused_df['Fire Cause'] = caused_df.index
    return caused_df.hvplot.barh(x='Fire Cause', y='Count', 
                                 c='Fire Cause', cmap='fire_r', 
                                 legend=False).opts(ylabel="Number of Fires",
                                 title="Number of Wildfires Grouped by Cause in {}".format(year))
                              
plot_cause_count(2003)
森林野火故事2.0:一眼看穿!使用 Panel 和 hvPlot 視覺化 ⛵

對比上圖,我們可以看到一些有趣差異,例如:

  • 吸菸帶來第2燃燒時長的野火,而它在引發的野火總數中排名第9。
  • 除了閃電之外,燃燒時間最長的火災並不是最頻繁發生的火災。

這也解釋了為什麼野火難以撲滅,它們通常發生在更偏遠的地方,很難及早控制。閃電、吸菸和營火都有可能在這些地區引發火災,因為那裡有大量木材可燃燒,而周圍很少有人在早期發現跡象並報告煙霧。

也有一些年份看起來完全不同,例如 2006 年,電力線故障導致大火平均燃燒數天。如下圖所示:

pn.Row(plot_cause_count(2006) , plot_cause_burn(2006), width=1000)
森林野火故事2.0:一眼看穿!使用 Panel 和 hvPlot 視覺化 ⛵

這裡需要注意的是 Panel 和 hvPlot 如何識別這兩個子圖共享相同的 y 軸。

? 分析結果組裝

現在我們已經從不同維度進行了分析,我們使用小部件把它們進行組合,使我們可以沿著時間軸動態選擇和做一些資料探索,構建組合儀表板的程式碼如下:

plots_box = pn.WidgetBox(pn.Column(pn.Row(pn.bind(fire_count, year), year),
                                   pn.Row(pn.bind(plot_map, year), pn.bind(plot_class, year)) ,
                                   pn.Row(pn.bind(plot_cause_count, year), pn.bind(plot_cause_burn, year)), align="start",
                                   width=800, sizing_mode="stretch_width"))


dashboard = pn.Row(plots_box, sizing_mode="stretch_width")
森林野火故事2.0:一眼看穿!使用 Panel 和 hvPlot 視覺化 ⛵

上面只是擷取的一些gif動圖,大家快快實際操作一下吧!會有更清晰的認識。

? 總結

在本篇內容中 ShowMeAI 給大家講解了使用 hvPlot 和 Panel 構建各種組合視覺化看板儀表盤的方法,當我們需要進行資料探索和分析的時候,簡單的一些資料分析視覺化用 Pandas 和 Seaborn 等就可以快速完成,當我們需要一個互動式探索分析工具時,使用hvPlot 和 Panel 是一個非常棒的選擇。

參考資料

森林野火故事2.0:一眼看穿!使用 Panel 和 hvPlot 視覺化 ⛵

相關文章