在python中寻找更好的方法来处理numpy数组或列表上的周期性边界条件

2024-04-29 12:23:07 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一组大约5到100行、5000到25000列的大型数据集(二维矩阵)。我被告知从每一行中提取一条,给出了条的长度。对于每一行,将从该行的随机位置开始填充条带,并一直填充,如果该位置超出该行的长度,它将从开始拾取条目,就像周期边界一样。例如,假设一行有10个元素

row = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

该位置拾取为8,条带长度为4。然后,条带将被[9, 10, 1, 2]

首先,我尝试使用NumPy进行计算

A = np.ones((5, 8000), order='F')
import time
L = (4,3,3,3,4) # length for each of the 5 strips
starttime = time.process_time()
for i in range(80000):
    B = []
    for c, row in enumerate(A):
        start = random.randint(0,len(row)-1)
        end = start+L[c]
        if end>len(row)-1:
            sce = np.zeros(L[c])
            for k in range(start, end):
                sce[k-start] = k%len(row)
        else:
            sce = row[start:end]
        B = sce

print(time.process_time() - starttime)

我没有处理边界条件的好方法,所以我把它分成两种情况:一种是当整个条带在行内,另一种是当条带的一部分在行外。此代码有效,运行大约需要1.5秒。然后我试着用列表代替

A = [[1]*8000]*5
starttime = time.process_time()
for i in range(80000):
    B = []
    for c, row in enumerate(A):
        start = random.randint(0,len(row)-1)
        end = start+L[c]
        if end>len(row)-1:
            sce = np.zeros(L[c])
            for k in range(start, end):
                sce[k-start] = k%len(row)
        else:
            sce = row[start:end]
        B = sce

 print(time.process_time() - starttime)

这一个大约快了0.5秒,我很惊讶我期望NumPy会更快!!!这两种代码都适用于较小的矩阵大小和较少的迭代次数。但在实际项目中,我将处理一个非常大的矩阵和更多的迭代,我想知道是否有任何建议来提高效率。此外,对于如何处理周期性边界条件(更整洁、更高效),是否有任何建议


Tags: innumpyforlentimenprange矩阵
1条回答
网友
1楼 · 发布于 2024-04-29 12:23:07

考虑到在计时之前创建数组A,这两种解决方案的速度相同,因为您只是在数组上迭代。但我实际上不知道为什么纯python解决方案更快,也许是因为基于集合的迭代器(枚举)更适合原始python类型

查看一行的示例,您希望从该行中获取一系列元素,并环绕越界索引。为此,我建议:

row = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
start = 8
L = 4
np.take(row, np.arange(start, start+L), mode='wrap')

输出:

array([ 9, 10,  1,  2])

然后,可以通过指定axis关键字将此行为扩展到两个维度。但是在L中处理长度不均匀的数组确实有点棘手,因为使用非均匀数组会失去使用numpy的大部分好处。解决方法是以相同大小的长度分组在一起的方式对L进行分区

如果我正确理解了整个任务,那么会给您一些起始值,并且希望沿A的第二个轴提取每个对应的条带长度

A = np.arange(5*8000).reshape(5,8000) # using arange makes it easier to verify output
L = (4,3,3,3,4) # length for each of the 5 strips
parts = ((0,4), (1,2,3)) # partition L (to lazy to implement this myself atm)
start = 7998 # arbitrary start position

for part in parts:
  ranges = np.arange(start, start+L[part[0]])
  out = np.take(A[part,:], ranges, axis=-1, mode='wrap')
  print(f'Output for rows {part} with length {L[part[0]]}:\n\n{out}\n')

输出:

Output for rows (0, 4) with length 4:

[[ 7998  7999     0     1]
 [39998 39999 32000 32001]]

Output for rows (1, 2, 3) with length 3:

[[15998 15999  8000]
 [23998 23999 16000]
 [31998 31999 24000]]

尽管如此,看起来您希望每行都有一个随机的起始位置

相关问题 更多 >