我有以下数据框:
A B C
1 3 2
1 7 7
1 7 7
1 5 4
2 2 1
2 8 8
2 4 5
3 5 3
3 1 9
3 4 4
我想要做的是,对于 A 列中的每组相同值,找到 B 列中的值等于 C 中的值的最后一行,然后返回最后一行之前的行,其中 B = C,包括行本身。所以预期的结果是:
A B C
1 3 2
1 7 7
1 7 7
2 2 1
2 8 8
3 5 3
3 1 9
3 4 4
我尝试了下面的代码,但它返回第一行之前的行,其中 B = C,而不是最后一行之前。
mask = df['B'] == df['C']
df.loc[mask[::-1].groupby(df['A']).cummax()]
groupby.cummax
可以按预期工作:
m = df['B'].eq(df['C'])
out = df[m[::-1].groupby(df['A']).cummax()]
输出:
A B C
0 1 3 2
1 1 7 7
2 1 7 7
4 2 2 1
5 2 8 8
7 3 5 3
8 3 1 9
9 3 4 4
可重复输入:
df = pd.DataFrame({'A': [1, 1, 1, 1, 2, 2, 2, 3, 3, 3],
'B': [3, 7, 7, 5, 2, 8, 4, 5, 1, 4],
'C': [2, 7, 7, 4, 1, 8, 5, 3, 9, 4]})
错误可能来自具有重复值的索引,在这种情况下,您可以使用底层 numpy 数组绕过索引对齐并手动反转输出:
grouper = df.loc[::-1, 'A'].values
out = df.loc[m[::-1].groupby(grouper).cummax()[::-1]]
替代输入:
df = pd.DataFrame({'A': [1, 1, 1, 1, 2, 2, 2, 3, 3, 3],
'B': [3, 7, 7, 5, 2, 8, 4, 5, 1, 4],
'C': [2, 7, 7, 4, 1, 8, 5, 3, 9, 4]},
index=[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
)
tmp = df.assign(m=df['B'].ne(df['C']))
# rows before the first equality
m1 = tmp.groupby('A')['m'].cummin()
# first equality
m2 = ~tmp.duplicated(['A', 'm'])
out = df[m1|m2]
注意。如果您不想包含第一个等式,只需使用
out = df[m1]
。
输出:
A B C
0 1 3 2
1 1 7 7
4 2 2 1
5 2 8 8
7 3 5 3
8 3 1 9
9 3 4 4
对于我正确地工作您的解决方案,这是创建帮助列的替代方案:
mask = df['B'] == df['C']
df = df[df.assign(m=mask).iloc[::-1].groupby('A')['m'].cummax()]
#or
#df = df[df.assign(m=mask).iloc[::-1].groupby('A')['m'].cummax().iloc[::-1]]
print (df)
A B C
0 1 3 2
1 1 7 7
2 1 7 7
4 2 2 1
5 2 8 8
7 3 5 3
8 3 1 9
9 3 4 4
另一个想法是创建默认索引:
df = df.reset_index(drop=True)
#OP solution
mask = df['B'] == df['C']
df.loc[mask[::-1].groupby(df['A']).cummax()]
编辑:如果需要第一个相同的
B
和 C
列之前的所有行,请使用:
#sample data
print (df)
A B C
0 1 3 2
1 1 7 7
2 1 7 7
3 1 5 4
4 1 7 7
5 1 2 2
6 2 2 1
7 2 8 8
8 2 4 5
9 3 5 3
10 3 1 9
11 3 4 4
12 4 7 7
13 4 7 7
14 4 7 5
15 5 4 4
16 5 4 4
17 5 1 1
18 5 1 1
19 6 8 4
#compare columns
m = df['B'] == df['C']
#create groups by consecutive B, C columns
df1 = df.assign(g = df[['B','C']].ne(df[['B','C']].shift()).any(axis=1).cumsum())
print (df1)
A B C g
0 1 3 2 1
1 1 7 7 2
2 1 7 7 2
3 1 5 4 3
4 1 7 7 4
5 1 2 2 5
6 2 2 1 6
7 2 8 8 7
8 2 4 5 8
9 3 5 3 9
10 3 1 9 10
11 3 4 4 11
12 4 7 7 12
13 4 7 7 12
14 4 7 5 13
15 5 4 4 14
16 5 4 4 14
17 5 1 1 15
18 5 1 1 15
19 6 8 4 16
#filter only matched B, C and aggregate min
s = df1[m].groupby('A')['g'].min()
#compare mapped A groups and filter if less or equal values
df2 = df[df1['g'].le(df['A'].map(s))]
print (df2)
A B C
0 1 3 2
1 1 7 7
2 1 7 7
6 2 2 1
7 2 8 8
9 3 5 3
10 3 1 9
11 3 4 4
12 4 7 7
13 4 7 7
15 5 4 4
16 5 4 4
#if need only first row with same B, C chain duplicated
df3 = df[df1['g'].le(df['A'].map(s)) & ~df1['g'].duplicated()]
print (df3)
A B C
0 1 3 2
1 1 7 7
6 2 2 1
7 2 8 8
9 3 5 3
10 3 1 9
11 3 4 4
12 4 7 7
15 5 4 4
duckdb:
(
df1.sql.select("*,(b=c)::int col1")
.select("*,max(index) filter(col1=1) over(partition by a,col1) col2")
.select("*,max(col2) over(partition by a) col3")
.filter("index<=col3")
.order("index")
).df()