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

Python 如何";切片;基于其中一个列表中的值的一对列表,python,list,Python,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, ... ] 如何根据标签列表中的特定标签并行提取两个列表的子列表 例如,使用fish作为选择标准,我希望生成: selected_labels = [ 'fish', 'fish' ]

我有两个长度相等的列表,一个包含
标签
,另一个包含
数据
。例如:

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, ... ]
如何根据
标签
列表中的特定标签并行提取两个列表的子列表

例如,使用
fish
作为选择标准,我希望生成:

selected_labels = [ 'fish', 'fish' ]
selected_data = [ 0.3, 0.2 ]
我的最佳猜测听起来很麻烦——制作一个元素元组列表,从该列表中提取一个相关元组列表,然后将该元组列表分解为两个单个元素列表。即使这是实现它的方法,我对Python还是太陌生了,不会对它的语法感到困惑

您可以将列表放在一起,根据要查找的关键字对其进行筛选,然后

然后您的
选定的\u数据
选定的\u标签
将是:

>>> selected_data = list(items[1])
>>> selected_labels = list(items[0])
另一种选择是使用函数获取所需的格式:

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

这可能是一个很好的应用程序,它比
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'表示标签中的i)
True
False
的生成器<代码>压缩向下过滤
数据
元素,以适应出现
True
的情况

从文档字符串:

大致相当于:

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)

作为<代码> zip < /Cord>应答的替代,您可以考虑使用不同的数据结构。我会把它放在一个

dict

data = {'cat' : [0.3, 0.1],
        'dog' : [0.9, 0.5, 0.4],
        'fish' : [0.3, 0.2],
        'giraffe' : [0.8],
        # ...
        }
然后要访问,只需
数据['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)
使用和可通过以下方式完成此操作:

代码: 这是怎么回事? tuples行构建了一个将两个列表压缩在一起并删除任何不感兴趣的内容的窗口。第二行再次使用zip,然后根据需要将生成的元组映射到列表中

这样做的优点是不构建中间数据结构,因此速度应该相当快,内存效率也很高

测试代码: 结果:
在这里,最简单的方法是完全正确的,并且可能非常有效:

>>> 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

除了
*
,我了解所有这些-您能提供此语法的参考以帮助新手吗?(
*
很难搜索…)不要到处对
列表进行不必要的调用,这会破坏使用这些迭代器的目的。我没有在新的数据结构中提供数据的奢侈。如果要避免向下投票,请指定进行转换的代码。另外,看起来很奇怪,即使有10000个
fish
数据项,我仍然需要第二个列表,其中包含10000个相同的
fish
标签。我可以提出一些建议:与其创建难以检查/打印的神秘对象
tuples=(…)
,不如改为
list\u of tuples=[…]
哪个更容易检查/打印,最终更容易理解?如果元组在某种程度上更好,也许可以解释一下原因。@omatai它是一个生成器,所以您不需要一次计算所有元组。好的-那么您的意思是,仅仅因为它包含在
()
中,它不会立即生成元组,它会创建一个生成器对象,在需要时生成元组?我想我明白了。理解列表解包仍然有困难,但是处理它…在这种特殊情况下,generator实际上没有多少回报。在
*元组中对其进行全面评估。在Numpy/pandas数据上进行迭代通常是不同的。Numpy有非常专业(矢量化)的方法来获得性能。建议你提出另一个具体问题。这个问题很受欢迎,我相信你可以提出另一个很好的问题。谢谢你,这绝对是最容易理解的答案。这就是说,看到一系列解决同一问题的方法是非常有用的,其中一些方法引入了更强大的概念。
['fish', 'fish']
[0.3, 0.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
>>> 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