切片具有不同长度的子列表
Slicing sublists with different lengths
我有一个列表列表。每个子列表的长度在 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]
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)]
如果你想在 one-line forloop
和 array
中使用它,你可以这样做:
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}, {}, {}, ... ]
您可以将 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]
您可以使用 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']]
方法 #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 的输出。
我有一个列表列表。每个子列表的长度在 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]
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)]
如果你想在 one-line forloop
和 array
中使用它,你可以这样做:
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}, {}, {}, ... ]
您可以将 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]
您可以使用 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']]
方法 #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 的输出。