初始化多个 Numpy 数组(多重赋值) - 像 MATLAB deal()

Posted

技术标签:

【中文标题】初始化多个 Numpy 数组(多重赋值) - 像 MATLAB deal()【英文标题】:Initialize Multiple Numpy Arrays (Multiple Assignment) - Like MATLAB deal() 【发布时间】:2014-01-17 22:01:38 【问题描述】:

我找不到任何描述如何执行此操作的内容,这导致我相信我没有以正确的 Python 惯用方式执行此操作。也将不胜感激有关“正确” Python 方法的建议。

我正在编写的数据记录器有一堆变量(任意记录长度,最大长度已知)。在 MATLAB 中,我会将它们全部初始化为长度为 n 的一维零数组,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.

我也不希望只制作一个大矩阵,因为跟踪哪一列是哪个变量是不愉快的。也许解决方案是制作(length x numvars) 矩阵,并将列切片分配给各个变量?

编辑:假设到此结束时我将拥有许多相同长度的向量;例如,我的后处理获取每个日志文件,计算一堆单独的指标(>50),存储它们,然后重复直到所有日志都被处理。然后我生成直方图,means/maxes/sigmas/etc。对于我计算的所有各种指标。由于在 Python 中初始化 50 多个向量显然并不容易,那么最好的(最干净的代码和良好的性能)方法是什么?

【问题讨论】:

【参考方案1】:

如果您真的有动力在单行中执行此操作,您可以创建一个由零组成的 (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 的语法让这有点痛苦。请参阅我的问题的编辑。 抱歉,您必须自己跟踪在 LHS 上分配了多少变量。问题是,在 Python 中,被调用的函数不知道将如何处理返回值,因此它不知道 LHS 需要多少。可以将其视为您为能够灵活地执行诸如索引函数调用结果之类的事情而付出的代价。 您可以考虑使用recarray 来跟踪多个具有不同“列名”的长度相同的空向量【参考方案2】:

没有错或不符合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() 的讨论;我标记了另一个答案,但我发现这个也很有帮助,希望我不必只选择一个。【参考方案3】:

如果您将数据放在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]
    # ...

【讨论】:

【参考方案4】:

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)

【讨论】:

以上是关于初始化多个 Numpy 数组(多重赋值) - 像 MATLAB deal()的主要内容,如果未能解决你的问题,请参考以下文章

在numpy中为多个切片赋值

有没有办法用块范围进行多重赋值和初始化?

带副本的 Numpy 数组赋值

从多个numpy数组中制作一个巨大的dict

Multiindex Pandas Dataframe中的多重赋值[重复]

处理numpy数组时赋值运算符中的指针行为?