【问题标题】:Vectorizing function issue in PythonPython中的矢量化函数问题
【发布时间】:2016-02-01 05:38:30
【问题描述】:

我正在构建一个从文件导入数据的新函数。我正在构建自己的,因此它可以使用与 loadtxt() 相同的通用函数调用并处理数据列的标题。问题在于数据文件的大小,我使用的最后一个文件是 1.3gigs。为了尽量减少内存的使用,我计划将文件加载到一个变量中,将其分解为一个数组“f”,然后一次处理 50,000 行。这样,我可以将这 50,000 条已处理的行放入一个数组中,然后使用原始文件将它们从变量中删除。 (处理然后一次删除一行需要太长时间,因此想到做50,000。)

对于我正在使用的处理:

import numpy as np

def processing(arr, delimiter, dtype):
    return map(dtype, arr.split(delimiter))

df = open(file, 'r')
f = df.readlines()
df.close()

fn = vectorize(processing, otypes=[float])

fn 在我不向其传递数组的条件下工作。考虑:

a = ['1,2,3', '4,5,6', '7,8,9']

这个:

fn(a, ',', int)

返回,

"ValueError: 用序列设置数组元素。"

我的功能的其余部分有效。没有这个工作的变体,虽然对于大文件来说真的很慢。我有一个简短的一次性脚本,可以在 4 分钟内加载文件,这就是目标(loadtxt() 用尽了 ~16gigs 的内存并让我的机器崩溃了)。我想尝试这个矢量化的想法,但如果有更好的方法来分解数据,同时最大限度地减少内存使用,我愿意接受。

【问题讨论】:

  • 您是否考虑过自己打开文件,只读取其中的一部分,将该部分放入 StringIO 对象,然后将 StringIO 对象传递给loadtxt()numpy.loadtxt 文档中有一个示例。

标签: python arrays numpy vectorization


【解决方案1】:

vectorize 不能替代迭代。这是为您提供numpy 广播的全部功能的一种方式。生成的函数接受一个或多个数组,将它们一起广播,然后将一个简单的值元组(即标量,每个数组中的一个)提供给包装函数。

在您的代码中,f 是行列表 - 文件中的所有行。

你可以这样做:

N = len(f)
for i in range(0,N,1000):
    a = np.loadtxt(f[i:i+1000], delimiter=',')
    <process array a>

换句话说,将f 的行以块的形式提供给loadtxt

实际上,您不需要一次阅读所有行。您可以编写一个生成器,逐行读取文件并返回行块。

之前已经讨论过使用生成器来馈送loadtxt(或genfromtxt)。


矢量化的工作示例

In [121]: def processing(astr):
    return list(map(int, astr.split(',')))[0] # py3
   .....: 
In [122]: processing(a[0])
Out[122]: 1
In [123]: fn=np.vectorize(processing, otypes=[int])
In [124]: fn(a)
Out[124]: array([1, 4, 7])

这个函数接受一个字符串并返回一个int。还不如

In [125]: [processing(l) for l in a]
Out[125]: [1, 4, 7]

我从参数中删除了delimiterdtype,因为我们不想遍历这些参数。 vectorize 有一个exclude 参数;但我不想玩那个。

vectorize 也为otypes 取多个值,但我还没有看到这种用法的示例。您的函数不起作用,因为它返回了一个序列(例如 3 个整数),但 vectorize 期望它返回一个值(一个标量浮点数或整数)。


如果您将 otypes 指定为对象,则您的 processing 确实有效 - 有点

In [126]: def processing(astr):
    return list(map(int, astr.split(','))) # py3
   .....: 
In [127]: fn=np.vectorize(processing, otypes=[object])
In [128]: fn(a)
Out[128]: array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=object)

但为什么不只是迭代呢?

In [129]: [processing(l) for l in a]
Out[129]: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
In [130]: np.array([processing(l) for l in a])
Out[130]: 
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])

我隐约记得一些关于使用vectorizeobject 返回时出现错误的问题。

当我在滚动时,我不妨说明一下广播:

这是你的函数,它接受 2 个值 - 一个字符串和一个函数,并将函数应用于拆分的每个元素:

In [131]: def processing(astr, conv):
    return list(map(conv, astr.split(','))) # py3
   .....: 
In [132]: fn=np.vectorize(processing, otypes=[object])

现在vectorized 函数需要 2 个输入,例如列表和函数:

In [133]: fn(a,int)
Out[133]: array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=object)

a(2 个列表)中每个字符串的不同函数

In [134]: fn(a,[int,float,str])
Out[134]: array([[1, 2, 3], [4.0, 5.0, 6.0], ['7', '8', '9']], dtype=object)

或者将第二个列表设为“列”列表 - 并返回一个 (2,3) 列表数组。一行是整数,另一行是浮点数。显然我可以用数组(0、1d、2d 等)替换列表。

In [136]: fn(a,[[int],[float]])
Out[136]: 
array([[[1, 2, 3], [4, 5, 6], [7, 8, 9]],
       [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]], dtype=object)

如果您需要这种输入灵活性,请使用vectorize。但是,如果您只是迭代一个数组或列表 - 直接执行。


我找到了多个otypes的例子:https://stackoverflow.com/a/30255971/901925

应用于本案:

In [140]: def processing(astr):
    return tuple(map(int, astr.split(','))) # py3
   .....:

重要的是它返回一个元组,而不是一个列表或数组。

In [141]: processing(a[0])
Out[141]: (1, 2, 3)
In [142]: fn=np.vectorize(processing, otypes=[int,int,int])

请注意,返回元组的每个项目都必须有一个 otype

In [144]: fn(a)
Out[144]: (array([1, 4, 7]), array([2, 5, 8]), array([3, 6, 9]))

[1, 4, 7] 是 3 个输入中每个输入的第一个值。它返回的是一组数组,而不是一个数组。

In [146]: x,y,z=fn(a)
In [147]: x
Out[147]: array([1, 4, 7])

这种行为困扰了另一个提问者,我怀疑这是否也是你想要的。 :)

https://stackoverflow.com/a/30088791/901925 - 带有时间测试的矢量化示例。

【讨论】:

  • 我仍在努力解决这个问题,但你问为什么不只是迭代。我希望 vectorize 允许我为减少开销的许多实例调用一次相同的函数,并且它可能允许处理器同时处理多个进程以减少时间。我的印象是它一次只运行一个。
  • np.vectorize 不是多处理包装器。它是一个迭代包装器。在其他情况下,它比用户实现的迭代节省了大约 20% 的时间。但是如果任务是一次处理 50,000 行,那么与处理时间相比,迭代开销会很小。
猜你喜欢
  • 1970-01-01
  • 2022-01-14
  • 2015-04-11
  • 2020-04-06
  • 1970-01-01
  • 2014-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多