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
- 一般来说,在处理文件时,读/写大数倍的数据块要比小数倍的数据块好(文件通常驻留在磁盘上,磁盘操作很慢)。但是,缓冲默认发生(在 Python、OS 级别),因此这不再是问题,但仍然。但一如既往,这是资源(时间、内存等)之间的权衡。
我选择每行写入一次文件(而不是像原来那样每个元素一次)。当然也有3rd一次全部写完的可能性,但我想对于更大的数据集,这不会是最好的解决方案
- 可能,一些优化也可能发生在 NumPy 级别(因为它处理批量数据的速度比 Python代码(迭代)确实如此),但我不是该领域的专家,也不知道数据的样子
我觉得这应该很简单,但我一直在寻找一个巧妙的解决方案。我提供的代码有效,并给出了我期望的输出,但我不觉得它是 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
- 一般来说,在处理文件时,读/写大数倍的数据块要比小数倍的数据块好(文件通常驻留在磁盘上,磁盘操作很慢)。但是,缓冲默认发生(在 Python、OS 级别),因此这不再是问题,但仍然。但一如既往,这是资源(时间、内存等)之间的权衡。
我选择每行写入一次文件(而不是像原来那样每个元素一次)。当然也有3rd一次全部写完的可能性,但我想对于更大的数据集,这不会是最好的解决方案 - 可能,一些优化也可能发生在 NumPy 级别(因为它处理批量数据的速度比 Python代码(迭代)确实如此),但我不是该领域的专家,也不知道数据的样子