python pandas:删除A列的重复项,保留B列中具有最高值的行

Posted

技术标签:

【中文标题】python pandas:删除A列的重复项,保留B列中具有最高值的行【英文标题】:Remove duplicates by columns A, keeping the row with the highest value in column B 【发布时间】:2012-09-11 22:05:58 【问题描述】:

我在 A 列中有一个包含重复值的数据框。我想删除重复项,将具有最高值的行保留在 B 列中。

所以这个:

A B
1 10
1 20
2 30
2 40
3 10

应该变成这样:

A B
1 20
2 40
3 10

Wes 添加了一些不错的功能来删除重复项:http://wesmckinney.com/blog/?p=340。但是 AFAICT,它是为精确重复而设计的,因此没有提及选择保留哪些行的标准。

我猜可能有一种简单的方法可以做到这一点——可能就像在删除重复项之前对数据帧进行排序一样简单——但我不太了解 groupby 的内部逻辑,无法弄清楚。有什么建议吗?

【问题讨论】:

请注意,问题中的 URL 出现 EOL。 对于惯用和高效的方式,see this solution below。 时间在流逝......在撰写本文时,我相信this solution below 更快(至少在有很多重复的情况下)而且更简单。 【参考方案1】:

这是最后一个。虽然不是最大值:

In [10]: df.drop_duplicates(subset='A', keep="last")
Out[10]: 
   A   B
1  1  20
3  2  40
4  3  10

你也可以这样做:

In [12]: df.groupby('A', group_keys=False).apply(lambda x: x.loc[x.B.idxmax()])
Out[12]: 
   A   B
A       
1  1  20
2  2  40
3  3  10

【讨论】:

小注:colstake_last 参数已被折旧,已被 subsetkeep 参数取代。 pandas.pydata.org/pandas-docs/version/0.17.1/generated/… 正如@Jezzamon 所说,FutureWarning: the take_last=True keyword is deprecated, use keep='last' instead 有理由不使用df.sort_values(by=['B']).drop_duplicates(subset=['A'], keep='last')吗?我的意思是这个 sort_values 对我来说似乎是安全的,但我不知道它是否真的是。 这个答案现在已经过时了。请参阅下面@Ted Petrou 的回答。 如果你想使用这段代码但group_by中有多个列的情况,你可以添加.reset_index(drop=True)df.groupby(['A','C'], group_keys=False).apply(lambda x: x.ix[x.B.idxmax()]).reset_index(drop=True)这将重置索引,因为它的默认值是由 'A''C' 组成的 Multindex 【参考方案2】:

最重要的答案是做太多的工作,而且对于更大的数据集看起来很慢。 apply 很慢,应该尽可能避免。 ix 已被弃用,也应避免使用。

df.sort_values('B', ascending=False).drop_duplicates('A').sort_index()

   A   B
1  1  20
3  2  40
4  3  10

或者简单地按所有其他列分组并取您需要的列的最大值。 df.groupby('A', as_index=False).max()

【讨论】:

这实际上是一种切刀方法。我想知道它是否可以通过在删除时使用一些lamba 函数来概括。例如,我怎样才能只删除小于这些重复值的平均值的值。 这比groupby 慢(因为最初的sort_values()O[n log n]groupby 避免了)。见a 2021 answer。【参考方案3】:

最简单的解决方案:

根据一列删除重复项:

df = df.drop_duplicates('column_name', keep='last')

要删除基于多列的重复项:

df = df.drop_duplicates(['col_name1','col_name2','col_name3'], keep='last')

【讨论】:

最佳解决方案。谢谢。 很高兴为您提供帮助。 @Flavio 我的数据框有 10 列,我使用此代码删除了三列中的重复项。但是,它从其余列中删除了行。有没有办法只删除最后 4 列的重复项? 但是 OP 希望在 B 列中保留最高值。如果您先排序,这可能会起作用。但这基本上是 Ted Petrou 的回答。 记得将 df 分配回 df df = df.drop_duplicates。单独做df.drop_duplicates(...) 是行不通的【参考方案4】:

我会先用 B 列降序对数据框进行排序,然后删除 A 列的重复项并保持第一

df = df.sort_values(by='B', ascending=False)
df = df.drop_duplicates(subset='A', keep="first")

没有任何分组

【讨论】:

【参考方案5】:

试试这个:

df.groupby(['A']).max()

【讨论】:

你知道重新索引它以使其看起来像原始 DataFrame 的最佳习语吗?当你忍着我时,我正试图弄清楚这一点。 :^) 整洁。如果数据框包含更多列(例如 C、D、E)怎么办? Max 在这种情况下似乎不起作用,因为我们需要指定 B 是唯一需要最大化的列。 @DSM 检查原始问题中的链接。有一些代码可以重新索引分组的数据框。【参考方案6】:

我认为在您的情况下,您实际上并不需要 groupby。我会按您的 B 列的降序排序,然后在 A 列删除重复项,如果您愿意,您还可以有一个新的 nice 和 像这样清理索引:

df.sort_values('B', ascending=False).drop_duplicates('A').sort_index().reset_index(drop=True)

【讨论】:

这与其他帖子有何不同?【参考方案7】:

我是通过来自duplicate question 的链接被带到这里的。

只有两列,不是更简单吗:

df.groupby('A')['B'].max().reset_index()

并保留一整行(当有更多列时,这就是将我带到这里的“重复问题”所问的问题):

df.loc[df.groupby(...)[column].idxmax()]

例如,要保留'C' 取其最大值的整行,对于每组['A', 'B'],我们会这样做:

out = df.loc[df.groupby(['A', 'B')['C'].idxmax()]

当组相对较少时(即很多重复项),这比 drop_duplicates() solution 更快(排序较少):

设置:

n = 1_000_000
df = pd.DataFrame(
    'A': np.random.randint(0, 20, n),
    'B': np.random.randint(0, 20, n),
    'C': np.random.uniform(size=n),
    'D': np.random.choice(list('abcdefghijklmnopqrstuvwxyz'), size=n),
)

(添加sort_index()以确保平等解决):

%timeit df.loc[df.groupby(['A', 'B'])['C'].idxmax()].sort_index()
# 101 ms ± 98.7 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit df.sort_values(['C', 'A', 'B'], ascending=False).drop_duplicates(['A', 'B']).sort_index()
# 667 ms ± 784 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

【讨论】:

【参考方案8】:

你也可以试试

df.drop_duplicates(subset='A', keep='last')

我从https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.drop_duplicates.html 提到了这个

【讨论】:

【参考方案9】:

我必须解决一个值得分享的变体:对于columnA 中的每个唯一字符串,我想在columnB 中找到最常见的关联字符串。

df.groupby('columnA').agg('columnB': lambda x: x.mode().any()).reset_index()

.any() 会在该模式出现平局时选择一个。 (请注意,在一系列 ints 上使用 .any() 会返回一个布尔值,而不是选择其中一个。)

对于原题,对应的做法简化为

df.groupby('columnA').columnB.agg('max').reset_index().

【讨论】:

【参考方案10】:

最简单的方法:

# First you need to sort this DF as Column A as ascending and column B as descending 
# Then you can drop the duplicate values in A column 
# Optional - you can reset the index and get the nice data frame again
# I'm going to show you all in one step. 

d = 'A': [1,1,2,3,1,2,3,1], 'B': [30, 40,50,42,38,30,25,32]
df = pd.DataFrame(data=d)
df

    A   B
0   1   30
1   1   40
2   2   50
3   3   42
4   1   38
5   2   30
6   3   25
7   1   32


df = df.sort_values(['A','B'], ascending =[True,False]).drop_duplicates(['A']).reset_index(drop=True)

df

    A   B
0   1   40
1   2   50
2   3   42

【讨论】:

【参考方案11】:

当已经给出的帖子回答了这个问题时,我通过添加应用 max() 函数的列名进行了一些小改动,以提高代码的可读性。

df.groupby('A', as_index=False)['B'].max()

【讨论】:

请为您的答案提供更多背景信息,解释它们的工作原理以及它们为何优于或补充现有问题的答案。如果它们没有提供附加值,请不要发布关于旧问题的额外答案。最后,请format你的代码缩进代码块。【参考方案12】:

这也有效:

a=pd.DataFrame('A':a.groupby('A')['B'].max().index,'B':a.groupby('A')       ['B'].max().values)

【讨论】:

虽然这段代码 sn-p 可以解决问题,但including an explanation 确实有助于提高帖子的质量。请记住,您正在为将来的读者回答问题,而这些人可能不知道您的代码建议的原因。也请尽量不要用解释性的 cmets 挤满你的代码,这会降低代码和解释的可读性!【参考方案13】:

我不会给你完整的答案(我不认为你正在寻找解析和写入文件部分),但一个关键的提示就足够了:使用 python 的 set() 函数,然后sorted().sort() 加上.reverse()

>>> a=sorted(set([10,60,30,10,50,20,60,50,60,10,30]))
>>> a
[10, 20, 30, 50, 60]
>>> a.reverse()
>>> a
[60, 50, 30, 20, 10]

【讨论】:

也许我错了,但是将 pandas DataFrame 重新转换为一个集合,然后将其转换回来似乎是解决此问题的一种非常低效的方法。我正在做日志分析,所以我会将其应用于一些非常大的数据集。 对不起,我对这个特定场景了解不多,所以我的通用答案可能不会对您的问题太有效。

以上是关于python pandas:删除A列的重复项,保留B列中具有最高值的行的主要内容,如果未能解决你的问题,请参考以下文章

有条件地删除重复的pandas python

有条件地删除重复的pandas python

使用 pandas 和 Python 删除重复项

pandas删除数据行中的重复数据行基于dataframe所有列删除重复行基于特定数据列或者列的作何删除重复行删除重复行并保留重复行中的最后一行pandas删除所有重复行(不进行数据保留)

在python数据框中删除不包含列中特定字符串的多列的重复项

python pandas删除重复的列