在 pandas 列中,如何找到特定值出现的最大连续行数?
In a pandas column, how to find the max number of consecutive rows that a particular value occurs?
假设我们有以下带有列名的 df。
df = pd.DataFrame({
'names':['Alan', 'Alan', 'John', 'John', 'Alan', 'Alan','Alan', np.nan, np.nan, np.nan, np.nan, np.nan, 'Christy', 'Christy','John']})
>>> df
names
0 Alan
1 Alan
2 John
3 John
4 Alan
5 Alan
6 Alan
7 NaN
8 NaN
9 NaN
10 NaN
11 NaN
12 Christy
13 Christy
14 John
我想 运行 在列上应用函数,returns 特定值出现的最大连续次数。起初,我想为 NaN 执行此操作,但推而广之,我想切换到列中的任何其他值。
说明:
如果我们 运行 申请 Nan,结果将是 5,因为 5 是 NaN 连续出现的最高次数。如果列中其他值之后有后续行,然后 NaN 连续出现 gt 超过 5 次,那么结果就是这样。
如果我们运行 申请 Alan,结果将是 3,因为 3 将在连续出现的 Alan 的第一次出现中取代 2。
df_counts = df #create new df to keep the original
df_counts['names'].fillna("NaN", inplace=True) # replace np.nan with string
df_counts['counts'] = df.names.groupby((df.names != df.names.shift()).cumsum()).transform('size') # count consecutive names
df_counts = df_counts.sort_values('counts').drop_duplicates("names",keep='last') #keep only the highest counts
def get_counts(name):
return df_counts.loc[df['names'] == name, 'counts'].item()
那么get_counts("Alan")
会return3
,get_counts("NaN")
会return5
.
这是一个可以与 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
采用两个系列,returns 一个新系列,如果每行中的元素不相等则为 True,如果它们相等则为 False。
我们可以使用 df["names"]
并将其与自身进行比较,除了移位 1 (df["names"].shift()
)。每当名称从以前的值更改时,这将 return 为真。
所以这给了我们一个布尔系列,其中每个 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
奖励: 如果您想查看每个名称和子组的长度,您可以将 return 由 .apply
into a dataframe using .reset_index
编辑的系列:
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
由于np.nan == np.nan
为False,所以在计数之前必须检查提供的值是否为NaN。要获取连续的元素,您可以使用 itertools' groupby
.
def max_consecutives(value):
if pd.isna(value):
value_equals = lambda x: pd.isna(x)
else:
value_equals = lambda x: x == value
def max_consecutive_values(col):
elements_per_group_counter = (
sum(1 for elem in group if value_equals(elem))
for _, group in groupby(col)
)
return max(elements_per_group_counter)
return max_consecutive_values
df.apply(max_consecutives(np.nan)) # returns 5
df.apply(max_consecutives("Alan")) # returns 3
假设我们有以下带有列名的 df。
df = pd.DataFrame({
'names':['Alan', 'Alan', 'John', 'John', 'Alan', 'Alan','Alan', np.nan, np.nan, np.nan, np.nan, np.nan, 'Christy', 'Christy','John']})
>>> df
names
0 Alan
1 Alan
2 John
3 John
4 Alan
5 Alan
6 Alan
7 NaN
8 NaN
9 NaN
10 NaN
11 NaN
12 Christy
13 Christy
14 John
我想 运行 在列上应用函数,returns 特定值出现的最大连续次数。起初,我想为 NaN 执行此操作,但推而广之,我想切换到列中的任何其他值。
说明: 如果我们 运行 申请 Nan,结果将是 5,因为 5 是 NaN 连续出现的最高次数。如果列中其他值之后有后续行,然后 NaN 连续出现 gt 超过 5 次,那么结果就是这样。
如果我们运行 申请 Alan,结果将是 3,因为 3 将在连续出现的 Alan 的第一次出现中取代 2。
df_counts = df #create new df to keep the original
df_counts['names'].fillna("NaN", inplace=True) # replace np.nan with string
df_counts['counts'] = df.names.groupby((df.names != df.names.shift()).cumsum()).transform('size') # count consecutive names
df_counts = df_counts.sort_values('counts').drop_duplicates("names",keep='last') #keep only the highest counts
def get_counts(name):
return df_counts.loc[df['names'] == name, 'counts'].item()
那么get_counts("Alan")
会return3
,get_counts("NaN")
会return5
.
这是一个可以与 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
采用两个系列,returns 一个新系列,如果每行中的元素不相等则为 True,如果它们相等则为 False。
我们可以使用 df["names"]
并将其与自身进行比较,除了移位 1 (df["names"].shift()
)。每当名称从以前的值更改时,这将 return 为真。
所以这给了我们一个布尔系列,其中每个 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
奖励: 如果您想查看每个名称和子组的长度,您可以将 return 由 .apply
into a dataframe using .reset_index
编辑的系列:
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
由于np.nan == np.nan
为False,所以在计数之前必须检查提供的值是否为NaN。要获取连续的元素,您可以使用 itertools' groupby
.
def max_consecutives(value):
if pd.isna(value):
value_equals = lambda x: pd.isna(x)
else:
value_equals = lambda x: x == value
def max_consecutive_values(col):
elements_per_group_counter = (
sum(1 for elem in group if value_equals(elem))
for _, group in groupby(col)
)
return max(elements_per_group_counter)
return max_consecutive_values
df.apply(max_consecutives(np.nan)) # returns 5
df.apply(max_consecutives("Alan")) # returns 3