【问题标题】:Using python generators with lots of data使用包含大量数据的 python 生成器
【发布时间】:2021-11-13 02:27:31
【问题描述】:

我有一个包含 250k 个项目的数据集,这些项目在添加到列表/生成器之前需要满足某些条件。为了加快处理速度,我想使用生成器,但我不确定是否使用产生过滤样本的函数过滤数据,或者我是否应该将过滤后的样本返回到生成器对象等。我想最终对象仅包含满足过滤条件的样本,但默认情况下 python 将返回/产生一个 NoneType 对象。我已经包含了示例过滤器函数、数据(真正的问题使用字符串,但为简单起见,我使用随机正态分布的浮点数),以及我打算如何处理下面的数据。

在这种情况下我应该如何有效地使用生成器?为此目的使用生成器是否合乎逻辑/有效?我知道我可以检查返回函数中的元素是否为 None 以将其从容器(列表/生成器)中排除,但是如何使用产生值的函数来执行此操作?

# For random data
import numpy as np

# Functions
def filter_and_yield(item_in_data):
    if item_in_data > 0.0:
        yield item_in_data

def filter_and_return(item_in_data):
    if item_in_data > 0.0:
        return item_in_data

# Arbitrary data
num_samples = 250 * 10**3
data = np.random.normal(size=(num_samples,))

# Should I use this: generator with generator elements?
filtered_data_as_gen_with_gen_elements = (filter_and_yield(item) for item in data)

# Should I use this: list with generator elements?
filtered_data_as_lst_with_gen_elements = [filter_and_yield(item) for item in data]

# Should I use this: generator with non-generator elements?
filtered_data_as_gen_with_non_gen_elements = (
    filter_and_return(item) for item in data if filter_and_return(item) is not None)

# Should I use this: list with non-generator elements?
filtered_data_as_lst_with_non_gen_elements = [
    filter_and_return(item) for item in data if filter_and_return(item) is not None]

# Saving the data as csv -- note, `filtered_data` is NOT defined 
# but is a place holder for whatever correct way of filtering the data is
df = pd.DataFrame({'filtered_data': filtered_data})
df.to_csv('./filtered_data.csv')

【问题讨论】:

  • 这取决于过滤是什么。最好自己处理 in numpy (即调用数组上的方法),而不是对每个元素进行 python 迭代。在您的示例中,每个值都需要从有效的硬件级整数转换为 python int,然后是一个 exprensive 函数调用。对于 numpy 和 pandas,您希望尽可能多地使用它们的 ufunc。
  • tdelaney:我不知道这一点,将进一步调查。谢谢。
  • 你可以改用df = pd.DataFrame({'filtered_data': data[data > 0.0]})。它为data 创建一个 true.false 掩码,然后过滤掉 false。

标签: python pandas performance generator


【解决方案1】:

简短的回答是,这些都不是最好的。 Numpy 和 pandas 包含大量 C 和 Fortan 代码,这些代码适用于存储在连续数组中的硬件级数据类型。 Python 对象,即使是像intfloat 这样的低级对象也相对庞大。它们包括标准的 python 对象头并​​在堆上分配。甚至像> 这样的简单操作也需要调用其中一个方法。

最好尽量使用 numpy/pandas 函数和操作符。这些包重载了标准的 python 操作符,可以一次调用处理整组数据。

df = pd.DataFrame({'filtered_data': data[data > 0.0]})

在这里,data > 0.0 为比较创建了一个新的真/假 numpy 数组。 data[...] 创建了一个新数组,其中仅包含 data 的值,这些值也是正确的。

其他说明

filter_and_yield 是一个生成器,它将迭代 0 或 1 个值。 Python 把它变成了一个生成器,因为它有一个yield。当它返回None 时,python 将其变成StopIteration 异常。这个生成器的消费者不会看到None

(filter_and_yield(item) for item in data) 是一个返回生成器的生成器。如果你使用它,你最终会得到生成器的数据框列。

[filter_and_yield(item) for item in data] 是生成器列表(因为 filter_and_yield 是生成器)。当 pandas 创建列时,它需要知道列的大小。因此,它将生成器扩展为您在此处所做的列表。你可以为熊猫做这个,没关系。除了 pandas 会在完成后删除该列表,这会减少内存使用量。

(filter_and_return(item) for item in data if filter_and_return(item) is not None) 这个可行,但速度很慢。 data 拥有一个硬件级别的整数数组。 for item in data 必须将这些整数中的每一个转换为 python 级别的整数,而 nfilter_and_return(item) 是一个相对昂贵的函数调用。这可以重写为(value for value in (filter_and_return(item) for item in data) if value is not None) 以将函数调用次数减半。

[filter_and_return(item) for item in data if filter_and_return(item) is not None] 如上所述。这样做是可以的,但为了节省内存,请在完成后删除。

【讨论】:

  • tdelaney:我问的问题是我真正问题的简化版本。但是,您在这里传递的信息对我非常有用!我真正的问题涉及首先将 rdkit.Chem.rdchem.Mol> (RDKit 库中特定的化学信息学)对象转换为字符串,然后检查该字符串以查看它是否符合某些标准。我怀疑在进一步查看 RDKit 库后,我可能能够找到一些 numpy 使用的类似语法(例如,data[data [data LOGICAL OP condition for data])。您的见解加深了我对 python 的理解,谢谢。
猜你喜欢
  • 1970-01-01
  • 2017-07-07
  • 1970-01-01
  • 1970-01-01
  • 2012-07-15
  • 1970-01-01
  • 2012-11-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多