按组计算连续重复项

sa_*_*_zy 5 python duplicates dataframe pandas

我有一个ID列,日期列和值的数据集。我想计算连续日期范围内ID的连续出现/重复值。

我的问题非常类似于按组计数连续重复的值,但是在Python中。此外,该问题与如何在pandas数据框中查找重复项有所不同,因为我需要计数基于两列,其中一列不相同-这是日期(发生变化,但如果是连续的,我要对其进行计数)

这是一个示例数据集:

ID      tDate            value
79  2019-06-21 00:00:00  397
79  2019-07-13 00:00:00  404
79  2019-07-18 00:00:00  405
79  2019-07-19 00:00:00  406
79  2019-08-02 00:00:00  410
79  2019-08-09 00:00:00  413
Run Code Online (Sandbox Code Playgroud)

我希望结果数据集是:

ID      tDate            val  consec_count
79  2019-06-21 00:00:00  397  0
79  2019-07-13 00:00:00  404  0
79  2019-07-18 00:00:00  405  1
79  2019-07-19 00:00:00  406  2
79  2019-08-02 00:00:00  410  0
79  2019-08-09 00:00:00  413  0
Run Code Online (Sandbox Code Playgroud)

我用0而不是1标记了“单个”,因为我需要将两者分开。我将以不同于单个记录的方式处理批处理的“重复项”。

谢谢!

jez*_*ael 5

样品

df = pd.DataFrame({'ID': [79, 79, 79, 79, 79, 79, 80, 80, 80, 80, 80, 80, 80], 
                   'tDate': [pd.Timestamp('2019-07-12 00:00:00'),
                             pd.Timestamp('2019-07-13 00:00:00'),
                             pd.Timestamp('2019-07-18 00:00:00'),
                             pd.Timestamp('2019-07-19 00:00:00'),
                             pd.Timestamp('2019-07-20 00:00:00'),
                             pd.Timestamp('2019-08-03 00:00:00'), 
                             pd.Timestamp('2019-06-21 00:00:00'), 
                             pd.Timestamp('2019-06-22 00:00:00'), 
                             pd.Timestamp('2019-07-18 00:00:00'), 
                             pd.Timestamp('2019-07-19 00:00:00'), 
                             pd.Timestamp('2019-07-26 00:00:00'), 
                             pd.Timestamp('2019-08-02 00:00:00'), 
                             pd.Timestamp('2019-08-03 00:00:00')],
                   'value':[397, 404, 405, 406, 408, 413, 397, 404, 405, 406, 408, 410, 413]})

print (df)
    ID      tDate  value
0   79 2019-07-12    397
1   79 2019-07-13    404
2   79 2019-07-18    405
3   79 2019-07-19    406
4   79 2019-07-20    408
5   79 2019-08-03    413
6   80 2019-06-21    397
7   80 2019-06-22    404
8   80 2019-07-18    405
9   80 2019-07-19    406
10  80 2019-07-26    408
11  80 2019-08-02    410
12  80 2019-08-03    413
Run Code Online (Sandbox Code Playgroud)

解决方案

a = df.groupby('ID')['tDate'].diff().eq(pd.Timedelta(1, unit='d'))
s = (~a).cumsum()
df['consec_count']=np.where(a.groupby(s).transform('any'), df.groupby(s).cumcount(1).add(1),0)

print (df)
    ID      tDate  value  consec_count
0   79 2019-07-12    397             1
1   79 2019-07-13    404             2
2   79 2019-07-18    405             1
3   79 2019-07-19    406             2
4   79 2019-07-20    408             3
5   79 2019-08-03    413             0
6   80 2019-06-21    397             1
7   80 2019-06-22    404             2
8   80 2019-07-18    405             1
9   80 2019-07-19    406             2
10  80 2019-07-26    408             0
11  80 2019-08-02    410             1
12  80 2019-08-03    413             2
Run Code Online (Sandbox Code Playgroud)

说明

首先创建一个掩码,用于比较DataFrameGroupBy.diff一天之间每组的差异:

print (df.assign(diff= df.groupby('ID')['tDate'].diff(),
                 a = df.groupby('ID')['tDate'].diff().eq(pd.Timedelta(1, unit='d'))))
    ID      tDate  value    diff      a
0   79 2019-07-12    397     NaT  False
1   79 2019-07-13    404  1 days   True
2   79 2019-07-18    405  5 days  False
3   79 2019-07-19    406  1 days   True
4   79 2019-07-20    408  1 days   True
5   79 2019-08-03    413 14 days  False
6   80 2019-06-21    397     NaT  False
7   80 2019-06-22    404  1 days   True
8   80 2019-07-18    405 26 days  False
9   80 2019-07-19    406  1 days   True
10  80 2019-07-26    408  7 days  False
11  80 2019-08-02    410  7 days  False
12  80 2019-08-03    413  1 days   True
Run Code Online (Sandbox Code Playgroud)

通过Series.cumsum颠倒条件创建唯一组~

print (df.assign(diff= df.groupby('ID')['tDate'].diff(),
                 a = df.groupby('ID')['tDate'].diff().eq(pd.Timedelta(1, unit='d')),
                 a_neg = ~a,
                 s = (~a).cumsum()))

    ID      tDate  value    diff      a  a_neg  s
0   79 2019-07-12    397     NaT  False   True  1
1   79 2019-07-13    404  1 days   True  False  1
2   79 2019-07-18    405  5 days  False   True  2
3   79 2019-07-19    406  1 days   True  False  2
4   79 2019-07-20    408  1 days   True  False  2
5   79 2019-08-03    413 14 days  False   True  3
6   80 2019-06-21    397     NaT  False   True  4
7   80 2019-06-22    404  1 days   True  False  4
8   80 2019-07-18    405 26 days  False   True  5
9   80 2019-07-19    406  1 days   True  False  5
10  80 2019-07-26    408  7 days  False   True  6
11  80 2019-08-02    410  7 days  False   True  7
12  80 2019-08-03    413  1 days   True  False  7
Run Code Online (Sandbox Code Playgroud)

克里特通过掩模GroupBy.transformDataFrameGroupBy.any如果每个组包含至少一个用于测试True-然后组的所有值都被设置为TrueS:

print (df.assign(diff= df.groupby('ID')['tDate'].diff(),
                 a = df.groupby('ID')['tDate'].diff().eq(pd.Timedelta(1, unit='d')),
                 a_neg = ~a,
                 s = (~a).cumsum(),
                 mask = a.groupby(s).transform('any')))

    ID      tDate  value  consec_count    diff      a  a_neg  s   mask
0   79 2019-07-12    397             1     NaT  False   True  1   True
1   79 2019-07-13    404             2  1 days   True  False  1   True
2   79 2019-07-18    405             1  5 days  False   True  2   True
3   79 2019-07-19    406             2  1 days   True  False  2   True
4   79 2019-07-20    408             3  1 days   True  False  2   True
5   79 2019-08-03    413             0 14 days  False   True  3  False
6   80 2019-06-21    397             1     NaT  False   True  4   True
7   80 2019-06-22    404             2  1 days   True  False  4   True
8   80 2019-07-18    405             1 26 days  False   True  5   True
9   80 2019-07-19    406             2  1 days   True  False  5   True
10  80 2019-07-26    408             0  7 days  False   True  6  False
11  80 2019-08-02    410             1  7 days  False   True  7   True
12  80 2019-08-03    413             2  1 days   True  False  7   True
Run Code Online (Sandbox Code Playgroud)

创建每个组计数器sGroupBy.cumcount

print (df.assign(diff= df.groupby('ID')['tDate'].diff(),
                 a = df.groupby('ID')['tDate'].diff().eq(pd.Timedelta(1, unit='d')),
                 a_neg = ~a,
                 s = (~a).cumsum(),
                 mask = a.groupby(s).transform('any'),
                 c = df.groupby(s).cumcount(1).add(1)))

    ID      tDate  value  consec_count    diff      a  a_neg  s   mask  c
0   79 2019-07-12    397             1     NaT  False   True  1   True  1
1   79 2019-07-13    404             2  1 days   True  False  1   True  2
2   79 2019-07-18    405             1  5 days  False   True  2   True  1
3   79 2019-07-19    406             2  1 days   True  False  2   True  2
4   79 2019-07-20    408             3  1 days   True  False  2   True  3
5   79 2019-08-03    413             0 14 days  False   True  3  False  1
6   80 2019-06-21    397             1     NaT  False   True  4   True  1
7   80 2019-06-22    404             2  1 days   True  False  4   True  2
8   80 2019-07-18    405             1 26 days  False   True  5   True  1
9   80 2019-07-19    406             2  1 days   True  False  5   True  2
10  80 2019-07-26    408             0  7 days  False   True  6  False  1
11  80 2019-08-02    410             1  7 days  False   True  7   True  1
12  80 2019-08-03    413             2  1 days   True  False  7   True  2
Run Code Online (Sandbox Code Playgroud)

而在去年附加0通过numpy.where与面膜mask

print (df.assign(diff= df.groupby('ID')['tDate'].diff(),
                 a = df.groupby('ID')['tDate'].diff().eq(pd.Timedelta(1, unit='d')),
                 a_neg = ~a,
                 s = (~a).cumsum(),
                 mask = a.groupby(s).transform('any'),
                 c = df.groupby(s).cumcount(1).add(1),
                 out =  np.where(mask, df.groupby(s).cumcount(1).add(1), 0)))

    ID      tDate  value  consec_count    diff      a  a_neg  s   mask  c  out
0   79 2019-07-12    397             1     NaT  False   True  1   True  1    1
1   79 2019-07-13    404             2  1 days   True  False  1   True  2    2
2   79 2019-07-18    405             1  5 days  False   True  2   True  1    1
3   79 2019-07-19    406             2  1 days   True  False  2   True  2    2
4   79 2019-07-20    408             3  1 days   True  False  2   True  3    3
5   79 2019-08-03    413             0 14 days  False   True  3  False  1    0
6   80 2019-06-21    397             1     NaT  False   True  4   True  1    1
7   80 2019-06-22    404             2  1 days   True  False  4   True  2    2
8   80 2019-07-18    405             1 26 days  False   True  5   True  1    1
9   80 2019-07-19    406             2  1 days   True  False  5   True  2    2
10  80 2019-07-26    408             0  7 days  False   True  6  False  1    0
11  80 2019-08-02    410             1  7 days  False   True  7   True  1    1
12  80 2019-08-03    413             2  1 days   True  False  7   True  2    2
Run Code Online (Sandbox Code Playgroud)


And*_* L. 1

您还可以尝试在 groupby 上创建掩码ID,并or标记shift(-1)所有连续行True并分配给 mask s1。最后,使用np.whereons1s1.groupby.cumsum

s = df.groupby('ID').tDate.diff().eq(pd.Timedelta(days=1))
s1 = s | s.shift(-1, fill_value=False)
df['consec_count'] = np.where(s1, s1.groupby(df.ID).cumsum(), 0)

Out[185]:
   ID      tDate  value  consec_count
0  79 2019-06-21    397             0
1  79 2019-07-13    404             0
2  79 2019-07-18    405             1
3  79 2019-07-19    406             2
4  79 2019-08-02    410             0
5  79 2019-08-09    413             0
Run Code Online (Sandbox Code Playgroud)