Python 列表中的交替方向 - 更多 Pythonic 解决方案

Alternating Directions in Python List - More Pythonic Solution

我觉得这应该很简单,但我一直在寻找一个巧妙的解决方案。我提供的代码有效,并给出了我期望的输出,但我不觉得它是 Pythonic 的,它让我很紧张。

我使用基础数据集 'griddata' 生成了三组坐标,X、Y 和 Z。坐标均匀 spaced 在未知的总面积/形状(不一定是正方形/矩形)上产生 NaN 结果,我想忽略每个列表的边界。该列表应该从 'bottom left'(在坐标系中)开始遍历,穿过 x 轴,在 y 方向向上 space 然后从右到左,然后再继续。行数可能为奇数或偶数。

无论方向如何,对每个点执行的操作都是相同的,并且保证X中存在的每个点Y和Z中都存在一个点,如下面的代码所示。

数组(列表?)的格式为数据点[行][列]。

k = 0
for i in range(len(x)):
    if k % 2 == 0:  # cut left to right, then right to left
        for j in range(len(x[i])):
            if not numpy.isnan(x[i][j]):
                file.write(f'X{x[i][j]} Y{y[i][j]} Z{z[i][j]}')
    else:
        for j in reversed(range(len(x[i]))):
            if not numpy.isnan(x[i][j]):
                file.write(f'X{x[i][j]} Y{y[i][j]} Z{z[i][j]}')
    k += 1

我能想到的一个解决方案是在 运行 循环之前反转每个列表中的每隔一行。它会节省我几行,但从性能的角度来看可能没有意义 - 谁有更好的建议?

通过列表的预期路线:

End════<══════╗
╔══════>══════╝
╚══════<══════╗
Start══>══════╝

我同意@Prune 的观点,您的代码看起来可读性强并且做了它应该做的事情。您可以通过预先计算索引来稍微压缩它,就像这样(请注意,这是从左上角开始的):

import numpy as np

# generate some sample data
x = np.arange(100).reshape(10,10)

#precompute both directions
fancyranges = (
    list(range(len(x[0,:]))),
    reversed(list(range(len(x[0,:]))))
)

for a in range(x.shape[0]):
    # call appropriate directions
    for b in fancyranges[a%2]:
        # do things
        print(x[a,b])

您可以将可重复的代码移动到 sub_func,以便在一处进行进一步更改

def func():

    def sub_func():
        # repeatable code
        if not numpy.isnan(x[i][j]):
            print(f'X{x[i][j]}...')

    k = 0
    for i in range(len(x)):
        if k % 2 == 0:  # cut left to right, then right to left
            for j in range(len(x[i])):
                sub_func()
        else:
            for j in reversed(range(len(x[i]))):
                sub_func()
        k += 1


func()

这是一个变体:

for i, (x_row, y_row, z_row) in enumerate(zip(x, y, z)):
    if i % 2:
        z_row = reversed(x_row)
        y_row = reversed(y_row)
        z_row = reversed(z_row)
    row_strs = list()
    for x_elem, y_elem, z_elem in zip(x_row, y_row, z_row):
        if not numpy.isnan(x_elem):
            row_strs.append(f"X{x_elem} Y{y_elem} Z{z_elem}")
    file.write("".join(row_strs))

注意事项

没有永远比其他优化效果更好的优化方法。它还取决于代码处理的数据。这是我在不知道数据是什么样子的情况下可以想到的事情的列表:

  • for index range(len(sequence)): 不是 Pythonic 迭代方式。这里使用了 foreach 惯用语。如果需要索引,可以使用[Python 3.Docs]: Built-in Functions - enumerate(iterable, start=0)
  • 由于前面的项目符号,这不再适用,但 reversed(range(n))range(n - 1, -1, -1) 相同。不知道后者是否更快,但看起来会是
  • 一次迭代多个 iterables,使用 [Python 3.Docs]: Built-in Functions - zip(*iterables)
  • 不需要k,已经有i
  • 一般来说,在处理文件时,读/写大数倍的数据块要比小数倍的数据块好(文件通常驻留在磁盘上,磁盘操作很慢)。但是,缓冲默认发生(在 PythonOS 级别),因此这不再是问题,但仍然。但一如既往,这是资源(时间、内存等)之间的权衡。
    我选择每行写入一次文件(而不是像原来那样每个元素一次)。当然也有3rd一次全部写完的可能性,但我想对于更大的数据集,这不会是最好的解决方案
  • 可能,一些优化也可能发生在 NumPy 级别(因为它处理批量数据的速度比 Python代码(迭代)确实如此),但我不是该领域的专家,也不知道数据的样子