探索西雅图天气#

(本教程改编自 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()'
)

接下来,让我们看看西雅图的降水量如何在一年中变化。Altair本地支持日期和日期的离散化,当我们将类型设置为 temporal (简写 T)时。例如,在以下绘图中,我们计算每个月的总降水量。为了将数据离散化为月份,我们可以使用 month 分箱(有关此及其他 TimeUnit 分箱的更多信息,请参见 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)',
)

如果我们使用条形图并将年份标记为“序数”(有序类别)类型,这样会更加清晰。为美观起见,让我们通过将序数值分配给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 操作来显式地向数据框添加一列来完成相同的计算;那里的缺点是派生值必须存储在图表规范中,而不是在浏览器中按需计算。

NoneNone

接下来我们将探索 weather 字段,它编码了一个描述特定日期天气的分类变量。我们可能希望了解不同天气类型(例如晴天或雨天)在一年中的分布情况。要回答这个问题,我们可以按月份对日期进行离散化,然后统计 y 轴上的记录数。接着,我们通过将这一列映射到颜色通道来按天气类型细分条形图。当条形图的某个字段映射到颜色时,Altair 会自动将条形堆叠在一起:

alt.Chart(df).mark_bar().encode(
    x='month(date):N',
    y='count()',
    color='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参考