当前位置: 首页 > 知识库问答 >
问题:

对数据帧或数据系列应用多个筛选器的有效方法

祁远
2023-03-14

我有一个场景,用户想要将几个过滤器应用到Pandas DataFrame或Series对象。本质上,我希望有效地将用户在运行时指定的一堆过滤(比较操作)链接在一起。

过滤器应为添加剂(即,应用的每个过滤器应缩小结果范围)。

我目前正在使用reindex(),但这每次都会创建一个新对象并复制基础数据(如果我正确理解留档)。因此,在过滤大型系列或数据帧时,这可能非常低效。

我认为使用apply()map()或类似的方法可能会更好。我对熊猫还不太熟悉,所以我还是试着去了解一切。

我想使用以下形式的字典,并将每个操作应用于给定的Series对象,并返回一个筛选的Series对象。

relops = {'>=': [1], '<=': [1]}

我将从一个我目前拥有的示例开始,只过滤一个序列对象。以下是我当前使用的函数:

   def apply_relops(series, relops):
        """
        Pass dictionary of relational operators to perform on given series object
        """
        for op, vals in relops.iteritems():
            op_func = ops[op]
            for val in vals:
                filtered = op_func(series, val)
                series = series.reindex(series[filtered])
        return series

用户提供一个字典,其中包含要执行的操作:

>>> df = pandas.DataFrame({'col1': [0, 1, 2], 'col2': [10, 11, 12]})
>>> print df
>>> print df
   col1  col2
0     0    10
1     1    11
2     2    12

>>> from operator import le, ge
>>> ops ={'>=': ge, '<=': le}
>>> apply_relops(df['col1'], {'>=': [1]})
col1
1       1
2       2
Name: col1
>>> apply_relops(df['col1'], relops = {'>=': [1], '<=': [1]})
col1
1       1
Name: col1

同样,我上述方法的“问题”是,我认为中间步骤可能存在大量不必要的数据复制。

此外,我还想对此进行扩展,以便传入的字典可以包括要运算符打开的列,并基于输入字典过滤整个数据帧。然而,我假设任何适用于该系列的东西都可以轻松地扩展为数据帧。

共有3个答案

毛景曜
2023-03-14

最简单的解决方案:

使用:

filtered_df = df[(df['col1'] >= 1) & (df['col1'] <= 5)]

另一个例子是,要过滤数据帧中属于2018年2月的值,请使用以下代码

filtered_df = df[(df['year'] == 2018) & (df['month'] == 2)]
凌景辉
2023-03-14

连锁条件产生长线,这是由Pep8劝阻。使用。查询方法强制使用字符串,这是强大的,但不是Pythonic,也不是非常动态的。

一旦每个过滤器都到位,一种方法是

import numpy as np
import functools
def conjunction(*conditions):
    return functools.reduce(np.logical_and, conditions)

c_1 = data.col1 == True
c_2 = data.col2 < 64
c_3 = data.col3 != 4

data_filtered = data[conjunction(c1,c2,c3)]

np.logical在上运行且速度快,但不接受两个以上的参数,这由functools.reduce处理。

请注意,这仍然有一些冗余: a)快捷方式不会在全局级别上发生b)每个单独的条件都在整个初始数据上运行。尽管如此,我希望这对于许多应用程序来说足够高效,并且非常可读。

您还可以使用np.logical\u或进行析取(其中只有一个条件需要为真):

import numpy as np
import functools
def disjunction(*conditions):
    return functools.reduce(np.logical_or, conditions)

c_1 = data.col1 == True
c_2 = data.col2 < 64
c_3 = data.col3 != 4

data_filtered = data[disjunction(c_1,c_2,c_3)]
羊舌自强
2023-03-14

熊猫(和numpy)允许布尔索引,这将更加有效:

In [11]: df.loc[df['col1'] >= 1, 'col1']
Out[11]: 
1    1
2    2
Name: col1

In [12]: df[df['col1'] >= 1]
Out[12]: 
   col1  col2
1     1    11
2     2    12

In [13]: df[(df['col1'] >= 1) & (df['col1'] <=1 )]
Out[13]: 
   col1  col2
1     1    11

如果您想为此编写帮助函数,请考虑以下内容:

In [14]: def b(x, col, op, n): 
             return op(x[col],n)

In [15]: def f(x, *b):
             return x[(np.logical_and(*b))]

In [16]: b1 = b(df, 'col1', ge, 1)

In [17]: b2 = b(df, 'col1', le, 1)

In [18]: f(df, b1, b2)
Out[18]: 
   col1  col2
1     1    11

更新:pandas 0.13为此类用例提供了一种查询方法,假设列名是有效的标识符,下面的工作(并且对于大帧来说可能更有效,因为它在幕后使用numexpr):

In [21]: df.query('col1 <= 1 & 1 <= col1')
Out[21]:
   col1  col2
1     1    11
 类似资料:
  • 若要用单个列过滤数据文件(DF),如果我们考虑有男性和女性的数据,我们可以: 问题1——但如果数据跨越多年,而我只想看到2014年的男性会怎样? 在其他语言中,我可能会这样做: (除非我想这样做并在新的dataframe对象中获取原始dataframe的子集) 问题2。我如何在一个循环中实现这一点,并为每个独特的年份和性别集(即:2013年男性、2013年女性、2014年男性和2014年女性)创建

  • 我正在尝试筛选将< code>None作为行值的PySpark数据帧: 我可以使用字符串值正确过滤: 但这失败了: 但是每一类都有明确的价值。这是怎么回事?

  • 我有两个数据帧。我需要用第二列中的平均值更新第一列中的一列,并按索引分组。这里是示例df1(col1是索引) df2(col1是索引) 我需要df2的col2(a=2,d=3)的平均值,并且只更新col3=X的行的df1 我试过这个 只有在我不使用loc的情况下,它才有效。 我试图得到的结果是df1(col1是索引)

  • 这一组数据在进行groupby前已经完成筛选,但进行groupby聚合后的结果显示是利用未筛选的数据进行的聚合,就像下面的结果,在groupby前已经完成点击量非0过滤,但最后仍存在含0的资源,询问chatGPT给的方案是可能用索引前的数据进行的聚合,重置索引后仍无法解决,请教大牛是否遇到过类似的问题,虽然可以在聚合后重新进行filter过滤,但这个问题搞得很焦灼 代码源文本

  • 我有一个关于熊猫以及正确索引和替换值的问题。 我有两个数据帧,df1和df2,具有相同的列(Col1、Col2、Col3和Col4)。 在df1中,我想用另一个值(比如100)替换与df2中其他列(Col1、Col2和Col3)的值匹配的行中Col4中的值。 生成的df1看起来像这样: 我试过这样的方法: 但是我得到了错误,我不确定这是否达到了我想要的。

  • 筛选数据 你可以通过 3 种方式筛选数据: 点击字段框中的向下箭头,然后选择“筛选”。 使用筛选窗格。 右击图表上的系列或数据点。 【提示】如果要清除筛选,则需要使用筛选窗格。 排序数据 你可以通过 2 种方式排序数据: 点击字段框中的向下箭头,然后选择“排序”。 使用排序窗格。