【问题标题】:Write a Boolean expression of arbitrary size from a list or dictionary从列表或字典中编写任意大小的布尔表达式
【发布时间】:2021-11-06 11:54:09
【问题描述】:

我正在处理的 pandas 数据帧对列有一些统一的标签架构,但列的数量是任意的。

例如,下面的 df 包含列 col_names、列的特殊子集以及与这些特殊列对应的过滤条件。它们是通过索引链接的,也可以通过字典链接,任你选择。

col_names = ['col1','col2','col3','xyz.1','xyz.2','xyz.3']
relevant_col_names = ['xyz.1', 'xyz.2', 'xyz.3']
filter_criteria = [something1, something2, something3]

我想获取 'xyz.1' == something1 & 'xyz.2' == something2 & 'xyz.3' ==something3 的子集数据框

我通常会这样做:

whatIwant = df.loc[df['xyz.1']==something1 & df['xyz.2']==something2 & df['xyz.3']==something3]

问题是我需要能够为任意数量的“xyz”列编写该表达式,而无需手动编写上述表达式。

例如,如果 df 恰好有 5 个相关列,如下所示:

['col1','col2','col3','xyz.1','xyz.2','xyz.3','xyz.4','xyz.5']

然后我需要自动写这样的东西:

whatIwant = df.loc[df['xyz.1']==something1 & df['xyz.2']==something2 & df['xyz.3']==something3 & df['xyz.4']==something4 & df['xyz.5']==something5]

有没有办法根据列表或字典(或其他东西)编写任意长度的布尔表达式,我在其中对其中的所有内容进行与运算?

我不知道如何用谷歌来表达这个问题。它似乎与列表和字典理解、表达式合成或其他东西有关。即使知道表达这个问题的正确方法,如何为stackoverflow标记这个问题,和/或我应该谷歌什么,也会很有帮助。谢谢!

【问题讨论】:

  • 主要问题在我看来是“某事*”。这些是单个变量(坏)还是列表中的项目(好)还是其他什么?
  • 您想要应用所有过滤器吗?您是否尝试循环应用过滤器,将每个过滤器应用于上一步的过滤结果?
  • @MichaelButscher 是的,所有“somethingK”都在一个列表中,索引方式与相应的“xyz.K”组件相同
  • @KarlKnechtel 是的,我希望所有过滤器同时应用,因此需要一个长布尔表达式。这是一个聪明的想法,这是有道理的。当我应用每个过滤器时,只需制作一个逐渐变小的数据框。尽管大 O 效率似乎很痛苦。但是很实用。谢谢
  • 因为数据框会逐渐变小,因此您不会遇到大 O 复杂性问题。你实际上是在做同样的短路。

标签: python pandas list-comprehension boolean-expression dictionary-comprehension


【解决方案1】:

假设你有一个列表中的所有“东西”,你可以这样做:

import functools
import operator
import pandas as pd

df = pd.DataFrame.from_dict({
    "abc": (1, 2, 3),
    "xyz.1": (4, 2, 7),
    "xyz.2": (8, 5, 5)
})
targets = [2, 5]

filtered_dfs = (df[f"xyz.{index + 1}"] == target for index, target in enumerate(targets))
filtered_df = df.loc[functools.reduce(operator.and_, filtered_dfs)]

print(filtered_df)

我们通过执行df[f"xyz.{index + 1}"] == targetfiltered_dfs 中的df 上构造每个过滤器。对于通过targets 的第一次迭代,这将是df["xyz.1"] == 2。对于第二次迭代,它将是df["xyz.2"] == 5

然后我们将所有这些过滤器与functools.reduce(operator.and_, filtered_dfs) 结合起来,就像在做filtered_df_1 & filtered_df_2 & ...

我们最终通过df.loc 将过滤器应用于数据帧,这给出了在xyz.1xyz.2 中具有25 的行。输出是:

   abc  xyz.1  xyz.2
1    2      2      5

【讨论】:

  • 优雅!谢谢。我想知道您的答案和 Karl Knetchel 的答案之间的 O 性能差异有多大。我不知道在处理 pandas 操作时如何考虑性能。
  • @IsaacHowenstine Karl 的解决方案和我的解决方案都是 O(nk) 时间复杂度,因为我们在应用过滤器 n 次时遍历原始数据帧的 k 行。这可以是 n 像 Karl 建议的那样一一对应,或者在应用之前将 n 过滤器组合成一个过滤器(我的解决方案)。两种解决方案都是O(k) 空间复杂度,因为只有 2 个(恒定数量)数据帧/系列一次存储在内存中,两者都有k 行。 Karl 会一个接一个地构建过滤器,而我使用的是生成器,然后是 functools.reduce
  • 这里有一本适合您的读物:stackoverflow.com/questions/47789/…。在我的解决方案中使用 filtered_dfs 的生成器表达式会导致它是 O(k) 空间复杂度,而不是 O(nk) 如果我使用了列表理解。因此,即使我在应用之前将所有过滤器合并为一个,但这并不意味着所有过滤器都一次存储在内存中。但是总的来说,除非您已经对程序进行了概要分析并确保它存在,否则我不会担心这里有任何性能差异。只需将上述比较视为一个有趣的练习。
猜你喜欢
  • 2019-01-30
  • 1970-01-01
  • 1970-01-01
  • 2012-03-25
  • 2015-01-21
  • 2017-05-31
  • 1970-01-01
  • 2016-11-21
  • 1970-01-01
相关资源
最近更新 更多