【问题标题】:How to evaluate and add string to numpy array element如何评估字符串并将其添加到numpy数组元素
【发布时间】:2017-01-08 10:37:49
【问题描述】:

拥有我正在尝试优化的这段代码。 它使用列表推导和工作。

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']
  • 我需要空值。

标签: python string performance numpy


【解决方案1】:

我们可以使用基于向量化的方法

  • np.core.defchararray.add 用于将't:' 附加到有效字符串中,并且

  • np.where 根据条件语句选择并执行追加或仅使用空字符串的默认值。

所以,我们会有这样的实现 -

np.where(series1>series2,np.core.defchararray.add('t:',series2.astype(str)),'')

加油!

我们可以根据series1>series2 的掩码在有效元素上使用np.core.defchararray.add 的附加功能,以在使用默认空字符串初始化数组然后只为其分配有效值后进一步提高性能。

所以,修改后的版本看起来像这样 -

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 in comments 所指出的,我们可能可以在追加之前使用series2 的dtype。所以,我在那里使用U32 来保持输出dtype 与str dtype 相同,即series2.astype('U32')np.core.defchararray.add 调用中。矢量化方法的新时机是 -

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

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

【讨论】:

    【解决方案2】:

    您的列表推导适用于列表,实际上不需要使用数组。对于像这样的操作,数组可能不会带来任何速度优势。

    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 不是将数组作为输入处理吗?
    • 代码示例不断使用。数组大小为 10,000-5,000,可以是有条件的几个数组。在基准测试方面,值得重新编写一些内容。
    • np.core.defchararray.add('t:',series2.astype(str)) 比 np.char.add('t:',aseries2.astype('U3') 快) -- 它的 unicode 转换。
    • 我使用'U3',因为我在 Py3 上;那里str 做同样的事情。在 Py2 会话中,strS3 应该相同。 U3 将创建更长的数组。
    • @Merlin 在我的帖子中加入了这个想法。
    【解决方案3】:

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

    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 
    

    【讨论】:

    • 可以添加时间吗?
    猜你喜欢
    • 1970-01-01
    • 2023-03-03
    • 2019-04-08
    • 1970-01-01
    • 2012-10-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多