首页 > 解决方案 > 如何(有效地、惯用地)根据条件从熊猫系列中获取元素

问题描述

假设我有一个pandas Series对象,并且我想获取其对应值符合某些条件的所有元素(即索引)。

有很多可能的方法可以做到这一点,但我希望有一种简单、有效、惯用的方法——我还没有找到。

这个问题描述了如何使用布尔索引来做到这一点,但这对于一个简单的命令来说似乎过于冗长 - 例如:

import pandas as pd

age = pd.Series(index=['mom','dad','cat1','cat2','baby'],
                data=[30,30,3,3,1])

age[age>10].index.values

[编辑:请注意变量名称age在前一行中出现了两次。当然age[age>10]很短,但这只是因为age它是一个短名称 - 如果我遇到带有长名称的系列,例如age_of_family_members_after_filtering,那么age_of_family_members_after_filtering[age_of_family_members_after_filtering>10]看起来就不那么好了。

我发现的其他解决方案同样冗长:

age.where(lambda x: x>10).dropna().index.values

或者:

[name for name, _age in age.items() if _age>10]

(最后一个返回一个列表,而前一个返回数组,但我都可以)

由于这是一个非常常见的命令,我期待类似age.filter_where(lambda x: x>10)或类似的东西,我很惊讶没有找到。

我错过了什么(如果有的话)?提前致谢。

标签: pythonpandas

解决方案


对于给定的解决方案,您可以使用 jupyter timeit魔术命令进行一些评估来简单地测试它:

# %%
%timeit age[age>10].index.values
--> 235 µs ± 8.68 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)


# %%
%timeit age.where(lambda x: x>10).dropna().index.values
--> 510 µs ± 14.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

# %%
%timeit [name for name, _age in age.items() if _age>10]
--> 12.5 µs ± 429 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

对于给定的解决方案,最后一个是最快的,但第一个是最简单且仍然完全有效的解决方案。

另一个,注意效率差异:

age.index[age.values > 10].tolist()
--> 16.5 µs ± 823 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

age.index[age > 10].tolist()
--> 157 µs ± 12.1 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)


更新@Alexander 的想法:

# %% 
from itertools import compress
%timeit list(compress(age.index, age > 10))
--> 119 µs ± 3.24 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

推荐阅读