如何在Python的Pandas中删除值出现频率小于5次的行?

5

我有一个包含许多行的数据帧。有时候值只出现一次,对我的目的来说没有什么用处。

如何删除所有列2和3的值不超过5次的行?

df输入:

 Col1     Col2     Col3       Col4
 1        apple    tomato     banana
 1        apple    potato     banana
 1        apple    tomato     banana
 1        apple    tomato     banana
 1        apple    tomato     banana
 1        apple    tomato     banana
 1        grape    tomato     banana
 1        pear     tomato     banana
 1        lemon    tomato     banana

输出

 Col1     Col2     Col3       Col4
 1        apple    tomato     banana
 1        apple    tomato     banana
 1        apple    tomato     banana
 1        apple    tomato     banana
 1        apple    tomato     banana

请查看这个非常有用的答案:https://dev59.com/D2Ik5IYBdhLWcg3wUsvv - Denis Rasulev
你是基于全局计数还是列计数进行检查的?这有关系吗?除了Col2,apple也可能出现在Col3中吗? - cs95
@coldspeed 按列计数。计数限制于该列,因此如果“apple”出现在不同的列中,则应基于col2的计数。 - aiden rosenblatt
如果Col3的第一个值是“potato”,那么期望的输出应该是什么? - Tai
6个回答

6

全局计数
使用 stack + value_counts + replace -

v = df[['Col2', 'Col3']]
df[v.replace(v.stack().value_counts()).gt(5).all(1)]

   Col1   Col2    Col3    Col4
0     1  apple  tomato  banana
2     1  apple  tomato  banana
3     1  apple  tomato  banana
4     1  apple  tomato  banana
5     1  apple  tomato  banana

(更新)
按列计数

在你感兴趣的列上使用apply函数和pd.Series.value_counts来进行计数,然后以与之前相同的方式进行过滤 -

v = df[['Col2', 'Col3']]
df[v.replace(v.apply(pd.Series.value_counts)).gt(5).all(1)]

   Col1   Col2    Col3    Col4
0     1  apple  tomato  banana
2     1  apple  tomato  banana
3     1  apple  tomato  banana
4     1  apple  tomato  banana
5     1  apple  tomato  banana

细节
使用value_counts对数据框中的值进行计数 -

c = v.apply(pd.Series.value_counts)
c

        Col2  Col3
apple    6.0   NaN
grape    1.0   NaN
lemon    1.0   NaN
pear     1.0   NaN
potato   NaN   1.0
tomato   NaN   8.0

使用replace函数,将DataFrame中的值替换为它们的计数-

i = v.replace(c)
i

   Col2  Col3
0     6     8
1     6     1
2     6     8
3     6     8
4     6     8
5     6     8
6     1     8
7     1     8
8     1     8

从那时起,

m = i.gt(5).all(1)

0     True
1    False
2     True
3     True
4     True
5     True
6    False
7    False
8    False
dtype: bool

使用掩码对df进行索引。

这也将过滤掉我们不想要的Col4。同时,它会计算所有值 - 而不是在单独的列中。因此,我们可能会遇到不同列中相同值的问题。 - Alexey Trofimov
1
@AlexeyTrofimov 那是你的解释。我的解释是,根据我对问题的理解,计数是全局的。在我们进一步讨论之前,我们将等待 OP 澄清 ;) - cs95
计数基于每一行。@AlexeyTrofimov 我正在尝试清洗这个数据集中的低频值。 - aiden rosenblatt
我也不理解每一行的部分 :) - Alexey Trofimov
这个解决方案考虑了全局计数,我需要列计数。 - aiden rosenblatt
显示剩余2条评论

5
使用transform轻松实现
counts_col2 = df.groupby("Col2")["Col2"].transform(len)
counts_col3 = df.groupby("Col3")["Col3"].transform(len)

mask = (counts_col2 > 5) & (counts_col3 > 5)

df[mask]

输出:

   Col1   Col2    Col3    Col4
0     1  apple  tomato  banana
2     1  apple  tomato  banana
3     1  apple  tomato  banana
4     1  apple  tomato  banana
5     1  apple  tomato  banana

在这个特定的问题中,据我所见,我们只关注Col2和Col3。 - Alexey Trofimov
我的问题的第二部分仍然适用。如果有其他10列呢? - cs95
我们可以轻松地循环遍历它们,将计数存储在列表中,制作掩码。 - Alexey Trofimov
1
如果您正在使用 groupby,则可以使用 .filter 代替 transformdf.groupby("Col2").filter(lambda grp: len(grp) > 3) - Ben
1
@Alexey Trofimov,你能解释一下 df.groupby("Col2")["Col2"].transform(len) 这个是什么意思吗? - Zesty Dragon
显示剩余2条评论

1
创建示例数据框。
import pandas as pd

text = '''Col1     Col2     Col3       Col4
 1        apple    tomato     banana
 1        apple    potato     banana
 1        apple    tomato     banana
 1        apple    tomato     banana
 1        apple    tomato     banana
 1        apple    tomato     banana
 1        grape    tomato     banana
 1        pear     tomato     banana
 1        lemon    tomato     banana'''

count = 1
data = []
for line in text.split('\n'):
    if count == 1:
        headers = line.split()
    else:
        data.append(line.split())
    count += 1

df = pd.DataFrame(data = data,columns=headers)

value_counts方法生成一个字典,其中唯一的列值作为键,计数作为值。我将这些键分配给k。

  • value_counts返回一个Pandas系列对象,但它像一个字典

这个列表推导式有一个过滤器'if'语句,如果与之关联的值不是> 5,则忽略键

在这个例子中,它只返回一个值的列表,但在其他情况下可能会更多。

Col2_more_than_5 = [k for k in df['Col2'].value_counts().keys() 
if df['Col2'].value_counts()[k] > 5]

Col3_more_than_5 = [k for k in df['Col3'].value_counts().keys() 
if df['Col3'].value_counts()[k] > 5]

我现在有两个列表,它们包含每列中出现次数 > 5 的字符串,现在我创建了一个选择器,返回同时满足这两个条件的行。

df[(df['Col2'].isin(Col2_more_than_5)) & (df['Col3'].isin(Col3_more_than_5))]

'isin'方法适用于列表中有多个值的情况。

enter image description here


这段代码是做什么的?df[(df['Col2'] == first[0]) & (df['Col3'] == second[0])] - aiden rosenblatt
它使用列表推导式中的每个值来过滤列。 - memebrain
3
"添加一些描述以证明你的答案。" - Satendra
我修改了我的答案,使其更清晰、更健壮,如果有多个值大于5,我还添加了更多的解释。 - memebrain

1
v=df.astype(str).sum(1)
df[v.eq(v.value_counts()[v.value_counts()>=5].index.values[0])]
Out[145]: 
   Col1   Col2    Col3    Col4
0     1  apple  tomato  banana
2     1  apple  tomato  banana
3     1  apple  tomato  banana
4     1  apple  tomato  banana
5     1  apple  tomato  banana

嗯...调用value_counts两次似乎有些浪费。也许可以缓存中间结果? - cs95

1

最快的方法,由@ALollz提供

def agg_size_nosort(df):
    counts_col2 = df.groupby("Col2", sort=False)["Col2"].transform('size')
    counts_col3 = df.groupby("Col3", sort=False)["Col3"].transform('size')
    mask = (counts_col2 > 5) & (counts_col3 > 5)
    return df[mask]

0

一个人也可以两次使用filter

df.groupby("Col2").filter(lambda x: len(x) >= 5) \
  .groupby("Col3").filter(lambda x: len(x) >= 5)

filter的文档说明如下:

返回一个从DataFrame中排除不满足由func指定的布尔条件的组元素的副本。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接