【问题标题】:matplotlib plot sampling strategy at a 3H frequencymatplotlib 以 3H 频率绘制采样策略
【发布时间】:2021-04-29 09:34:21
【问题描述】:

问题概述

我正在尝试为我运行的实验绘制一个采样时间表。我们开始每 3 小时采样一次,我希望能够观察每个样本在每日周期中绘制的时间。

  • x 轴变量应该是一天中的时间 (00:00 - 23:00)。
  • 每一行(或者可能是 y 轴变量?)都应该是新的一天。
  • 与理想的 3 小时采样策略相比,该点应根据是早期发布(蓝色)还是延迟发布(红色)来着色。

我设想的情节如下所示:

模拟虚拟数据来解释问题

import xarray as xr
import numpy as np
import matplotlib.pyplot as plt 
import pandas as pd 
from pandas.tseries.offsets import DateOffset
import matplotlib.dates as mdates
import itertools

value = np.random.normal(size=100)
expected_time = pd.date_range("2000-01-01", freq="180min", periods=100)
# add random offset to simulate being +/- the true expected release time
time_deltas = np.array([DateOffset(minute=max(0, min(int(i), 59))) for i in np.abs(np.random.normal(0, 10, size=100))])
time = [expected_time[i] + time_deltas[i] if (i % 2 == 0) else expected_time[i] - time_deltas[i] for i in range(100)]

df = pd.DataFrame({"launchtime": time, "value": value})
ds = df.set_index("launchtime").to_xarray()
ds = ds.assign_coords(expected_time=("launchtime", expected_time))

如您所见,基础数据大约每 3 小时有一次观察(尽管确切时间略有不同)。

In []: ds["launchtime.hour"]

Out[]:
<xarray.DataArray 'hour' (launchtime: 100)>
array([ 0,  3,  6,  9, 12, 15, 18, 21,  0,  3,  6,  9, 12, 15, 18, 21,  0,
        3,  6,  9, 12, 15, 18, 21,  0,  3,  6,  9, 12, 15, 18, 21,  0,  3,
        6,  9, 12, 15, 18, 21,  0,  3,  6,  9, 12, 15, 18, 21,  0,  3,  6,
        9, 12, 15, 18, 21,  0,  3,  6,  9, 12, 15, 18, 21,  0,  3,  6,  9,
       12, 15, 18, 21,  0,  3,  6,  9, 12, 15, 18, 21,  0,  3,  6,  9, 12,
       15, 18, 21,  0,  3,  6,  9, 12, 15, 18, 21,  0,  3,  6,  9])
Coordinates:
  * launchtime     (launchtime) datetime64[ns] 2000-01-01 ... 2000-01-13T09:0...
    expected_time  (launchtime) datetime64[ns] 2000-01-01 ... 2000-01-13T09:0...

使用matplotlib 的初始尝试

# get the total number of days
day_months = list(itertools.product(np.unique(ds["launchtime.day"].values), np.unique(ds["launchtime.month"].values)))
N_DAYS = len(day_months)

fig, axs = plt.subplots(N_DAYS, 1, figsize=(6, 0.5*N_DAYS), sharey=True)
for ix, (day, month) in enumerate(day_months):
    
    mask = (ds["launchtime.day"] == day) & (ds["launchtime.month"] == month)
    day = ds.sel(launchtime=mask)
    error = np.array([pd.to_datetime(dt) for dt in day.launchtime.values]) - np.array([pd.to_datetime(dt) for dt in day.expected_time.values])
    error = [e.total_seconds() for e in error]
    colors = ["r" if e > 0 else "b" if e < 0 else "grey" for e in error]
    print(colors)
    ax = axs[ix]

    ax.scatter(day.expected_time, [1 for _ in range(len(day.expected_time))], color=colors)
    ax.set_ylabel(f"Day {ix}")
    
    # remove the spines and ytick labels
    for spine in ax.spines:
        ax.spines[spine].set_visible(False)

    ax.axes.yaxis.set_visible(False)

plt.xticks(rotation=60)
fig.suptitle("Radiosonde Releases over a month campaign")

# 剩余问题

我看到的主要问题是:

  1. x 轴应该是每日周期,而不是还包括日/月/年的“日期时间”
  2. 将每一天整齐地堆叠为一个新列会很好,可以作为一个方面,也可以将每一天用作 y 轴变量

【问题讨论】:

    标签: python python-3.x pandas matplotlib


    【解决方案1】:

    这是一个完整且可重复的示例,具有以下主要功能:

    • 数据仅由 pandas 使用矢量化操作处理;
    • 通过将 DateOffset 替换为 Timedelta 对象来纠正时间增量的模拟,因此现在大约一半的点是蓝色的;
    • 保留了堆叠子图的方法(而不是使用 y 轴变量显示天数),因为这避免了对 x 轴值的额外计算,并且更容易通过以下方式绘制灰线(如示例图像所示)使用 x 轴刻度线和脊椎;
    • 对齐 x 轴(例如最后一个子图)的问题通过使用时间戳设置适当的 x 轴限制来解决,这些时间戳不是从expected_time 时间戳列表中获取的,以防频率有时不规则(例如,一天午夜没有发射);
    • fig.subplots_adjust 和图形高度用于调整子图之间的垂直间距,以确保刻度标签可见,而不是使用修改图形大小的tight_layoutconstrained_layout

    导入包并生成示例数据

    import numpy as np               # v 1.19.2
    import pandas as pd              # v 1.2.3
    import matplotlib.pyplot as plt  # v 3.3.4
    
    rng = np.random.default_rng(seed=1)  # random number generator
    size = 30
    expected_time = pd.date_range("2000-01-01", freq="180min", periods=size)
    time_deltas = np.array([pd.Timedelta(int(i), unit="minute")
                            for i in abs(rng.normal(0, 10, size=size))])
    time = [expected_time[i] + time_deltas[i] if (i % 2 == 0)
            else expected_time[i] - time_deltas[i] for i in range(size)]
    df = pd.DataFrame({"launchtime": time, "expected_time": expected_time})
    

    创建 matplotlib 图

    # Create day/month tuples based on expected time to ensure correct plotting for
    # cases where a day would have only one launch time occuring early before midnight
    day_months = [(dt.day, dt.month) for dt in df["expected_time"].dt.date.unique()]
    N_DAYS = len(day_months)
    
    fig, axs = plt.subplots(N_DAYS, 1, figsize=(6, 1*N_DAYS), sharey=True)
    for ix, (day, month) in enumerate(day_months):
        day = df[(df["expected_time"].dt.day==day) & (df["expected_time"].dt.month==month)]
        deltas = pd.to_datetime(day["expected_time"]) - pd.to_datetime(day["launchtime"])
        error = deltas.dt.total_seconds()
        colors = ["red" if e > 0 else "blue" if e < 0 else "grey" for e in error]
        # Create midnight timestamp regardless of the times of the data points
        ts = pd.to_datetime(day["expected_time"].dt.date.min())
        
        # Create subplot with appropriate x-axis limits and ticks
        ax = axs[ix]
        ax.scatter(day["launchtime"], np.repeat(1, len(day)), color=colors, clip_on=False)
        ax.set_xlim(ts, ts + pd.Timedelta(23, unit="hour"))
        ax.set_xticks(day["expected_time"])
        ax.set_xticklabels(day["expected_time"].dt.strftime("%H:%S"))
    #     ax.set_xticklabels(day["expected_time"].dt.hour)  # alternative method
        ax.set_ylabel(f"Day {ix}", labelpad=25, y=0.3, size=12)
        ax.set_yticks([])
        
        # Format spines and ticks to draw grey lines
        for spine in ["left", "right", "top"]:
            ax.spines[spine].set_visible(False)
        ax.spines["bottom"].set(linewidth=3, color="lightgrey")
        ax.tick_params(axis="x", direction="in", length=13,
                       width=3, color="lightgrey", pad=7)
    
    fig.suptitle("Radiosonde Releases over a month campaign", size=14, y=0.95)
    fig.subplots_adjust(hspace=0.5)
    

    【讨论】:

      猜你喜欢
      • 2019-02-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-12
      相关资源
      最近更新 更多