初始化多个Numpy数组(多重赋值)- 类似于MATLAB的deal()函数

12

我找不到任何描述如何做到这一点的内容,这导致我相信我没有用正确的Python惯用方式来做这件事。关于“正确”的Python方法的建议也将不胜感激。

我正在编写一个数据记录器的一堆变量(任意记录长度,已知最大长度)。在MATLAB中,我会将它们全部初始化为长度为n的零数组的1-D数组,n大于我曾经看到的条目数,分配每个单独元素variable(measurement_no) = data_point在记录循环中,并在测量结束时修剪掉多余的零。初始化看起来像这样:

[dData gData cTotalEnergy cResFinal etc] = deal(zeros(n,1));

有没有一种在Python/NumPy中完成这个操作的方法,这样我就不必将每个变量放在自己的行上:

dData = np.zeros(n)
gData = np.zeros(n)
etc.

我也不希望只创建一个大矩阵,因为跟踪哪一列是哪个变量很麻烦。也许解决方案是创建一个(长度×变量数)的矩阵,并将列切片分配给各个变量?

编辑:假设我最后会有很多相同长度的向量;例如,我的后处理对每个日志文件进行计算,得出许多单独的指标(>50),并存储它们,直到所有日志都被处理完为止。然后我为我计算的所有各种指标生成直方图、均值/最大值/标准差等。由于在Python中初始化50多个向量显然很困难,那么最好的方法(清晰的代码和良好的性能)是什么?

4个回答

22

如果你非常有动力把这个做成一行代码,你可以创建一个由零组成的 (n_vars, ...) 数组,然后沿着第一维度解包它:

如果您真的很有动力,在一行内完成此操作,您可以创建一个由零组成的(n_vars, ...)数组,然后沿着第一维展开它:
a, b, c = np.zeros((3, 5))
print(a is b)
# False

另外一种选项是使用列表推导式或生成器表达式:

a, b, c = [np.zeros(5) for _ in range(3)]   # list comprehension
d, e, f = (np.zeros(5) for _ in range(3))   # generator expression
print(a is b, d is e)
# False False

不过要小心!你可能认为在包含np.zeros()调用的列表或元组上使用*运算符会达到相同的效果,但事实并非如此:

h, i, j = (np.zeros(5),) * 3
print(h is i)
# True
这是因为元组内的表达式先被计算。因此,np.zeros(5) 只会调用一次,而重复的元组中的每个元素最终都将成为对同一数组的引用。这也是为什么你不能只使用 a = b = c = np.zeros(5) 的原因。
除非你真的需要赋值大量的空数组变量,并且你真的非常关心使你的代码紧凑(!),否则我建议为了可读性将它们分别在单独的行中初始化。

这个最接近我想做的事情。在我的其他Matlab代码中 - 用于分析数据记录器输出的内容 - 我最终创建了大量向量来存储每次运行计算度量的结果。它们都作为一个大型deal(zeros(n,1))的一部分进行初始化,我不想在赋值语句左侧跟踪它们的数量。我更愿意使用结构体,但Matlab的语法使这有些痛苦。请参见我问题的编辑。 - schodge
抱歉,你需要自己记录在左侧分配了多少个变量。问题在于在Python中,被调用的函数不知道将返回值用于何处,因此它无法知道左侧需要多少变量。可以把这看作是灵活性的代价,例如对函数调用结果进行索引等操作。 - ali_m
你可以考虑使用recarray来跟踪具有不同“列名”的相同长度的多个空向量。 - ali_m

5

没有任何问题或不符合Python风格的地方

dData = np.zeros(n)
gData = np.zeros(n)
etc.

你可以将它们放在一行上,但没有特别的理由这样做。

dData, gData = np.zeros(n), np.zeros(n)

不要尝试使用dData = gData = np.zeros(n),因为对dData的更改会改变gData(它们指向同一个对象)。出于同样的原因,通常不要使用x = y = []。 MATLAB中的deal是一种便利方式,但并非魔法。这是Octave实现它的方法。
function [varargout] = deal (varargin)
  if (nargin == 0)
    print_usage ();
  elseif (nargin == 1 || nargin == nargout)
    varargout(1:nargout) = varargin;
  else
    error ("deal: nargin > 1 and nargin != nargout");
  endif

endfunction

与Python不同,Octave(以及可能的MATLAB)中的情况是这样的:
one=two=three=zeros(1,3)

将不同的对象分配给3个变量。

注意 MATLAB 如何将 deal 视为一种将单元格和结构数组内容分配给变量的方法。 http://www.mathworks.com/company/newsletters/articles/whats-the-big-deal.html


谢谢,我很感激关于deal()的讨论。我标记了另一个答案,但我也觉得这个答案非常有帮助,希望我不必只选一个。 - schodge

0
如果您将数据放入collections.defaultdict中,您就不需要进行任何显式初始化。每个元素在第一次使用时都会被初始化。
import numpy as np
import collections
n = 100
data = collections.defaultdict(lambda: np.zeros(n))
for i in range(1, n):
    data['g'][i] = data['d'][i - 1]
    # ...

0
使用 map 呢?
import numpy as np
n = 10  # Number of data points per array
m = 3   # Number of arrays being initialised
gData, pData, qData = map(np.zeros, [n] * m)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接