【问题标题】:Find group of consecutive dates in Pandas DataFrame在 Pandas DataFrame 中查找连续日期组
【发布时间】:2019-03-24 21:04:42
【问题描述】:

我正在尝试从 Pandas DataFrame 中获取具有连续日期的数据块。我的df 如下所示。

      DateAnalyzed           Val
1       2018-03-18      0.470253
2       2018-03-19      0.470253
3       2018-03-20      0.470253
4       2018-09-25      0.467729
5       2018-09-26      0.467729
6       2018-09-27      0.467729

在这个df 中,我想获取前 3 行,进行一些处理,然后获取最后 3 行并对其进行处理。

我通过应用以下代码计算了 1 个滞后的差异。

df['Delta']=(df['DateAnalyzed'] - df['DateAnalyzed'].shift(1))

但是在那之后我不知道如何在不迭代的情况下获得连续行的组。

【问题讨论】:

    标签: python pandas datetime


    【解决方案1】:

    herehere 之后有类似的问题,有更具体的输出要求。因为这个比较笼统,所以我也想在这里贡献一下。

    我们可以很容易地用一行代码为连续的组分配一个唯一的标识符:

    df['grp_date'] = df.DateAnalyzed.diff().dt.days.ne(1).cumsum()
    

    在这里,每次我们看到一个相差大于一天的日期时,我们都会为该日期添加一个值,否则它会保留之前的值,以便我们最终得到每个组的唯一标识符。

    查看输出:

      DateAnalyzed       Val  grp_date
    1   2018-03-18  0.470253         1
    2   2018-03-19  0.470253         1
    3   2018-03-20  0.470253         1
    4   2018-09-25  0.467729         2
    5   2018-09-26  0.467729         2
    6   2018-09-27  0.467729         2
    

    现在,groupby "grp_date" 和 applyagg 做任何你想做的事情都很容易。


    例子:

    # Sum across consecutive days (or any other method from pandas groupby)
    df.groupby('grp_date').sum()
    
    # Get the first value and last value per consecutive days
    df.groupby('grp_date').apply(lambda x: x.iloc[[0, -1]])
    # or df.groupby('grp_date').head(n) for first n days
    
    # Perform custom operation across target-columns
    df.groupby('grp_date').apply(lambda x: (x['col1'] + x['col2']) / x['Val'].mean())
    
    # Multiple operations for a target-column
    df.groupby('grp_date').Val.agg(['min', 'max', 'mean', 'std'])
    
    # and so on...
    

    【讨论】:

      【解决方案2】:

      您似乎需要两个布尔掩码:一个用于确定组之间的间隔,另一个用于确定组中的哪些日期。

      还有一个棘手的部分可以通过示例来充实。请注意,下面的 df 包含一个添加的行,该行之前或之后没有任何连续日期。

      >>> df
        DateAnalyzed       Val
      1   2018-03-18  0.470253
      2   2018-03-19  0.470253
      3   2018-03-20  0.470253
      4   2017-01-20  0.485949  # < watch out for this
      5   2018-09-25  0.467729
      6   2018-09-26  0.467729
      7   2018-09-27  0.467729
      
      >>> df.dtypes
      DateAnalyzed    datetime64[ns]
      Val                    float64
      dtype: object
      

      下面的答案假设您想完全忽略2017-01-20,而不对其进行处理。 (如果您确实想处理此日期,请参阅答案末尾的解决方案。)

      第一:

      >>> dt = df['DateAnalyzed']
      >>> day = pd.Timedelta('1d')
      >>> in_block = ((dt - dt.shift(-1)).abs() == day) | (dt.diff() == day)
      >>> in_block
      1     True
      2     True
      3     True
      4    False
      5     True
      6     True
      7     True
      Name: DateAnalyzed, dtype: bool
      

      现在,in_block 会告诉您哪些日期位于“连续”块中,但不会告诉您每个日期属于哪些组。

      下一步是派生分组本身:

      >>> filt = df.loc[in_block]
      >>> breaks = filt['DateAnalyzed'].diff() != day
      >>> groups = breaks.cumsum()
      >>> groups
      1    1
      2    1
      3    1
      5    2
      6    2
      7    2
      Name: DateAnalyzed, dtype: int64
      

      然后您可以根据自己的选择拨打df.groupby(groups)

      >>> for _, frame in filt.groupby(groups):
      ...     print(frame, end='\n\n')
      ... 
        DateAnalyzed       Val
      1   2018-03-18  0.470253
      2   2018-03-19  0.470253
      3   2018-03-20  0.470253
      
        DateAnalyzed       Val
      5   2018-09-25  0.467729
      6   2018-09-26  0.467729
      7   2018-09-27  0.467729
      

      要将其合并回df,分配给它,隔离日期将为NaN

      >>> df['groups'] = groups
      >>> df
        DateAnalyzed       Val  groups
      1   2018-03-18  0.470253     1.0
      2   2018-03-19  0.470253     1.0
      3   2018-03-20  0.470253     1.0
      4   2017-01-20  0.485949     NaN
      5   2018-09-25  0.467729     2.0
      6   2018-09-26  0.467729     2.0
      7   2018-09-27  0.467729     2.0
      

      如果您确实想包含“单独”日期,事情会变得更简单:

      dt = df['DateAnalyzed']
      day = pd.Timedelta('1d')
      breaks = dt.diff() != day
      groups = breaks.cumsum()
      

      【讨论】:

      • 我来自未来需要这样做,发现(df.Date.diff(-1) == -day) | (df.Date.diff() == day) 更干净一点。在我的例子中,df.Date 是日期列。
      猜你喜欢
      • 1970-01-01
      • 2016-09-17
      • 1970-01-01
      • 1970-01-01
      • 2021-09-09
      • 1970-01-01
      • 2021-08-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多