python - 使用 matplotlib 和 seaborn 在多元时间序列图中突出显示时间间隔

标签 python matplotlib time-series seaborn highlight

我想用时间间隔注释多元时间序列图(每种注释类型都用颜色表示)。

数据概览

示例数据集如下所示:

            metrik_0  metrik_1  metrik_2  geospatial_id  topology_id  \
2020-01-01 -0.848009  1.305906  0.924208             12            4   
2020-01-01 -0.516120  0.617011  0.623065              8            3   
2020-01-01  0.762399 -0.359898 -0.905238             19            3   
2020-01-01  0.708512 -1.502019 -2.677056              8            4   
2020-01-01  0.249475  0.590983 -0.677694             11            3   

            cohort_id  device_id  
2020-01-01          1          1  
2020-01-01          1          9  
2020-01-01          2         13  
2020-01-01          2          8  
2020-01-01          1         12  

标签如下所示:

cohort_id marker_type               start                 end
0          1           a 2020-01-02 00:00:00                 NaT
1          1           b 2020-01-04 05:00:00 2020-01-05 16:00:00
2          1           a 2020-01-06 00:00:00                 NaT

期望的结果

  • cohort_id 的所有时间序列的多变量图
  • 标记突出显示(每种类型有不同的颜色)
    • 注意标记可能会重叠/透明度很有用
    • 标记类型a周围会有衰减(按小时数配置)

我考虑过使用seaborn/matplotlib来完成这个任务。

到目前为止我已经明白了:

%pylab inline
import seaborn as sns; sns.set()
import matplotlib.dates as mdates

aut_locator = mdates.AutoDateLocator(minticks=3, maxticks=7)
aut_formatter = mdates.ConciseDateFormatter(aut_locator)

g = df[df['cohort_id'] == 1].plot(figsize=(8,8))
g.xaxis.set_major_locator(aut_locator)
g.xaxis.set_major_formatter(aut_formatter)
plt.show()

这相当困惑。 我担心,不可能将指标(多变量数据)拟合到单个图中。 它应该由每列组成。 然而,这又需要重新调整 Seaborn FacetGrid 的数据帧才能工作,这也感觉不太对劲——特别是当同类群组 ID 中的元素数量(时间序列)变大时。 如果 FacetGrid 是正确的方式,那么类似于:https://seaborn.pydata.org/examples/timeseries_facets.html将是第一部分,但标签仍然会丢失。

如何添加标签? 第一部分应该如何完成?

所需结果的示例: /image/JYilG.jpg ,即其中之一 enter image description here

对于每个指标值

示例数据的代码

数据集是从下面的代码片段生成的:

import pandas as pd
import numpy as np

import random
random_seed = 47
np.random.seed(random_seed)
random.seed(random_seed)
def generate_df_for_device(n_observations, n_metrics, device_id, geo_id, topology_id, cohort_id):
        df = pd.DataFrame(np.random.randn(n_observations,n_metrics), index=pd.date_range('2020', freq='H', periods=n_observations))
        df.columns = [f'metrik_{c}' for c in df.columns]
        df['geospatial_id'] = geo_id
        df['topology_id'] = topology_id
        df['cohort_id'] = cohort_id
        df['device_id'] = device_id
        return df
    
def generate_multi_device(n_observations, n_metrics, n_devices, cohort_levels, topo_levels):
    results = []
    for i in range(1, n_devices +1):
        #print(i)
        r = random.randrange(1, n_devices)
        cohort = random.randrange(1, cohort_levels)
        topo = random.randrange(1, topo_levels)
        df_single_dvice = generate_df_for_device(n_observations, n_metrics, i, r, topo, cohort)
        results.append(df_single_dvice)
        #print(r)
    return pd.concat(results)

# hourly data, 1 week of data
n_observations = 7 * 24
n_metrics = 3
n_devices = 20
cohort_levels = 3
topo_levels = 5

df = generate_multi_device(n_observations, n_metrics, n_devices, cohort_levels, topo_levels)
df = df.sort_index()
df.head()

marker_labels = pd.DataFrame({'cohort_id':[1,1, 1], 'marker_type':['a', 'b', 'a'], 'start':['2020-01-2', '2020-01-04 05', '2020-01-06'], 'end':[np.nan, '2020-01-05 16', np.nan]})
marker_labels['start'] = pd.to_datetime(marker_labels['start'])
marker_labels['end'] = pd.to_datetime(marker_labels['end'])

最佳答案

一般来说,您可以使用 plt.fill_ Between 表示水平带,使用 plt.fill_ Betweenx 表示垂直带。对于“bands-within-bands”,您只需调用该方法两次即可。

使用您的数据的基本示例如下所示。我对带的位置使用了固定值,但您可以将它们放在主数据帧上并在循环内动态引用它们。

import matplotlib.pyplot as plt

fig, ax = plt.subplots(3 ,figsize=(20, 9), sharex=True)
plt.subplots_adjust(hspace=0.2)

metriks = ["metrik_0", "metrik_1", "metrik_2"]
colors = ['#66c2a5', '#fc8d62', '#8da0cb'] #Set2 palette hexes

for i, metric in enumerate(metriks):
    
    df[[metric]].plot(ax=ax[i], color=colors[i], legend=None)
    ax[i].set_ylabel(metric)

    ax[i].fill_betweenx(y=[-3, 3], x1="2020-01-04 05:00:00",
                        x2="2020-01-05 16:00:00", color='gray', alpha=0.2)
    ax[i].fill_betweenx(y=[-3, 3], x1="2020-01-04 15:00:00",
                        x2="2020-01-05 00:00:00", color='gray', alpha=0.4)

enter image description here

关于python - 使用 matplotlib 和 seaborn 在多元时间序列图中突出显示时间间隔,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64356412/

相关文章:

python - 如何在 matplotlib python 中生成每年发生次数的条形图?

python - Pandas 缺失值 : fill with the closest non NaN value

python - 由于 SSL 模块不可用 Python,无法访问 URL

python - Pandas groupby 顺序值

python - 使用预训练模型的 CNN 输出层可视化

确定绘图宽度的 Pythonic 方法

python - 使用 rcParams 更改 matplotlib 网格颜色

python - 如何向下移动颜色条标签?

r - 如何创建从特定日期开始的每日时间序列

python - Pandas 重新采样将周末推迟到周五