这是您可以与groupby 一起使用的解决方案:
# convert nans to str
df["names"] = df["names"].fillna("NaN")
# assign a subgroup to each set of consecutive rows
df["subgroup"] = df["names"].ne(df["names"].shift()).cumsum()
# take the max length of any subgroup that belongs to "name"
def get_max_consecutive(name):
return df.groupby(["names", "subgroup"]).apply(len)[name].max()
for name in df.names.unique():
print(f"{name}: {get_max_consecutive(name)}")
输出:
Alan: 3
John: 2
NaN: 5
Christy: 2
说明:
pandas.Series.ne 接受两个系列并返回一个新系列,如果每行中的元素不相等,则返回 True,如果相等则返回 False。
我们可以使用 df["names"] 并将其与自身进行比较,除了移位 1 (df["names"].shift())。每当名称从以前的值更改时,这将返回 True。
所以这给了我们一个布尔系列,其中每个True 都标志着名称的变化:
df["names"].ne(df["names"].shift())
0 True
1 False
2 True
3 False
4 True
5 False
6 False
7 True
8 False
9 False
10 False
11 False
12 True
13 False
14 True
Name: names, dtype: bool
那么,.cumsum 只是这个系列的累加和。在这种情况下,True 等于 1,False 为 0。这有效地为我们提供了一个新数字,每次名称从先前的值更改时。我们可以将它分配给它自己的列subgroup,以便我们稍后使用 groupby。
df.names.ne(df.names.shift()).cumsum()
0 1
1 1
2 2
3 2
4 3
5 3
6 3
7 4
8 4
9 4
10 4
11 4
12 5
13 5
14 6
Name: names, dtype: int64
最后,我们可以使用 .groupby 在“名称”和“子组”列上使用多索引对数据框进行分组。现在我们可以应用len 函数来获取每个子组的长度。
df.groupby(["names", "subgroup"]).apply(len)
names subgroup
Alan 1 2
3 3
Christy 5 2
John 2 2
6 1
NaN 4 5
dtype: int64
奖励:如果您想查看每个名称和子组的 len,可以使用 .reset_index 将 .apply 返回的系列转换为数据框:
df_count = df.groupby(["names", "subgroup"]).apply(len).reset_index(name="len")
df_count
输出:
names subgroup len
0 Alan 1 2
1 Alan 3 3
2 Christy 5 2
3 John 2 2
4 John 6 1
5 NaN 4 5