【问题标题】:Slicing sublists with different lengths切片不同长度的子列表
【发布时间】:2016-11-08 13:16:35
【问题描述】:

我有一个列表列表。每个子列表的长度在 1 到 100 之间变化。每个子列表包含一组数据中不同时间的粒子 ID。我想在给定时间形成所有粒子 ID 的列表。为此,我可以使用类似的东西:

    list = [[1,2,3,4,5],[2,6,7,8],[1,3,6,7,8]]
    list2 = [item[0] for item in list]

list2 将包含列表中每个子列表的第一个元素。我想不仅对第一个元素执行此操作,还对 1 到 100 之间的每个元素执行此操作。我的问题是每个子列表都不存在第 100 个元素(或 66 或 77 等)。

是否有某种方法可以创建列表列表,其中每个子列表是给定时间所有粒子 ID 的列表。

我考虑过尝试使用 numpy 数组来解决这个问题,就好像列表的长度一样,这将是微不足道的。我尝试在每个列表的末尾添加 -1 以使它们的长度相同,然后掩盖负数,但到目前为止这对我不起作用。我将在给定时间使用 ID 列表来分割另一个单独的数组:

    pos = pos[satIDs]

【问题讨论】:

  • 你想用一行for循环来做这个吗?还是任何 for 循环都可以?
  • 任何循环都可以,但是数据集非常大,所以速度可能是个问题。
  • “屏蔽负数,但到目前为止这对我没有用” - 你怎么没用?

标签: python list numpy slice


【解决方案1】:
lst = [[1,2,3,4,5],[2,6,7,8],[1,3,6,7,8]]
func =  lambda x: [line[x] for line in lst if len(line) > x]

func(3)
[4, 8, 7]
func(4)
[5, 8]

--更新--

func =  lambda x: [ (line[x],i) for i,line in enumerate(lst) if len(line) > x]
func(4)
[(5, 0), (8, 2)]

【讨论】:

  • 非常感谢,还有一种方法可以跟踪 func(x) 中的哪个元素来自哪个列表,例如一个附加列表(对于 func(4)),5 来自子列表 0,8 来自子列表 2?
  • ...使用lambda 然后将其分配给名称有什么意义?只需使用def func(x): return [line [x] ...]
  • @Jack 已更新。元组中的第一个元素是值,第二个是子列表的数量
  • @Bakuriu 同意你的看法
【解决方案2】:

如果你想用one-line forlooparray 来做,你可以这样做:

list2 = [[item[i] for item in list if len(item) > i] for i in range(0, 100)]

如果你想知道哪个 id 来自哪个列表,你可以这样做:

list2 = [{list.index(item): item[i] for item in list if len(item) > i} for i in range(0, 100)]

list2 会是这样的:

[{0: 1, 1: 2, 2: 1}, {0: 2, 1: 6, 2: 3}, {0: 3, 1: 7, 2: 6}, {0: 4, 1: 8, 2: 7},
 {0: 5, 2: 8}, {}, {}, ... ]

【讨论】:

    【解决方案3】:

    您可以将 numpy.nan 附加到您的短列表中,然后创建一个 numpy 数组

    import numpy
    import itertools
    
    lst = [[1,2,3,4,5],[2,6,7,8],[1,3,6,7,8,9]]
    arr = numpy.array(list(itertools.izip_longest(*lst, fillvalue=numpy.nan)))
    

    之后你就可以像往常一样使用 numpy 切片了。

    print arr
    print arr[1, :]   # [2, 6, 3]
    print arr[4, :]   # [5, nan, 8]
    print arr[5, :]   # [nan, nan, 9]
    

    【讨论】:

    • 如果我以这种方式进行切片,我能否分辨出切片数组中的每个元素来自哪一行? IE。它会保留nans吗?
    • 当然可以。每当您跑过任何列表的末尾时,该条目就会变成nan
    • 但是我是否可以使用切片数组,例如 x = [nan,nan,9] 来执行另一个数组 data[x] 的切片 - 这不会给我一个错误? IndexError:用作索引的数组必须是整数(或布尔)类型
    • 您正在尝试将结果列表用作索引?抱歉,我没有在您的帖子中看到。
    【解决方案4】:

    您可以使用itertools.zip_longest。这会将zip 列表放在一起,并在其中一个列表用完时插入None

    >>> lst = [[1,2,3,4,5],['A','B','C'],['a','b','c','d','e','f','g']]    
    >>> list(itertools.zip_longest(*lst))
    [(1, 'A', 'a'),
     (2, 'B', 'b'),
     (3, 'C', 'c'),
     (4, None, 'd'),
     (5, None, 'e'),
     (None, None, 'f'),
     (None, None, 'g')]
    

    如果您不想要 None 元素,可以将它们过滤掉:

    >>> [[x for x in sublist if x is not None] for sublist in itertools.zip_longest(*lst)]
    [[1, 'A', 'a'], [2, 'B', 'b'], [3, 'C', 'c'], [4, 'd'], [5, 'e'], ['f'], ['g']]
    

    【讨论】:

      【解决方案5】:

      方法#1

      可以建议一种几乎*矢量化的方法,即根据新顺序创建 ID 并进行拆分,就像这样 -

      def position_based_slice(L):
      
          # Get lengths of each element in input list
          lens = np.array([len(item) for item in L])
      
          # Form ID array that has *ramping* IDs within an element starting from 0
          # and restarts with a new element at 0
          id_arr = np.ones(lens.sum(),int)
          id_arr[lens[:-1].cumsum()] = -lens[:-1]+1
      
          # Get order maintained sorted indices for sorting flattened version of list
          ids = np.argsort(id_arr.cumsum(),kind='mergesort')
      
          # Get sorted version and split at boundaries decided by lengths of ids
          vals = np.take(np.concatenate(L),ids)
          cut_idx = np.where(np.diff(ids)<0)[0]+1
          return np.split(vals,cut_idx)
      

      *一开始就涉及到一个循环理解,但它只收集列表输入元素的长度,它对总运行时间的影响应该是最小的。

      示例运行 -

      In [76]: input_list = [[1,2,3,4,5],[2,6,7,8],[1,3,6,7,8],[3,2]]
      
      In [77]: position_based_slice(input_list)
      Out[77]: 
      [array([1, 2, 1, 3]), # input_list[ID=0]
       array([2, 6, 3, 2]), # input_list[ID=1]
       array([3, 7, 6]),    # input_list[ID=2]
       array([4, 8, 7]),    # input_list[ID=3]
       array([5, 8])]       # input_list[ID=4]
      

      方法 #2

      这是另一种创建2D 数组的方法,它更容易索引和追溯至原始输入元素。这使用 NumPy 广播和布尔索引。实现看起来像这样 -

      def position_based_slice_2Dgrid(L):
      
          # Get lengths of each element in input list
          lens = np.array([len(item) for item in L])
      
          # Create a mask of valid places in a 2D grid mapped version of list
          mask = lens[:,None] > np.arange(lens.max())
          out = np.full(mask.shape,-1,dtype=int)
          out[mask] = np.concatenate(L)
          return out
      

      示例运行 -

      In [126]: input_list = [[1,2,3,4,5],[2,6,7,8],[1,3,6,7,8],[3,2]]
      
      In [127]: position_based_slice_2Dgrid(input_list)
      Out[127]: 
      array([[ 1,  2,  3,  4,  5],
             [ 2,  6,  7,  8, -1],
             [ 1,  3,  6,  7,  8],
             [ 3,  2, -1, -1, -1]])
      

      所以,现在输出的每一列都将对应于基于 ID 的输出。

      【讨论】:

        猜你喜欢
        • 2022-01-18
        • 2022-06-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-31
        • 2016-08-09
        • 2021-07-11
        • 1970-01-01
        相关资源
        最近更新 更多