展平和取消展平 numpy 数组的嵌套列表

Posted

技术标签:

【中文标题】展平和取消展平 numpy 数组的嵌套列表【英文标题】:Flattening and unflattening a nested list of numpy arrays 【发布时间】:2015-03-14 23:13:31 【问题描述】:

扁平化嵌套列表的方法有很多。我在这里复制一个解决方案仅供参考:

def flatten(x):
    result = []
    for el in x:
      if hasattr(el, "__iter__") and not isinstance(el, basestring):
        result.extend(flatten(el))
      else:
        result.append(el)
    return result

我感兴趣的是逆运算,它将列表重建为其原始格式。例如:

L = [[array([[  24, -134],[ -67, -207]])], 
     [array([[ 204,  -45],[  99, -118]])], 
     [array([[  43, -154],[-122,  168]]), array([[  33, -110],[ 147,  -26],[ -49, -122]])]]

# flattened version

L_flat = [24, -134, -67, -207, 204, -45, 99, -118, 43, -154, -122, 168, 33, -110, 147, -26, -49, -122]

有没有一种有效的方法来展平、保存索引并重建为原始格式?

请注意,列表可以是任意深度,并且可能没有规则的形状,并且将包含不同维度的数组。

当然,扁平化函数也要改成存储列表的结构和numpy数组的形状。

【问题讨论】:

您应该如何从扁平化版本中知道它最初的样子?您在展平过程中丢失了信息。 当然,扁平化功能应该改成存储列表的结构。 在某种程度上,您已经回答了自己的问题;您需要修改flatten 以提供有关列表结构和其中数组形状的保留信息。例如,它可以在扁平化的L 旁边返回[[(2, 2)], [(2, 2)], [(2, 2), (3, 2)]]。然后你将不得不相应地切分 L_flatreshape 每个切片中的数组。 是的,我认为/希望有人有现成的配方。 我很难相信纯 Python 中的扁平化、保存、读取和重构会击败内在 (C) 方法来做同样的事情。您可能应该更好地定义“相对于什么慢”。相对于纯 C 或 FORTRAN?当然。相对于 XML?可能不是。您是否知道创建 array(L) 会将您的列表构建成一个 numpy 本机数组,该数组将保留结构?你用 savez 测试过 p×q×N 数组的性能吗? 【参考方案1】:

您正在构建一个悖论:您想要扁平化对象,但又不想扁平化对象,将其结构信息保留在对象的某处。

因此,pythonic 的方法是 不是 来展平对象,而是编写一个具有__iter__ 的类,它允许您按顺序(即以平面方式)通过底层对象的元素。这将与转换为平面事物一样快(如果每个元素仅应用一次),并且您不会复制或更改原始的非平面容器。

【讨论】:

我的用例是我需要对数组的排序元素应用一个函数。 另一个常见的用例是使用矢量化函数。您将整形缩小到一维,应用函数,然后将结果整形回原始格式。 Numpy 的 reshape 函数假定完整的矩阵,而不是(可能参差不齐的)嵌套列表。【参考方案2】:

这是我想出的,结果证明它比迭代嵌套列表和单独加载快约 30 倍。

def flatten(nl):
    l1 = [len(s) for s in itertools.chain.from_iterable(nl)]
    l2 = [len(s) for s in nl]

    nl = list(itertools.chain.from_iterable(
        itertools.chain.from_iterable(nl)))

    return nl,l1,l2

def reconstruct(nl,l1,l2):
    return np.split(np.split(nl,np.cumsum(l1)),np.cumsum(l2))[:-1]

L_flat,l1,l2 = flatten(L)
L_reconstructed = reconstruct(L_flat,l1,l2)

一个更好的解决方案可以对任意数量的嵌套层进行迭代。

【讨论】:

这给了我一个错误object of type 'numpy.float64' has no len()【参考方案3】:

我正在寻找一种解决方案来展平和取消展平 numpy 数组的嵌套列表,但只发现了这个未回答的问题,所以我想出了这个:

def _flatten(values):
    if isinstance(values, np.ndarray):
        yield values.flatten()
    else:
        for value in values:
            yield from _flatten(value)

def flatten(values):
    # flatten nested lists of np.ndarray to np.ndarray
    return np.concatenate(list(_flatten(values)))

def _unflatten(flat_values, prototype, offset):
    if isinstance(prototype, np.ndarray):
        shape = prototype.shape
        new_offset = offset + np.product(shape)
        value = flat_values[offset:new_offset].reshape(shape)
        return value, new_offset
    else:
        result = []
        for value in prototype:
            value, offset = _unflatten(flat_values, value, offset)
            result.append(value)
        return result, offset

def unflatten(flat_values, prototype):
    # unflatten np.ndarray to nested lists with structure of prototype
    result, offset = _unflatten(flat_values, prototype, 0)
    assert(offset == len(flat_values))
    return result

例子:

a = [
    np.random.rand(1),
    [
        np.random.rand(2, 1),
        np.random.rand(1, 2, 1),
    ],
    [[]],
]

b = flatten(a)

# 'c' will have values of 'b' and structure of 'a'
c = unflatten(b, a)

输出:

a:
[array([ 0.26453544]), [array([[ 0.88273824],
       [ 0.63458643]]), array([[[ 0.84252894],
        [ 0.91414218]]])], [[]]]
b:
[ 0.26453544  0.88273824  0.63458643  0.84252894  0.91414218]
c:
[array([ 0.26453544]), [array([[ 0.88273824],
       [ 0.63458643]]), array([[[ 0.84252894],
        [ 0.91414218]]])], [[]]]

许可证:WTFPL

【讨论】:

以上是关于展平和取消展平 numpy 数组的嵌套列表的主要内容,如果未能解决你的问题,请参考以下文章

在 R 中展平或取消列出数据框

如何在 Amazon Redshift 的列中取消嵌套/展开/展平逗号分隔值?

展平 NumPy 数组列表?

Bigquery:UNNEST 重复与展平表性能

在 BigQuery 中展平嵌套和重复的结构(标准 SQL)

在 BigQuery 中取消嵌套结构