获取 pandas 布尔系列为 True 的索引列表
Posted
技术标签:
【中文标题】获取 pandas 布尔系列为 True 的索引列表【英文标题】:Getting a list of indices where pandas boolean series is True 【发布时间】:2019-02-09 21:25:29 【问题描述】:我有一个带有布尔条目的熊猫系列。我想获取值为True
的索引列表。
例如输入pd.Series([True, False, True, True, False, False, False, True])
应该产生输出[0,2,3,7]
。
我可以通过列表理解来做到这一点,但是有什么更简洁或更快的方法吗?
【问题讨论】:
更好的测试用例是s = pd.Series([True, False, True, True, False, False, False, True], index=list('ABCDEFGH'))
。预期输出:Index(['A', 'C', 'D', 'H'], ...)
。由于某些解决方案(尤其是所有 np 函数)会删除索引并使用自动编号索引。
...如果我们有一个命名索引,通常不希望删除它。
【参考方案1】:
使用Boolean Indexing
>>> s = pd.Series([True, False, True, True, False, False, False, True])
>>> s[s].index
Int64Index([0, 2, 3, 7], dtype='int64')
如果需要np.array
对象,请获取.values
>>> s[s].index.values
array([0, 2, 3, 7])
使用np.nonzero
>>> np.nonzero(s)
(array([0, 2, 3, 7]),)
使用np.flatnonzero
>>> np.flatnonzero(s)
array([0, 2, 3, 7])
使用np.where
>>> np.where(s)[0]
array([0, 2, 3, 7])
使用np.argwhere
>>> np.argwhere(s).ravel()
array([0, 2, 3, 7])
使用pd.Series.index
>>> s.index[s]
array([0, 2, 3, 7])
使用python内置的filter
>>> [*filter(s.get, s.index)]
[0, 2, 3, 7]
使用list comprehension
>>> [i for i in s.index if s[i]]
[0, 2, 3, 7]
【讨论】:
如果系列索引有标签而不是索引范围怎么办? @pyd 那么您可以使用答案中提到的选项Boolean Indexing
、pd.Series.index
。 filter
和 list comprehension
— 基本上不是 numpy 的
@Dahn 我不明白你的回答。你能进一步解释一下吗?
@MattS 如果该系列的索引 other than 范围索引,那么rafaelc
的答案中列出的任何基于 numpy 的方法都将不起作用,因为numpy 将在转换时忘记索引。因此,我列出了在这种情况下仍然有效的方法。这对你有用吗?
最后一个使用列表推导的版本在 if 子句中只有一个小错字:if s[i] 而不是 s[I]【参考方案2】:
作为rafaelc's answer 的补充,以下是以下设置的相应时间(从最快到最慢)
import numpy as np
import pandas as pd
s = pd.Series([x > 0.5 for x in np.random.random(size=1000)])
使用np.where
>>> timeit np.where(s)[0]
12.7 µs ± 77.4 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
使用np.flatnonzero
>>> timeit np.flatnonzero(s)
18 µs ± 508 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
使用pd.Series.index
布尔索引的时间差确实让我感到惊讶,因为布尔索引通常更常用。
>>> timeit s.index[s]
82.2 µs ± 38.9 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
使用Boolean Indexing
>>> timeit s[s].index
1.75 ms ± 2.16 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
如果您需要np.array
对象,请获取.values
>>> timeit s[s].index.values
1.76 ms ± 3.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
如果您需要更容易阅读的版本
>>> timeit s[s==True].index
1.89 ms ± 3.52 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
使用pd.Series.where
>>> timeit s.where(s).dropna().index
2.22 ms ± 3.32 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
>>> timeit s.where(s == True).dropna().index
2.37 ms ± 2.19 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
使用pd.Series.mask
>>> timeit s.mask(s).dropna().index
2.29 ms ± 1.43 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
>>> timeit s.mask(s == True).dropna().index
2.44 ms ± 5.82 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
使用list comprehension
>>> timeit [i for i in s.index if s[i]]
13.7 ms ± 40.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
使用python内置的filter
>>> timeit [*filter(s.get, s.index)]
14.2 ms ± 28.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
使用np.nonzero
>>> timeit np.nonzero(s)
ValueError: Length of passed values is 1, index implies 1000.
使用np.argwhere
>>> timeit np.argwhere(s).ravel()
ValueError: Length of passed values is 1, index implies 1000.
>>> timeit s.where(s).dropna().index
2.22 ms ± 3.32 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
>>> timeit s.where(s == True).dropna().index
2.37 ms ± 2.19 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
pd.Series.mask
>>> timeit s.mask(s).dropna().index
2.29 ms ± 1.43 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
>>> timeit s.mask(s == True).dropna().index
2.44 ms ± 5.82 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
使用list comprehension
>>> timeit [i for i in s.index if s[i]]
13.7 ms ± 40.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
使用python内置的filter
>>> timeit [*filter(s.get, s.index)]
14.2 ms ± 28.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
使用np.nonzero
>>> timeit np.nonzero(s)
ValueError: Length of passed values is 1, index implies 1000.
使用np.argwhere
>>> timeit np.argwhere(s).ravel()
ValueError: Length of passed values is 1, index implies 1000.
>>> timeit np.nonzero(s)
ValueError: Length of passed values is 1, index implies 1000.
np.argwhere
>>> timeit np.argwhere(s).ravel()
ValueError: Length of passed values is 1, index implies 1000.
【讨论】:
【参考方案3】:也可以:
s.where(lambda x: x).dropna().index
,和
它的优点是易于链接管道 - 如果您的系列是动态计算的,则无需将其分配给变量。
请注意,如果 s
是从 r
计算得出的:s = cond(r)
你也可以使用:r.where(lambda x: cond(x)).dropna().index
。
【讨论】:
以上是关于获取 pandas 布尔系列为 True 的索引列表的主要内容,如果未能解决你的问题,请参考以下文章
pandas.DataFrame设置某一行为表头(列索引),设置某一列为行索引,按索引取多行多列