2017-07-25 80 views
0

我有一个接受列表(字符串)的函数。它在该列表上进行一些处理并返回另一个字符串列表,可能长度较短。将函数应用于列表的numpy数组中的每个列表

现在,我有一个numpy的字符串输入列表数组。我想将这个转换函数应用到我的数组中的每个列表。

从我迄今为止进行的搜索中,看起来好像vectorizeapply_along_axis可能是很好的候选人,但都没有像预期的那样工作。

我想尽可能有效地做到这一点。最终输入数组将包含大约100K个列表。

我想我可以遍历for循环中的numpy数组,然后将append每个输出列表一次一个迭代到一个新的输出数组中,但这看起来非常低效。

这是我试过的。出于测试目的,我做了一个虚拟转换函数,输入数组只包含3个列表。

def my_func(l): 
    # accepts list, returns another list 
    # dumbed down list transformation function 
    # for testing, just return the first 2 elems of original list 
    return l[0:2] 

test_arr = np.array([['the', 'quick', 'brown', 'fox'], ['lorem', 'ipsum'], ['this', 'is', 'a', 'test']]) 

np.apply_along_axis(my_func, 0, test_arr) 
Out[51]: array([['the', 'quick', 'brown', 'fox'], ['lorem', 'ipsum']], dtype=object) 

# Rather than applying item by item, this returns the first 2 elements of the entire outer array!! 

# Expected: 
# array([['the', 'quick'], ['lorem', 'ipsum'], ['this', 'is']]) 

# Attempt 2... 

my_func_vec = np.vectorize(my_func) 
my_func_vec(test_arr) 

结果:

Traceback (most recent call last): 

    File "<ipython-input-56-f9bbacee645c>", line 1, in <module> 
    my_func_vec(test_arr) 

    File "C:\Users\Tony\Anaconda2\lib\site-packages\numpy\lib\function_base.py", line 2218, in __call__ 
    return self._vectorize_call(func=func, args=vargs) 

    File "C:\Users\Tony\Anaconda2\lib\site-packages\numpy\lib\function_base.py", line 2291, in _vectorize_call 
    copy=False, subok=True, dtype=otypes[0]) 

ValueError: cannot set an array element with a sequence 
+1

这已被讨论过很多次。无论是矢量化还是应用...'会提高'效率'。他们仍然需要在每个列表上调用您的函数,并将结果累加到数组或列表中。总的来说,它运行你的函数100k次将会很慢,而不是迭代框架。 – hpaulj

+1

在过去的测试中,我发现'np.frompyfunc'是迭代对象数组的好工具。 – hpaulj

+1

其实我想的完全一样,但决定回答这个问题,而不是告诉我该怎么做。但后来我测试了它,出于某种原因,矢量化比列表理解或map()函数快10倍...任何想法为什么? – dnalow

回答

3

vectorize文档字符串中读取有关可选参数otypes

otypes : str or list of dtypes, optional 
    The output data type. It must be specified as either a string of 
    typecode characters or a list of data type specifiers. There should 
    be one data type specifier for each output. 

它允许你创建具有复杂的输出结构的阵列,也解决了你您将列表作为数组元素的问题。

my_func_vec = np.vectorize(my_func, otypes=[list])

+1

numpy旨在有效处理数值数组。我会仔细检查你正在做的矢量化是否会给你带来任何性能增益,而这与@Wli给出的列表理解相比 –

+0

@IgnacioVergaraKausel人们更喜欢复杂的答案:) – Wli

1
[my_func(x) for x in test_arr] 

你需要去下一层,你的解决方案只输出你的阵列的2个第一项,而不是你的阵列的每个项目的第2项。

1

一些比较和时间测试;但请记住这只是一个小例子。

In [106]: test_arr = np.array([['the', 'quick', 'brown', 'fox'], ['lorem', 'ipsum'], ['this', 'is', 'a', 'test']]) 
    ...: 
In [107]: def my_func(l): 
    ...:  # accepts list, returns another list 
    ...:  # dumbed down list transformation function 
    ...:  # for testing, just return the first 2 elems of original list 
    ...:  return l[0:2] 
    ...: 

list comprehension方法返回一个2d字符串数组 - 因为函数每次返回2个元素列表。

In [108]: np.array([my_func(x) for x in test_arr]) 
Out[108]: 
array([['the', 'quick'], 
     ['lorem', 'ipsum'], 
     ['this', 'is']], 
     dtype='<U5') 

输入数组是对象D型细胞因子列表的长度不同:

In [109]: test_arr 
Out[109]: 
array([list(['the', 'quick', 'brown', 'fox']), list(['lorem', 'ipsum']), 
     list(['this', 'is', 'a', 'test'])], dtype=object) 

frompyfunc返回一个对象D型阵列;与我过去的测试一致是适度较快(2x,但从来没有一个数量级)

In [110]: np.frompyfunc(my_func,1,1)(test_arr) 
Out[110]: 
array([list(['the', 'quick']), list(['lorem', 'ipsum']), 
     list(['this', 'is'])], dtype=object) 

In [111]: timeit np.frompyfunc(my_func,1,1)(test_arr) 
5.68 µs ± 230 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) 
In [112]: timeit np.array([my_func(x) for x in test_arr]) 
8.96 µs ± 25.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) 

vectorize使用frompyfunc,但有更多的开销。所述otypes是需要避免的sequence误差(否则它试图从试算推断返回类型):

In [113]: np.vectorize(my_func,otypes=[object])(test_arr) 
Out[113]: 
array([list(['the', 'quick']), list(['lorem', 'ipsum']), 
     list(['this', 'is'])], dtype=object) 
In [114]: timeit np.vectorize(my_func,otypes=[object])(test_arr) 
30.4 µs ± 132 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each) 
+1

不错,虽然我会说纯python解决方案doesn没有意义的做铸造成一个numpy数组或输入为numpy数组。 –

+1

正如我所说的,我以相同的方式测试了它,并且使用向量化与列表理解相比,速度提高了10倍。但我用了一个“很长”的test_arr ...像100k – dnalow

相关问题