如何在一个长度不同的列表的列表中移除最内层的嵌套?

7

我正在尝试从一个由单元素长度列表的列表组成的列表中移除最内层嵌套。你知道一个相对容易的方法(将其转换为NumPy数组也可以),来实现下面这种形式:

[[[1], [2], [3], [4], [5]], [[6], [7], [8]], [[11], [12]]]

你想把它翻译成什么?
[[1, 2, 3, 4, 5], [6, 7, 8], [11, 12]]

此外,我正在尝试处理的实际列表包含日期时间对象而不是示例中的整数。初始列表集合长度各不相同。或者,如果原始列表中有NaN,则每个列表的长度相同,只要输出列表中没有NaN即可。例如:
[[[1], [2], [3], [4], [5]], 
 [[6], [7], [8], [nan], [nan]], 
 [[11], [12], [nan], [nan], [nan]]]

转换为:

[[1, 2, 3, 4, 5], [6, 7, 8], [11, 12]]

什么是NaN?它是什么类型的对象? - kmaork
嵌套是否始终一致? - juanpa.arrivillaga
假设您已经有一个多维的numpy数组,那么np.squeeze()不就可以实现这个功能吗? - crackpotHouseplant
在 nan 的例子中,我使用了列表的 extend 方法来使列表长度相等。这使得我可以将其转换为 numpy 数组并重新整形以消除最内层的嵌套。 - Matthew Borish
1
你的实际数据中这些列表的典型长度是多少? - Warren Weckesser
7个回答

7
如果嵌套始终保持一致,那么这是微不足道的:
In [2]: import itertools

In [3]: nested = [ [ [1],[2],[3],[4], [5] ], [ [6],[7],[8] ] , [ [11],[12] ] ]

In [4]: unested = [list(itertools.chain(*sub)) for sub in nested]

In [5]: unested
Out[5]: [[1, 2, 3, 4, 5], [6, 7, 8], [11, 12]]

请注意,那些使用列表的add方法来解决问题的方案会导致性能为O(n^2),其中n是正在合并每个子列表中的子子列表的数量。


3

那么np.squeeze呢?

从数组的形状中删除单维条目。

arr = [ [ [1],[2],[3],[4], [5] ], [ [6],[7],[8] ] , [ [11],[12] ] ]
>>> arr
[[[1], [2], [3], [4], [5]], [[6], [7], [8]], [[11], [12]]]
>>> [np.squeeze(i) for i in arr]
[array([1, 2, 3, 4, 5]), array([6, 7, 8]), array([11, 12])]

并不一定是最内层的维度(即与维度数量无关)。但您的问题指定了“列表的列表”。


为了清理最终结果,您应该使用np.array进行组合:np.array([np.squeeze(i) for i in arr])以获得一个干净的数组。 - Michael Currie
对我来说,这是最干净的答案,因为它使用了内置的numpy函数(而且OP指定了可以使用它们)。 - wlo

2
>>> from operator import add
>>> lists = [ [ [1],[2],[3],[4], [5] ],   [ [6],[7],[8] ] , [ [11],[12] ] ]
>>> [reduce(add, lst) for lst in lists]
[[1, 2, 3, 4, 5], [6, 7, 8], [11, 12]]

这并不是一种非常高效的方法,因为每次调用add时都需要重建一个列表。或者,您可以使用sum或简单的列表推导式,就像其他答案中所看到的那样。

2
因为这个问题看起来很有趣!我使用了一个递归函数,如果列表只有一个值,它会解包这个值。
def make_singular(l):
    try:
        if len(l) == 1:
            return l[0]
        else:
            return [make_singular(l_) for l_ in l]
    except:
        return l

nest = [ [ [1],[2],[3],[4], [5] ], [ [6],[7],[8] ] , [ [11],[12] ] ]
make_singular(nest)

[[1, 2, 3, 4, 5], [6, 7, 8], [11, 12]]

0

试试这个:

l = [ [ [1],[2],[3],[4],[5] ],
      [ [6],[7],[8], [None],[None]] ,
      [ [11],[12],[None],[None],[None]] ]

l = [ [x[0] for x in s if x[0] is not None] for s in l]

0

就像您的情况一样,最内层对象只有一个元素。您可以根据索引访问该值,而无需使用其他函数。例如:

>>> [[y[0] for y in x] for x in my_list]
[[1, 2, 3, 4, 5], [6, 7, 8], [11, 12]]

如果您的最内层列表可能有多个元素,则可以执行以下操作:

>>> [[z for y in x for z in y] for x in my_list]
[[1, 2, 3, 4, 5], [6, 7, 8], [11, 12]]

0

如果您知道嵌套的层数,那么其中一个列表推导式就很容易。

In [129]: ll=[ [ [1],[2],[3],[4], [5] ], [ [6],[7],[8] ] , [ [11],[12] ] ]
In [130]: [[j[0] for j in i] for i in ll]        # simplest
Out[130]: [[1, 2, 3, 4, 5], [6, 7, 8], [11, 12]]

如果准则只是要移除嵌套的内层,不管它发生多深,代码将需要更多的思考。我可能会尝试将其编写为一个递归函数。

np.nan(或None)填充对于列表版本没有帮助。

In [131]: lln=[ [ [1],[2],[3],[4],[5] ], [ [6],[7],[8],[nan],[nan]] , [ [11],[12],[nan],[nan],[nan] ] ]
In [132]: [[j[0] for j in i if j[0] is not np.nan] for i in lln]
Out[132]: [[1, 2, 3, 4, 5], [6, 7, 8], [11, 12]]

填充使我们能够创建一个三维数组,然后可以轻松地压缩:

In [135]: arr = np.array(lln)
In [136]: arr.shape
Out[136]: (3, 5, 1)
In [137]: arr = arr[:,:,0]
In [138]: arr
Out[138]: 
array([[  1.,   2.,   3.,   4.,   5.],
       [  6.,   7.,   8.,  nan,  nan],
       [ 11.,  12.,  nan,  nan,  nan]])

但接下来有一个问题,如何去除那些nan并创建不规则子列表。

掩码数组可以让你在处理2D数组时不受这些 nan 的干扰:

In [141]: M = np.ma.masked_invalid(arr)
In [142]: M
Out[142]: 
masked_array(data =
 [[1.0 2.0 3.0 4.0 5.0]
 [6.0 7.0 8.0 -- --]
 [11.0 12.0 -- -- --]],
             mask =
 [[False False False False False]
 [False False False  True  True]
 [False False  True  True  True]],
       fill_value = 1e+20)
In [144]: M.sum(axis=1)      # e.g. sublist sums
Out[144]: 
masked_array(data = [15.0 21.0 23.0],
             mask = [False False False],
       fill_value = 1e+20)

arr 中移除 nan 可能最容易的方法是使用列表推导式。这些值是浮点数,因为 np.nan 是浮点数。
In [153]: [[i for i in row if ~np.isnan(i)] for row in arr]
Out[153]: [[1.0, 2.0, 3.0, 4.0, 5.0], [6.0, 7.0, 8.0], [11.0, 12.0]]

所以填充没有帮助。

如果填充是None,那么数组将是对象数据类型,更接近于字符中的嵌套列表。

In [163]: lln
Out[163]: 
[[[1], [2], [3], [4], [5]],
 [[6], [7], [8], [None], [None]],
 [[11], [12], [None], [None], [None]]]
In [164]: arr=np.array(lln)[:,:,0]
In [165]: arr
Out[165]: 
array([[1, 2, 3, 4, 5],
       [6, 7, 8, None, None],
       [11, 12, None, None, None]], dtype=object)
In [166]: [[i for i in row if i is not None] for row in arr]
Out[166]: [[1, 2, 3, 4, 5], [6, 7, 8], [11, 12]]

另一种数组方法是计算第二级别的有效元素数量;将整个数组展平,然后使用split函数。

一个递归函数:

def foo(alist):
    if len(alist)==1:
        return alist[0]
    else:
        return [foo(i) for i in alist if foo(i) is not None]

In [200]: ll=[ [ [1],[2],[3],[4], [5] ], [ [6],[7],[8] ] , [11], [[[12],[13]]]] 
In [201]: foo(ll)
Out[201]: [[1, 2, 3, 4, 5], [6, 7, 8], 11, [[12], [13]]]
In [202]: lln=[ [ [1],[2],[3],[4],[5] ], [ [6],[7],[8],[None],[None]] , [ [11],[12],[None],[None],[None] ] ]
In [203]: foo(lln)
Out[203]: [[1, 2, 3, 4, 5], [6, 7, 8], [11, 12]]

它递归到列表长度为1的级别。 它仍然很脆弱,如果嵌套级别不同则会出现问题。 从概念上讲,它与@piRSquared的答案非常相似。


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