【问题标题】:Chaining filters for a Series系列的链接过滤器
【发布时间】:2016-08-17 18:58:10
【问题描述】:

方便chain filters on a DataFrame using query

# quoting from the SO answer above
df = pd.DataFrame( np.random.randn(30,3), columns = ['a','b','c'])
df_filtered = df.query('a>0').query('0<b<2')

如果我需要对Series 做同样的事情怎么办:

df = pd.DataFrame({'a': [0, 0, 1, 1, 2, 2], 'b': [1, 2, 3, 4, 5, 6]})
df.groupby('a').b.sum().query('? > 3').query('? % 3 == 1')

Series.query 不存在(有充分的理由,大多数查询语法是允许访问多个列)。

【问题讨论】:

    标签: python python-3.x pandas dataframe


    【解决方案1】:

    你可以使用to_frame()方法:

    In [10]: df.groupby('a').b.sum().to_frame('v').query('v > 3').query('v % 3 == 1')
    Out[10]:
       v
    a
    1  7
    

    如果您需要将结果作为系列:

    In [12]: df.groupby('a').b.sum().to_frame('v').query('v > 3').query('v % 3 == 1').v
    Out[12]:
    a
    1    7
    Name: v, dtype: int64
    

    to_frame() 是否涉及复制系列?

    涉及到DataFrame构造函数的调用:

    https://github.com/pydata/pandas/blob/master/pandas/core/series.py#L1140:

    df = self._constructor_expanddim({name: self})
    

    https://github.com/pydata/pandas/blob/master/pandas/core/series.py#L265:

    def _constructor_expanddim(self):
        from pandas.core.frame import DataFrame
        return DataFrame
    

    性能影响(针对 600K 行 DF 进行测试):

    In [66]: %timeit df.groupby('a').b.sum()
    10 loops, best of 3: 46.2 ms per loop
    
    In [67]: %timeit df.groupby('a').b.sum().to_frame('v')
    10 loops, best of 3: 49.7 ms per loop
    
    In [68]: 49.7 / 46.2
    Out[68]: 1.0757575757575757
    

    性能影响(针对 6M 行 DF 进行测试):

    In [69]: df = pd.concat([df] * 10, ignore_index=True)
    
    In [70]: df.shape
    Out[70]: (6000000, 2)
    
    In [71]: %timeit df.groupby('a').b.sum()
    1 loop, best of 3: 474 ms per loop
    
    In [72]: %timeit df.groupby('a').b.sum().to_frame('v')
    1 loop, best of 3: 464 ms per loop
    

    性能影响(针对 60M 行 DF 进行测试):

    In [73]: df = pd.concat([df] * 10, ignore_index=True)
    
    In [74]: df.shape
    Out[74]: (60000000, 2)
    
    In [75]: %timeit df.groupby('a').b.sum()
    1 loop, best of 3: 4.28 s per loop
    
    In [76]: %timeit df.groupby('a').b.sum().to_frame('v')
    1 loop, best of 3: 4.3 s per loop
    
    In [77]: 4.3 / 4.28
    Out[77]: 1.0046728971962615
    

    结论:性能影响似乎没有那么大...

    【讨论】:

    • 我需要在使用系列特定操作之后立即将其转换回系列,但我想这种往返转换是不可避免的。
    • @max,是的,这很容易 - 我已将其添加到我的答案中
    • 是的,但我更想知道性能影响,而不是额外代码。 to_frame() 是否涉及复制系列?
    • @max,我已经为不同大小的 DF 添加了计时 - 请检查
    • 谢谢。另外,我刚刚意识到它不会复制系列(我们可以通过更改其中的项目来验证它)。但即使它确实如此,它实际上也有点快......(1亿个整数0.25秒,1亿个字符串0.7秒)。
    猜你喜欢
    • 2017-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-28
    • 1970-01-01
    • 1970-01-01
    • 2015-10-22
    相关资源
    最近更新 更多