探索西雅图天气#

(本教程改编自Vega-Lite 的文档)

在本教程中,您将学习更多在 Altair 中创建可视化的技巧。如果您不熟悉 Altair,请先阅读基本统计可视化

在本教程中,我们将创建可视化来探索西雅图的天气数据,数据来自 NOAA。数据集是一个 CSV 文件,包含温度(摄氏度)、降水量(毫米)、风速(米/秒)和天气类型的列。数据集包含了从 2012 年 1 月 1 日到 2015 年 12 月 31 日每天的数据行。

Altair 设计用于处理pandas数据框形式的数据,并包含用于加载此数据集和其他内置数据集的加载器

from vega_datasets import data

df = data.seattle_weather()
df.head()
            date  precipitation  temp_max  temp_min  wind  weather
    0 2012-01-01            0.0      12.8       5.0   4.7  drizzle
    1 2012-01-02           10.9      10.6       2.8   4.5     rain
    2 2012-01-03            0.8      11.7       7.2   2.3     rain
    3 2012-01-04           20.3      12.2       5.6   4.7     rain
    4 2012-01-05            1.3       8.9       2.8   6.1     rain

数据从网络加载并存储在一个 pandas DataFrame 中,然后我们可以使用 Altair 来探索它。

我们首先来看一下降水量,使用刻度标记来查看降水量值的分布

import altair as alt

alt.Chart(df).mark_tick().encode(
    x='precipitation',
)

看起来降水量偏向较低的值;也就是说,在西雅图下雨时,通常雨量不大。很难看到连续变量的模式,为了更好地观察,我们可以创建降水数据的直方图。为此,我们首先通过为x添加分箱来离散化降水量值。此外,我们将编码通道y设置为count。结果是一个降水量值的直方图

alt.Chart(df).mark_bar().encode(
    alt.X('precipitation').bin(),
    y='count()'
)

接下来,我们来看看西雅图的降水量如何随全年变化。当我们设置类型为temporal(简写为T)时,Altair 原生支持日期和日期的离散化。例如,在下面的图表中,我们计算了每个月的总降水量。为了将数据离散化为月份,我们可以使用month分箱(有关此分箱和其他timeUnit分箱的更多信息,请参阅时间单位

alt.Chart(df).mark_line().encode(
    x='month(date):T',
    y='average(precipitation)'
)

此图表显示,西雅图冬季的平均降水量远高于夏季(对于居住在那里的人来说,这是一个不足为奇的观察结果!)。通过改变编码通道到数据特征的映射,您可以开始探索数据中的关系。

在查看降水量和温度时,我们可能希望按年月(yearmonth)进行聚合,而不仅仅是按月。这使我们可以看到季节性趋势,同时平滑了每日变化。我们可能还希望查看每个月的最高和最低温度

alt.Chart(df).mark_line().encode(
    x='yearmonth(date):T',
    y='max(temp_max)',
)

在此图表中,在此相对较短的基准期内,最高温度似乎逐年增加。为了更仔细地研究这一点,我们转而查看每年每日最高温度的平均值

alt.Chart(df).mark_line().encode(
    x='year(date):T',
    y='mean(temp_max)',
)

如果我们使用条形图并将年份标记为“有序”(ordered category)类型,这将更清晰一些。出于美观原因,我们将有序值分配给 y 轴,使条形图变为水平方向

alt.Chart(df).mark_bar().encode(
    x='mean(temp_max)',
    y='year(date):O'
)

图表显示,在这四年期间,每日最高温度的年平均值有所增加,对于每日最低温度,您也可以证实这一事实。

您可能还会想知道每日温度范围如何随全年变化。为此,我们需要添加一个计算来派生新字段,这可以通过添加一个calculate转换来完成

alt.Chart(df).mark_bar().encode(
    x='mean(temp_range):Q',
    y='year(date):O'
).transform_calculate(
    temp_range="datum.temp_max - datum.temp_min"
)

请注意,此计算实际上并未在 Python 中进行任何数据操作,而是将操作编码并存储在图表规范中,由渲染器进行计算。

当然,也可以通过使用 pandas 操作显式地向数据框添加一列来完成相同的计算;这样做的缺点是派生值必须存储在图表规范中,而不是在浏览器中按需计算。

接下来我们将探索weather字段,它编码了一个描述给定日期天气的分类变量。我们可能想知道不同类型的天气(例如晴天或雨天)如何分布在全年。为了回答这个问题,我们可以按月份对日期进行离散化,然后计算 y 轴上的记录数。然后,我们将此列映射到颜色通道,按天气类型分解条形。当条形图有一个字段映射到颜色时,Altair 将自动将条形相互堆叠

alt.Chart(df).mark_bar().encode(
    x='month(date):N',
    y='count()',
    color='weather',
)

默认调色板的语义可能与我们的预期不符。例如,我们可能不希望“sun”(晴天)是紫色的。我们可以通过提供一个颜色比例范围来调整图表,该范围使用标准十六进制颜色代码将 weather 字段的值映射到有意义的颜色

scale = alt.Scale(domain=['sun', 'fog', 'drizzle', 'rain', 'snow'],
                  range=['#e7ba52', '#c7c7c7', '#aec7e8', '#1f77b4', '#9467bd'])

此比例尺可以传递给颜色编码以应用于绘图样式。此外,我们可以自定义坐标轴和图例的标题,使图表的含义更清晰

alt.Chart(df).mark_bar().encode(
    x=alt.X('month(date):N').title('Month of the year'),
    y='count()',
    color=alt.Color('weather', legend=alt.Legend(title='Weather type'), scale=scale),
)

结合上述想法,我们可以创建该数据集的任意数量的灵活可视化。例如,这是一个图表,它使用我们在上面开发的自定义设置来探索天气、降水量、最高温度和温度范围之间的关系,配置为使用更大的画布并允许使用鼠标进行交互式平移和缩放

alt.Chart(df).mark_point().encode(
    alt.X('temp_max').title('Maximum Daily Temperature (C)'),
    alt.Y('temp_range:Q').title('Daily Temperature Range (C)'),
    alt.Color('weather').scale(scale),
    alt.Size('precipitation').scale(range=[1, 200])
).transform_calculate(
    "temp_range", "datum.temp_max - datum.temp_min"
).properties(
    width=600,
    height=400
).interactive()

这让我们对西雅图的天气模式有了更多洞察:阴雨和雾天通常较凉爽,温度范围较窄,而较暖和的天气通常干燥晴朗,最高和最低温度之间的范围更宽。

您可以使用 Altair 的多面板图表和交互构建块进一步扩展。例如,我们可以构建一个按天气类型划分的天数直方图

alt.Chart(df).mark_bar().encode(
    x='count()',
    y='weather:N',
    color=alt.Color('weather:N').scale(scale),
)

现在我们可以将这个直方图与上面的点图垂直连接起来,并添加一个刷选工具,以便直方图反映选定的内容(有关选择的更多信息,请参阅交互式图表

brush = alt.selection_interval()
color = alt.Color("weather:N").scale(scale)
temp_range = alt.datum["temp_max"] - alt.datum["temp_min"]

points = alt.Chart(width=600, height=400).mark_point().encode(
    alt.X("temp_max:Q").title("Maximum Daily Temperature (C)"),
    alt.Y("temp_range:Q").title("Daily Temperature Range (C)"),
    color=alt.when(brush).then(color).otherwise(alt.value("lightgray")),
    size=alt.Size("precipitation:Q").scale(range=[1, 200]),
).transform_calculate(
    temp_range=temp_range
).add_params(
    brush
)

bars = alt.Chart(width=600).mark_bar().encode(
    x="count()",
    y="weather:N",
    color=color
).transform_calculate(
    temp_range=temp_range
).transform_filter(
    brush
)

alt.vconcat(points, bars, data=df)

此图表包含连接、数据转换、选择以及自定义的坐标轴标签和数据比例尺,展示了 Altair 背后的语法力量:您可以使用少量构建块创建复杂的图表。

这是本教程的结尾,您已经了解了对数据进行分箱和聚合、派生新字段以及自定义图表的各种方法。您可以在示例图库中找到更多可视化。如果您想进一步自定义图表,可以参考 Altair 的API 参考