【问题标题】:Accessing given element from each sublist of multidimensional list without for loop从多维列表的每个子列表中访问给定元素而不使用 for 循环
【发布时间】:2020-12-09 20:58:56
【问题描述】:

假设我有一个列表列表,例如:

list0 = [[a, .5, .3], [b, .3, .8], [c, .7, 1.3], [d, 1.03, .2]]

我想调用每个子列表的第二个元素以输出到我认为这可行的列表。

输入:print(list0[:][1])

输出:[b, .3, .8]

但我希望得到这个:(.5, .3, .7, 1.03)

有没有一种方法可以调用所有子列表并访问它们的元素,而无需遍历整个列表来创建新列表?你能比较一下时间并描述为什么我们不能调用所有或一系列子列表来从每个子列表中获取一个元素而不循环两次,一次是获取子列表,一次是访问每个子列表?

【问题讨论】:

  • list0[:] 只是制作列表的(浅)副本。它不是一个迭代器。您必须使用循环(或列表理解)来访问子列表。
  • 列表理解与仅循环遍历这个浅拷贝有何不同?如果您已经调用了每个子列表,它仍然有效,但仍然感觉效率低下,为什么不能同时调用那里的元素?
  • list0[:] 只是索引与切片的变体,list0[:2]。因为列表处理是我多年前从基本 Python 书籍中学到的,所以我不能直接向您指出文档。 “无循环”做事的想法属于numpy,而不是python列表。
  • 我意识到这是一个非常基本的问题,我准备深入了解数据类型的存储方式。为了快速回答,这种“没有循环”的方法是否仅适用于 numpy 列表或数组?

标签: python arrays list numpy


【解决方案1】:
In [196]: list0 = [['a', .5, .3], ['b', .3, .8], ['c', .7, 1.3], ['d', 1.03, .2]]

一些比较时间:

推荐的列表理解:

In [197]: timeit [l[1] for l in list0]
410 ns ± 17.6 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

从“转置”的列表版本开始:

In [198]: timeit list(zip(*list0))[1]
661 ns ± 3.63 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

从数组中选择一列:

In [199]: timeit np.array(list0)[:,1]
16 µs ± 177 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

numpy 问题经常问“没有循环”的原因是 numpy 数组上的 Python 级迭代很慢。在可能的情况下,我们希望使用快速编译的 numpy 代码(仍然有循环)。但是从列表开始,创建数组的成本相对较高。

从数组开始,列索引很快:

In [200]: %%timeit A = np.array(list0)
     ...: A[:,1]
325 ns ± 11.6 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

对数组的迭代比对列表的相同迭代慢:

In [201]: %%timeit A = np.array(list0)
     ...: [a[1] for a in A] 
5.47 µs ± 96.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

【讨论】:

  • 感谢您展示了 numpy 的可能性以及它的低效率。我想这与列表没有列和行的方式有关,当每个子列表可以是不同的长度时
  • 这回答了实际部分,但我仍然不确定为什么列表不能调用子列表中的每个元素,如 cols 和 rows?
  • 列表不是多维的。列表包含对其他对象的引用,这些对象本身可能是列表。但是外部列表没有任何对其元素执行操作的方法。在处理列表时,行和列等概念是没有意义的。
【解决方案2】:

只需使用列表推导式 -

second_element_of_each_sublist = [x[1] for x in list0]

或者,使用map 加上itemgetter -

from operator import itemgetter
second_element_of_each_sublist  = list(map(itemgetter(1), list0))

完全避免循环不会发生,至少如果您考虑到幕后可能发生的事情。这只是效率问题...

【讨论】:

  • 这仍然循环遍历子列表并创建一个新的,为什么我不能在调用所有或一系列子列表时调用子列表元素?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-08-01
  • 1970-01-01
  • 2020-08-15
  • 2020-11-14
  • 2017-11-06
  • 2020-08-02
  • 1970-01-01
相关资源
最近更新 更多