我有两个长度相等的列表,一个包含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 ]
我最好的猜测听起来很麻烦 - 制作元素明智元组列表,从该列表中提取相关元组列表,然后将元组列表反序列化为两个单元素列表。即使这是接近它的方式,我也不太陌生,因为它偶然发现了语法。
答案 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)
是True
和False
的生成器。 compress
按data
元素向下过滤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_data
和selected_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)