根据索引和值条件选择Pandas系列的切片

时间:2017-10-25 18:25:01

标签: python pandas numpy

我有一个Pandas Series,其中包含加速时间序列数据。我的目标是在给定阈值的情况下选择极端力的切片。我能够通过以下方式获得支持:

extremes = series.where(lambda force: abs(force - RESTING_FORCE) >= THRESHOLD, other=np.nan) 

现在extremes包含超过阈值的所有值和任何不存在的值的NaN,维持原始索引。

enter image description here

然而,次要要求是附近的峰值应合并为单个事件。在视觉上,您可以想象左边的三个极端(两个高,一个低)连接到一个完整的段,右边的两个峰连接到另一个完整的段。

我已经阅读了整个Series引用,但是我无法找到操作我的部分数据集的方法。例如,如果我有一个返回非NaN索引范围数组的方法,我将能够按顺序比较每个范围并决定是否用原始系列(附近)中的值填充空格或离开他们NaN(太远了)。

也许我需要放弃中间步骤并从一个完全不同的角度来解决这个问题?我是Python的新手,所以我很难在这方面做得很好。任何提示将不胜感激。

1 个答案:

答案 0 :(得分:1)

在没有循环的情况下提出矢量化解决方案实际上并非如此简单。

您可能需要逐步完成代码以查看每种方法的实际结果,但这里只是简短的草图:

解决方案大纲

  1. 通过简单的阈值过滤器识别所有峰值
  2. 获取峰值的时间戳到列中并在其间转发填充间隙,以便将当前有效时间戳与先前的有效时间戳进行比较
  3. 通过diff()进行实际比较,以获得时间增量并应用时间增量比较
  4. 将布尔值转换为整数以使用累积和来创建信号组
  5. 按信号分组并获取最小和最大时间戳值
  6. 示例数据

    以下是带有虚拟示例的代码:

    %matplotlib inline
    
    import pandas as pd
    import numpy as np
    
    size = 200
    
    # create some dummy data
    ts = pd.date_range(start="2017-10-28", freq="d", periods=size)
    values = np.cumsum(np.random.normal(size=size)) + np.sin(np.linspace(0, 100, size))
    series = pd.Series(values, index=ts, name="force")
    series.plot(figsize=(10, 5))
    

    enter image description here

    解决方案代码

    # define thresholds
    threshold_value = 6
    threshold_time = pd.Timedelta(days=10)
    
    # create data frame because we'll need helper columns
    df = series.reset_index()
    
    # get all initial peaks below or above threshold
    mask = df["force"].abs().gt(threshold_value)
    
    # create variable to store only timestamps of intial peaks
    df.loc[mask, "ts_gap"] = df.loc[mask, "index"]
    
    # create forward fill to enable comparison between current and next peak
    df["ts_fill"] = df["ts_gap"].ffill()
    
    # apply time delta comparison to filter only those within given time interval
    df["within"] = df["ts_fill"].diff() < threshold_time
    
    # convert boolean values into integers and 
    # create cummulative sum which creates group of consecutive timestamps
    df["signals"] = (~df["within"]).astype(int).cumsum()
    
    # create dataframe containing start and end values
    df_signal = df.dropna(subset=["ts_gap"])\
                  .groupby("signals")["ts_gap"]\
                  .agg(["min", "max"])
    
    # show results
    df_signal
    
    >>>       min           max
    signals         
    10        2017-11-06    2017-11-27
    11        2017-12-13    2018-01-22
    12        2018-02-03    2018-02-23
    

    最后,显示情节:

    series.plot(figsize=(10, 5))
    
    for _, (idx_min, idx_max) in df_signal.iterrows():
        series[idx_min:idx_max].plot()
    

    enter image description here

    结果

    正如您在图中所看到的,如果它们的最后和第一个时间戳在10天的范围内,那么绝对值为6的峰值将合并为单个信号。这里的阈值是任意的,仅用于说明目的。你可以将它们变成任何东西。