【问题标题】:Is there a fast way to get all neighbor elements in a list? [duplicate]有没有一种快速的方法来获取列表中的所有相邻元素? [复制]
【发布时间】:2021-01-24 03:48:35
【问题描述】:

例如,我有一个list(也可以是numpy.array 或其他任何东西,我只是想知道一种更快的方法,我不在乎数据类型)['a','b','c','d'],我想得到['ab','bc','cd'].

当然有一个简单的解决方案,例如:

letters = list('abcdefghijk')
my_list = [letters[i:i+2] for i in range(len(letters)-1)]

my_list 是:

[['a', 'b'],
 ['b', 'c'],
 ['c', 'd'],
 ['d', 'e'],
 ['e', 'f'],
 ['f', 'g'],
 ['g', 'h'],
 ['h', 'i'],
 ['i', 'j'],
 ['j', 'k']]

但我想知道是否有更快的方法通过numpy 或其他方式来执行此操作,因为我想使用相对较大的数据来执行此操作,因此简单的for 循环可能会很昂贵。

更新 现在让我总结一下下面的答案。非常感谢@Ehsan 和@Andy L 到目前为止的答案。我通过下面的代码对他们所有的解决方案进行了非常简单的测试,数据相对较大:

import time
import numpy as np
from numba import njit
import matplotlib.pyplot as plt
from numpy.lib.stride_tricks import as_strided

def m1(letters):
    return [letters[i]+letters[i+1] for i in range(len(letters)-1)]

@njit
def m2(letters):
    
    return [letters[i]+letters[i+1] for i in range(len(letters)-1)]

def m3(letters):
    return np.char.add(letters[:-1], letters[1:])

def m4(letters):
    n = len(letters) - 1
    m = np.array(letters[0]).itemsize
    arr = as_strided(letters, shape=(n, 2), strides=(m, m))
    return arr

def test_time(testfunc,args):
    start = time.time()
    testfunc(*args)
    return time.time() - start

# test
ns = [10000, 100000, 1000000, 10000000]
timecost = []
for n in ns:
    input_=np.random.choice(list('abcdefghijklmnopqrstuvwxyz'), size=n)
    cost = [test_time(testfunc, [input_]) for testfunc in [m1, m2, m3,m4]]
    timecost.append(cost)

# result plot
timecost = np.array(timecost)
labels = ['normal','numba.njit','numpy.char.add','numpy.lib.stride_tricks.as_strided']
for i in range(timecost.shape[-1]):
    plt.plot(ns, timecost[:, i], label=labels[i])
plt.legend()
plt.savefig('result.png')

结果是:

结果表明,np.char.addnumba 在更大数据上的性能确实优于常规方法,但stride_tricks 的性能更好:时间成本降低了两个数量级以上.

【问题讨论】:

  • 我不在乎数据类型:它们都是字符吗?或者你的意思是和浮动一样吗?
  • @Ehsan 我的错。我的意思是它们都是字符,但我可以接受任何类型的容器,例如 numpy.array 或其他东西。
  • 您想要获得['ab','bc','cd'][['a', 'b'],['b', 'c'], ['c', 'd'],... 吗?这是两种不同的格式。
  • @Divakar [['a', 'b'],['b', 'c'], ['c', 'd'],... 会更好。

标签: python python-3.x numpy


【解决方案1】:

使用stride_tricks库中的as_strided

from numpy.lib.stride_tricks import as_strided

n = len(letters) - 1
m = np.array(letters[0]).itemsize

arr = as_strided(letters, shape=(n, 2), strides=(m, m))

Out[289]:
array([['a', 'b'],
       ['b', 'c'],
       ['c', 'd'],
       ['d', 'e'],
       ['e', 'f'],
       ['f', 'g'],
       ['g', 'h'],
       ['h', 'i'],
       ['i', 'j'],
       ['j', 'k']], dtype='<U1')

【讨论】:

  • 非常感谢!这种方案效果更好,时间成本降低了两个数量级以上。我已经在我的帖子中更新了时间测试结果。
  • @C.K.好答案。请注意,这为您提供了一个视图。这就是为什么它基本上如此之快。如果您需要单独更改步幅,这可能会变得很棘手(例如,'b's 都引用相同的内存)
【解决方案2】:

你可以使用 numpy:

np.char.add(letters[:-1],letters[1:])
#['ab' 'bc' 'cd' 'de' 'ef' 'fg' 'gh' 'hi' 'ij' 'jk']

使用 numba 的另一种方式:

@njit
def m2(letters):
  return [letters[i]+letters[i+1] for i in range(len(letters)-1)]

比较使用benchit

def m1(letters):
  return [letters[i]+letters[i+1] for i in range(len(letters)-1)]

@njit
def m2(letters):
  return [letters[i]+letters[i+1] for i in range(len(letters)-1)]

def m3(letters):
  return np.char.add(letters[:-1],letters[1:])

in_ = [np.random.choice(list(string.ascii_lowercase),size=n) for n in [100,1000,10000,100000]]

它们看起来都一样(注意:不确定benchit 是否为 Numba 进行 AOT 编译):

【讨论】:

  • 非常感谢您!我已经在更大的数据中对其进行了测试,numpynumba 的两种方法确实比普通方法效果更好!
猜你喜欢
  • 1970-01-01
  • 2020-11-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多