展开和还原一个嵌套的numpy数组列表

5

有许多方法可以展开嵌套列表。这里仅提供一种解决方案供参考:

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 数组的形状。

1
从压缩版本中,你怎么可能知道它最初的样子呢?在压缩的过程中你丢失了信息。 - jonrsharpe
当然,展平函数应该被改变以存储列表的结构。 - memecs
在某种程度上,你已经回答了自己的问题;你需要修改 flatten 函数以保留列表的结构信息和其中数组的形状信息。例如,它可以返回除了被展平的 L 之外的 [[(2, 2)], [(2, 2)], [(2, 2), (3, 2)]]。然后你将需要相应地切分 L_flat 并且对每个切片中的数组进行 reshape - jonrsharpe
是啊,我也是这么想/希望有人已经有一个现成的配方可以使用。 - memecs
1
我觉得很难相信在纯Python中展平、保存、读取和重建会比使用内置的(C语言)方法更快。你应该更好地定义“相对于什么而言慢”。相对于纯C或FORTRAN?当然是。相对于XML?可能不是。你知道创建一个array(L)将把你的列表构建成一个numpy本地数组,它将保留结构吗?你测试过使用savez处理p×q×N数组的性能吗? - msw
显示剩余6条评论
3个回答

5

我正在寻找一个解决方案,用于展平和还原嵌套的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


0

这是我想出来的方法,结果比遍历嵌套列表并逐个加载要快约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)

一个更好的解决方案应该可以迭代地处理任意数量的嵌套层级。

这给了我一个错误:numpy.float64 类型的对象没有长度。 - jlansey

-1
你正在构建一个悖论:你想展开对象,但又不想展开对象,在对象的某个地方保留其结构信息。
因此,Pythonic 的做法不是展平对象,而是编写一个类,该类具有一个 __iter__ 方法,允许您以顺序(即以平面方式)遍历基础对象的元素。这将与将其转换为平面内容(仅对每个元素应用一次)的速度大致相同,并且您不会复制或更改原始非平面容器。

我的使用情况是需要在数组的排序元素上应用函数。 - jlansey
另一个常见的用例是与向量化函数一起使用。您可以将其重塑为一维,应用该函数,然后将结果重塑回原始格式。Numpy的重塑功能假定是完整的矩阵,而不是(可能是不规则的)嵌套列表。 - Jonathan Richards

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