与 Altair 交互绘制时间序列的问题

Problems plotting time-series interactively with Altair

问题描述

我的目标很基本:在交互式图中绘制时间序列。经过一些研究,我决定尝试 Altair。 已经有 QGIS plugins 用于时间序列可视化,但据我所知,none 用于在矢量级别绘制时间序列,交互式单击地图并选择多边形。所以这就是为什么我决定使用 Altair 寻求一个自制的解决方案,也许将它与 Folium 结合起来以在以后添加功能。

我是 Altair 库(以及 Vega 和 Vega-lite)的新手,在数据科学和数据可视化方面也是新手...所以提前为我的无知道歉!

已经有关于如何使用 Altair 绘制时间序列的很好解释的教程(例如 here,或在官方网站上)。但是,据我所知,我的研究案例有一些特殊性尚未完全解决。

数据是使用 Python API for Google Earth Engine 生成的,并使用 Python 和 pandas/geopandas 库进行了预处理:

预处理后的数据概览:

我的“最终”目标是在同一张图中显示一个交互式线图,其中一组线代表一个农业地块,地块按不同颜色的作物类型分类,例如绿色的玉米,黄色的小麦,棕色的同类树...(包含每个地块作物类型的信息可以添加到 DataFrame 与另一个 DataFrame).

我想的东西看起来或多或少类似于以下示例,传说中的年份是按作物类型着色的地块:

但到目前为止,我还没有设法让我的数据看起来像这样......根本没有。

如您所见,数据中有许多空值(这是由于云掩蔽函数的应用以及多个 Sentinel-2 轨道与 ROI 相交所致)。我只想省略 earch column/parcel 的非空值,但我不知道此数据配置是否会带来问题(对此有何建议?)。

到目前为止我得到了:

确实我做错了很多事。如果能得到一些建议来解决(其中一些)问题,那就太好了。

重现问题的数据和代码示例

Here 是 JSON 格式的数据文本示例,用于重现问题的代码如下:

import pandas as pd
import geopandas as gpd
import altair as alt

df= pd.read_json(r"path\to\json\file.json")
df['date']= pd.to_datetime(df['date'])
print(gdf.dtypes)
df

输出:

lines=alt.Chart(df).mark_line().encode(
    x='date:O',
    y='17811:Q',
    color=alt.Color(
        '17811:Q', scale=alt.Scale(scheme='redyellowgreen', domain=(-1, 1)))
    )
lines.properties(width=700, height=600).interactive()

输出:

在此先感谢您的帮助!

如果我理解正确,主要是你的数据框格式需要更改 from wide to long,你可以通过 pandas 中的 .melt 或 [=14] =] 在牵牛星。对于熔化,熔化列的默认名称为 'variable'(前一列名称)和 'value'(每列的值):

alt.Chart(df.melt(id_vars='date'), width=500).mark_line().encode(
    x='date:T',
    y='value',
    color=alt.Color('variable')
)

差距来自NaN;如果您希望 Altair 插入缺失值,您可以删除 NaN:

alt.Chart(df.melt(id_vars='date').dropna(), width=500).mark_line().encode(
    x='date:T',
    y='value',
    color=alt.Color('variable')
)

如果你想在 Altair 中完成所有操作,以下等效于上面最后一个 pandas 示例(转换使用 'key' 而不是 'variable' 作为名称前专栏)。我还使用和序号而不是标称类型进行颜色编码,以展示如何使颜色与您的示例更相似。:

alt.Chart(df, width=500).mark_line().encode(
    x='date:T',
    y='value:Q',
    color=alt.Color('key:O')
).transform_fold(
    df.drop(columns='date').columns.tolist()
).transform_filter(
    'isValid(datum.value)'
)