Pandas如何过滤Series数据?

161

在使用groupby('name')函数并对另一列使用mean()函数后,我得到了这样的Series。

name
383      3.000000
663      1.000000
726      1.000000
737      9.000000
833      8.166667

请问有人能告诉我如何筛选掉平均值为1.000000的行吗?谢谢并非常感谢您的帮助。


你会如何根据给定条件筛选一个序列? - user554546
7个回答

197
In [5]:

import pandas as pd

test = {
383:    3.000000,
663:    1.000000,
726:    1.000000,
737:    9.000000,
833:    8.166667
}

s = pd.Series(test)
s = s[s != 1]
s
Out[0]:
383    3.000000
737    9.000000
833    8.166667
dtype: float64

14
我更喜欢下面的答案,因为它们可以被链接在一起(即不需要定义s并在表达式中使用两次)。不过这只适用于pandas 0.18及以上版本。 - IanS
请参见piRSquared的答案中的时间比较。 - IanS

101

3
方法链式调用非常方便(并让我想起了Spark)。 - Dylan Hogg
2
在这种情况下,True但Spark做了更直观的事情:它只是摆脱不符合谓词的行,这意味着不使用“。dropna()”部分,这对我来说似乎显然是多余的,直到我读了文档才知道。被咬了一口:D - Florent Moiny

81

正如DACW指出,在pandas 0.18.1中有 方法链接改进,非常好地完成了你所需要的功能。

与其使用.where,你可以将函数传递给.loc索引器或Series索引器的[],从而避免调用.dropna

test = pd.Series({
383:    3.000000,
663:    1.000000,
726:    1.000000,
737:    9.000000,
833:    8.166667
})

test.loc[lambda x : x!=1]

test[lambda x: x!=1]

DataFrame 和 NDFrame 类也支持类似的行为。


3
这是我最喜欢的答案,也似乎是最快的,而且没有使用numpy(请参见时间比较)。 - IanS

31
一种快速的方法是使用numpy对底层数组进行切片重构。请参见下面的计时。
mask = s.values != 1
pd.Series(s.values[mask], s.index[mask])

0
383    3.000000
737    9.000000
833    8.166667
dtype: float64

天真的时序

输入图片描述


我喜欢你的方法,但我想知道如果我有多个掩码会怎样。谢谢。 - Menglong Li
1
@MenglongLi 取决于情况,你应该提出一个问题。很可能,你需要用 & 将它们组合起来。mask = mask1 & mask2 - piRSquared

7

另一种方法是先将数据转换为DataFrame,然后使用query方法(假设您已安装了numexpr):

import pandas as pd

test = {
383:    3.000000,
663:    1.000000,
726:    1.000000,
737:    9.000000,
833:    8.166667
}

s = pd.Series(test)
s.to_frame(name='x').query("x != 1")

我不认为将条件作为字符串传递是一个好主意。 - SzymonPajzert
2
这会增加数据框的所有开销,速度会非常慢。 - fantabolous

4
如果您喜欢链式操作,您也可以使用compress函数:
test = pd.Series({
383:    3.000000,
663:    1.000000,
726:    1.000000,
737:    9.000000,
833:    8.166667
})

test.compress(lambda x: x != 1)

# 383    3.000000
# 737    9.000000
# 833    8.166667
# dtype: float64

2
请注意,自pandas版本0.24.0以后,pandas.Series.compress已被弃用。 - ipj

2

在我的情况下,我有一个pandas Series,其中值是字符元组:

Out[67]
0    (H, H, H, H)
1    (H, H, H, T)
2    (H, H, T, H)
3    (H, H, T, T)
4    (H, T, H, H)

因此,我可以使用索引来过滤系列,但是为了创建索引,我需要使用apply。我的条件是“查找所有恰好具有一个'H'的元组”。
series_of_tuples[series_of_tuples.apply(lambda x: x.count('H')==1)]

我承认它不是“可链接”的(即请注意我重复使用了series_of_tuples两次;您必须将任何临时序列存储到变量中,以便可以在其上调用apply(...))。

除了.apply(...)之外,可能还有其他方法可以逐个元素地生成布尔索引。

许多其他答案(包括被接受的答案)使用可链接函数,例如:

  • .compress()
  • .where()
  • .loc[]
  • []

这些函数接受可调用对象(lambda),应用于Series,而不是这些系列中的各个值!

因此,当我尝试使用上述条件/可调用对象/lambda和任何可链接函数(如.loc[])时,我的元组系列表现出奇怪的行为:

series_of_tuples.loc[lambda x: x.count('H')==1]

产生了以下错误:

KeyError: 'Level H must be same as name (None)'

我感到非常困惑,但似乎是使用了 Series.count series_of_tuples.count(...) 函数,而这不是我想要的。

我承认另一种数据结构可能更好:

  • 一个类别数据类型?
  • 一个数据框(元组的每个元素变成一列)
  • 一系列字符串(只需将元组连接在一起):

这将创建一系列字符串(即通过将元组连接在一起来连接元组中的字符形成单个字符串)

series_of_tuples.apply(''.join)

这样,我就可以使用可链接的 Series.str.count 函数。

series_of_tuples.apply(''.join).str.count('H')==1

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