如何评估并向numpy数组元素添加字符串

4

我有一段代码需要优化,使用了列表推导式并且能够正常工作。

series1 = np.asarray(range(10)).astype(float)
series2 = series1[::-1]

ntup = zip(series1,series2)
[['', 't:'+str(series2)][series1 > series2] for series1,series2 in ntup ]
 #['', '', '', '', '', 't:4.0', 't:3.0', 't:2.0', 't:1.0', 't:0.0']

尝试在此处使用 np.where()。是否有使用 numpy 的解决方案(不需要消耗系列)。

series1 = np.asarray(range(10)).astype(float)
series2 = series1[::-1]  

np.where(series1 >  series2 ,'t:'+ str(series2),'' )

这是结果:
array(['', '', '', '', '', 't:[ 9.  8.  7.  6.  5.  4.  3.  2.  1.  0.]',
       't:[ 9.  8.  7.  6.  5.  4.  3.  2.  1.  0.]',
       't:[ 9.  8.  7.  6.  5.  4.  3.  2.  1.  0.]',
       't:[ 9.  8.  7.  6.  5.  4.  3.  2.  1.  0.]',
       't:[ 9.  8.  7.  6.  5.  4.  3.  2.  1.  0.]'], 
      dtype='|S43')

你需要那些空的吗,还是像这样 ['t:4.0', 't:3.0', 't:2.0', 't:1.0', 't:0.0'] 这样就可以了? - Divakar
我需要空值。 - Merlin
3个回答

2
我们可以使用基于向量化的方法,其中:
  • 对于字符串拼接,我们可以使用np.core.defchararray.add函数来将't:'与有效字符串连接起来。

  • 我们可以使用np.where函数根据条件语句进行选择,并执行字符串拼接或者使用空字符串的默认值。

因此,我们可以实现如下代码 -
np.where(series1>series2,np.core.defchararray.add('t:',series2.astype(str)),'')

提升它!
我们可以使用 np.core.defchararray.add 中的附加功能,根据 series1>series2 的掩码,在有效元素上将性能进一步提高,然后在初始化带有默认空字符串的数组后,仅将有效值分配到其中。
因此,修改后的版本将如下所示 -
mask = series1>series2
out = np.full(series1.size,'',dtype='U34')
out[mask] = np.core.defchararray.add('t:',series2[mask].astype(str))

运行时测试
向量化版本作为函数:
def vectorized_app1(series1,series2):
    mask = series1>series2
    return np.where(mask,np.core.defchararray.add('t:',series2.astype(str)),'')

def vectorized_app2(series1,series2):
    mask = series1>series2
    out = np.full(series1.size,'',dtype='U34')
    out[mask] = np.core.defchararray.add('t:',series2[mask].astype(str))
    return out

一个更大的数据集的时间 -
In [283]: # Setup input arrays
     ...: series1 = np.asarray(range(10000)).astype(float)
     ...: series2 = series1[::-1]
     ...: 

In [284]: %timeit [['', 't:'+str(s2)][s1 > s2] for s1,s2 in zip(series1, series2)]
10 loops, best of 3: 32.1 ms per loop # OP/@hpaulj's soln

In [285]: %timeit vectorized_app1(series1,series2)
10 loops, best of 3: 20.5 ms per loop

In [286]: %timeit vectorized_app2(series1,series2)
100 loops, best of 3: 10.4 ms per loop

正如评论中的OP所指出的那样,在附加之前,我们可以尝试使用series2的dtype。因此,在np.core.defchararray.add调用中,我使用了U32,以保持输出dtype与str的dtype相同,即series2.astype('U32')。矢量化方法的新时间为-
In [290]: %timeit vectorized_app1(series1,series2)
10 loops, best of 3: 20.1 ms per loop

In [291]: %timeit vectorized_app2(series1,series2)
100 loops, best of 3: 10.1 ms per loop

所以,那里有一些进一步的边际改善!

1
你的列表理解对于列表来说运行得非常好,不需要使用数组。对于像这样的操作,数组可能不会提供任何速度优势。
In [521]: series1=[float(i) for i in range(10)]
In [522]: series2=series1[::-1]
In [523]: [['', 't:'+str(s2)][s1 > s2] for s1,s2 in zip(series1, series2)]
Out[523]: ['', '', '', '', '', 't:4.0', 't:3.0', 't:2.0', 't:1.0', 't:0.0']

正如 @Divaker 所指出的,有一个 np.char.add 函数可以执行字符串操作。我的经验是它们比列表操作稍微快一些。而且当考虑到创建数组的开销时,它们可能会更慢。

=========

如@Divakar所示的array版本

In [539]: aseries1=np.array(series1)
In [540]: aseries2=np.array(series2)
In [541]: np.where(aseries1>aseries2, np.char.add('t:',aseries2.astype('U3')), '
     ...: ')
Out[541]: 
array(['', '', '', '', '', 't:4.0', 't:3.0', 't:2.0', 't:1.0', 't:0.0'], 
      dtype='<U5')

一些时间测试:

In [542]: timeit [['', 't:'+str(s2)][s1 > s2] for s1,s2 in zip(series1, series2)
     ...: ]
100000 loops, best of 3: 15.5 µs per loop

In [543]: timeit np.where(aseries1>aseries2, np.char.add('t:',aseries2.astype('U3')), '')
10000 loops, best of 3: 63 µs per loop

也许可以在更大的数组上进行基准测试?另外,OP不是正在处理数组作为输入吗? - Divakar
代码示例经常被使用。数组大小为10,000-5,000,可能有一些条件数组。在基准测试中,它值得重新编写一些内容。 - Merlin
np.core.defchararray.add('t:',series2.astype(str)) 比 np.char.add('t:',aseries2.astype('U3')) 更快 -- 这是因为它进行了 Unicode 转换。 - Merlin
1
我使用了'U3',因为我在Py3上;在那里,str做相同的事情。在Py2会话中,strS3应该是相同的。U3将创建更长的数组。 - hpaulj
@Merlin Incorporated 将那个想法融入了我帖子中的时间安排。 - Divakar

1

这对我有效。完全矢量化。

import numpy as np
series1 = np.arange(10)
series2 = series1[::-1]
empties = np.repeat('', series1.shape[0])
ts = np.repeat('t:', series1.shape[0])
s2str = series2.astype(np.str)
m = np.vstack([empties, np.core.defchararray.add(ts, s2str)])
cmp = np.int64(series1 > series2)
idx = np.arange(m.shape[1])
res = m[cmp, idx]
print res 

你可以加上计时功能吗? - Merlin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接