如何"切片"一对基于其中一个值的列表

时间:2018-02-12 03:12:01

标签: python list

我有两个长度相等的列表,一个包含labels,另一个包含data。例如:

labels = ['cat', 'cat', 'dog', 'dog', 'dog', 'fish', 'fish', 'giraffe', ...]
data = [ 0.3, 0.1, 0.9, 0.5, 0.4, 0.3, 0.2, 0.8, ... ]

如何根据labels列表中的特定标签并行提取两个列表的子列表?

例如,使用fish作为选择条件,我想生成:

selected_labels = [ 'fish', 'fish' ]
selected_data = [ 0.3, 0.2 ]

我最好的猜测听起来很麻烦 - 制作元素明智元组列表,从该列表中提取相关元组列表,然后将元组列表反序列化为两个单元素列表。即使这是接近它的方式,我也不太陌生,因为它偶然发现了语法。

5 个答案:

答案 0 :(得分:4)

使用zip()generator expression可以这样做:

代码:

tuples = (x for x in zip(labels, data) if x[0] == 'fish')
selected_labels, selected_data = map(list, zip(*tuples))

这是如何工作的?

tuples行构建一个generator expression,将两个列表压缩在一起并删除任何不感兴趣的内容。第二行再次使用zip,然后根据需要将map生成的元组转换为list

这样做的好处是不需要构建中间数据结构,因此应该具有相当快的速度和内存效率。

测试代码:

labels = ['cat', 'cat', 'dog', 'dog', 'dog', 'fish', 'fish', 'giraffe']
data = [0.3, 0.1, 0.9, 0.5, 0.4, 0.3, 0.2, 0.8]

tuples = (x for x in zip(labels, data) if x[0] == 'fish')
selected_labels, selected_data = map(list, zip(*tuples))

print(selected_labels)
print(selected_data)

结果:

['fish', 'fish']
[0.3, 0.2]

答案 1 :(得分:3)

这可能是应用itertools.compress的好地方,它比zip略快,至少对于您正在使用的数据结构的大小而言。

from itertools import compress

selected_data = list(compress(data, (i=='fish' for i in labels)))
selected_labels = ['fish'] * len(selected_data)

用法:

compress('ABCDEF', [1,0,1,0,1,1]) --> A C E F

定时:

def with_compress():
    selected_data = list(compress(data, (i=='fish' for i in labels)))
    selected_labels = ['fish'] * len(selected_data)
    return selected_data, selected_labels

def with_zip():
    tuples = (x for x in zip(labels, data) if x[0] == 'fish')
    selected_labels, selected_data = map(list, zip(*tuples))
    return selected_data, selected_labels

%timeit -r 7 -n 100000 with_compress()
3.82 µs ± 96.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%timeit -r 7 -n 100000 with_zip()
4.67 µs ± 348 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

(i=='fish' for i in labels)TrueFalse的生成器。 compressdata元素向下过滤True发生的情况。

来自docstring:

  

大致相当于:

def compress(data, selectors):
    # compress('ABCDEF', [1,0,1,0,1,1]) --> A C E F
    return (d for d, s in zip(data, selectors) if s)

答案 2 :(得分:2)

您可以zip列出这些列表,根据您要查找的关键字对其进行过滤,然后unzip

>>> items = zip(*filter(lambda x: x[0] == "fish",zip(labels,data)))
>>> list(items)
>>> [('fish', 'fish'), (0.3, 0.2)]

然后您的selected_dataselected_labels将是:

>>> selected_data = list(items[1])
>>> selected_labels = list(items[0])

另一种方法是使用map函数来获得所需的格式:

 >>> items = map(list,zip(*filter(lambda x: x[0] == "fish",zip(labels,data))))
>>> list(items) 
>>> [['fish', 'fish'], [0.3, 0.2]]

答案 3 :(得分:2)

最简单的方法在这里完全没问题,并且非常有效:

>>> selected_labels, selected_data  = [], []
>>> for l, d in zip(labels, data):
...     if l == 'fish':
...         selected_labels.append(l)
...         selected_data.append(d)
...
>>> selected_labels
['fish', 'fish']
>>> selected_data
[0.3, 0.2]

更多时间,到目前为止没有时间包括每一种方法,但这里有一些:

>>> labels*=5000
>>> data *= 5000
>>> def juan(data, labels, target):
...     selected_labels, selected_data  = [], []
...     for l, d in zip(labels, data):
...         if l == target:
...             selected_labels.append(l)
...             selected_data.append(d)
...     return selected_labels, selected_data
...
>>> def stephen_rauch(data, labels, target):
...     tuples = (x for x in zip(labels, data) if x[0] == target)
...     selected_labels, selected_data = map(list, zip(*tuples))
...     return selected_labels, selected_data
...
>>> from itertools import compress
>>>
>>> def brad_solomon(data, labels, target):
...     selected_data = list(compress(data, (i==target for i in labels)))
...     selected_labels = ['fish'] * len(selected_data)
...     return selected_data, selected_labels
...
>>> import timeit
>>> setup = "from __main__ import data, labels, juan, stephen_rauch, brad_solomon"
>>> timeit.timeit("juan(data,labels,'fish')", setup, number=1000)
3.1627789690101054
>>> timeit.timeit("stephen_rauch(data,labels,'fish')", setup, number=1000)
3.8860850729979575
>>> timeit.timeit("brad_solomon(data,labels,'fish')", setup, number=1000)
2.7442518350144383

我会说,依靠itertools.compress做得很好。我担心不得不做selected_labels = ['fish'] * len(selected_data)会减慢速度,但它是一个可以在Python中高度优化的表达式(提前知道列表的大小,只是重复相同的指针)。最后,请注意,我给出的简单,天真的方法可以通过“缓存”.append方法进行优化:

>>> def juan(data, labels, target):
...     selected_labels, selected_data  = [], []
...     append_label = selected_labels.append
...     append_data = selected_data.append
...     for l, d in zip(labels, data):
...         if l == target:
...             append_label(l)
...             append_data(d)
...     return selected_labels, selected_data
...
>>> timeit.timeit("juan(data,labels,'fish')", setup, number=1000)
2.577823764993809

答案 4 :(得分:0)

作为zip答案的替代方案,您可以考虑使用不同的数据结构。我会把它放在dict

data = {'cat' : [0.3, 0.1],
        'dog' : [0.9, 0.5, 0.4],
        'fish' : [0.3, 0.2],
        'giraffe' : [0.8],
        # ...
        }

然后要访问,只需data['fish']即可[0.3, 0.2]

您只需执行一次

,即可将您拥有的数据加载到此类dict
data2 = {}
for label, datum in zip(labels,data):
    if label not in data2:
        data2[label] = []
    data2[label].append(datum)

然后为每个查询执行此操作

select = 'fish'
selected_data = data2[select]
selected_labels = [select] * len(selected_data)