如果您希望通过课程系统地学习列表,建议查看我们的 数据科学 Python 入门 课程,其中有一章专门讲解 Python 列表。
背景
列表是刚开始学习 Python 以及已经有一定经验的开发者都越来越关注的话题。如果您相信 Google 趋势 的搜索结果,您会发现对此主题的搜索兴趣每年都在上升。
如果您经常访问一些用于提问或回答 Python 编程问题的论坛,比如 Stack Overflow、Quora 或 Reddit,您也许知道其中的原因。
许多 Python 问题都会到这些论坛上来,并持续发酵;用户会将部分问题标记为“重复”或“置顶”,为其投票,或者与他人讨论正确的解决方案。
通过这篇博文,DataCamp 希望以一种互动的方式,帮助您攻克一个主题:关于 Python 列表的常见问题。
Python 列表到底是什么?
列表是 Python 中四种内置数据结构之一,另外三种是 元组、字典和集合。列表用于存储有序的元素集合,这些元素可以是不同类型,但通常并不会这么用。列表中的元素以逗号分隔,并用方括号括起来。如下例所示:
# These list elements are all of the same type
zoo = ['bear', 'lion', 'panda', 'zebra']
print(zoo)
# But these list elements are not
biggerZoo = ['bear', 'lion', 'panda', 'zebra', ['chimpanzees', 'gorillas', 'orangutans', 'gibbons']]
print(biggerZoo)您会看到第二个变量 biggerZoo 与 zoo 类似,但同时也包含了另一个列表,其中列出了不同种类的猴子。
由于 Python 列表存储的是有序的元素或对象集合,我们可以说它们属于序列类型,因为它们的行为像一个序列。其他也被认为是序列类型的还有字符串和元组。
您也许会想,序列类型到底有什么特别之处?简单来说,这意味着程序可以对其进行迭代!这就是为什么列表、字符串、元组和集合被称为“可迭代对象”。
请记住,这个概念非常重要,您在其他用于数据科学的编程语言(例如 Scala)中也可能会再次遇到它!
现在进入正题!让我们深入探讨作为 Python 程序员,哪些关于列表的问题可能困扰过您。以下是本教程将回答的所有问题列表:
1. 何时使用 Python 列表,何时使用元组、字典或集合
只看介绍时似乎很直观,但当您真正编写一个小脚本或做一个项目时,在列表与其他序列类型之间做选择也许并不那么明确。
然而,为您的数据选择合适的数据结构至关重要!
继续阅读了解更多。
列表 vs 元组
元组用于收集不可变的有序元素列表。这意味着:
- 您不能向元组添加元素。元组没有
append()或extend()方法, - 您不能从元组中移除元素。元组没有
remove()或pop()方法, - 您可以在元组中查找元素,因为这不会改变元组。
- 您也可以使用
in运算符检查某元素是否存在于元组中。
因此,如果您要定义一组常量值,且只会对其进行迭代,使用元组而不是列表。它比列表更快,也更安全,因为元组的数据是“写保护”的。
列表 vs 字典
- 列表存储有序的元素集合,因此保留顺序。字典则不保证顺序。
- 字典是键到值的映射,而列表只包含值。
当您有一组无序且唯一的键映射到值时,使用字典。
注意,由于键和值彼此关联,在检查元素成员资格时,字典的性能通常优于列表。
列表 vs 集合
- 与字典类似,集合中元素是无序的,而列表是有序的。
- 集合要求其包含的元素可哈希,列表则可以存储不可哈希的元素。
- 集合要求元素唯一且不可变。集合不允许重复元素,而列表允许重复且可变。
当您有一组无序、唯一、不可变且可哈希的值时,应使用集合。
不确定哪些值是可哈希的?
请看下表以确认:
| 可哈希 | 不可哈希 |
|---|---|
| 浮点数 | 字典 |
| 整数 | 集合 |
| 元组 | 列表 |
| 字符串 | |
frozenset() |
提示:不要只听我们说!在下方自己动手试试,并在 IPython shell 中多做实验 :)
# Import the `collections` library
____ ____
# Check if a dictionary is hashable
print(isinstance({}, collections.Hashable))
# Check if a float is hashable
print(isinstance(0.125, collections.Hashable))注意,由于您使用的是可哈希元素,在检查成员资格(测试某个元素是否属于序列)时,集合通常会比列表更快。
2. 如何从列表中选取元素
如果您想要正确地使用这些列表,就需要知道如何访问它们。
您通常会访问列表来更改、更新或删除某些值,或对其执行其他操作。访问列表以及所有其他序列类型的方式,是使用索引运算符 [ ]。方括号中放入一个整数值。
来看一个示例:
# Select the first list element
oneZooAnimal = biggerZoo[0]
# Print `oneZooAnimal`
print(____)运行代码看看返回了什么。您会注意到返回的是 biggerZoo 的第一个元素。尽管起初可能有点令人困惑或意外,但索引是从 0 开始而不是 1。
如何获取列表中的最后一个元素
这个问题是在上一小节说明的补充。
尝试在索引运算符中使用负数,比如 -1 或 -2,以检索 biggerZoo 列表的最后一个元素!
# Pass -1 to the index operator on `biggerZoo`
monkeys = ___
print(monkeys)
# Pass -2 to the index operator on `biggerZoo`
zebra = ___
print(zebra)并不难,对吧?
“Index out of range” 错误是什么意思?
这个错误您会经常见到,尤其当您是编程或 Python 新手时。
理解该错误最好的方式就是自己试一下。
使用上面的交互式学习挂件,尝试传入特别小的负数或特别大的正数!
如您所见,当您传递给索引运算符的整数大于或远小于列表长度时,可能会出现 “Index Out Of Range” 错误!这意味着您引用了一个不存在(尚未存在)的索引或在该位置赋值。
下面这段代码会触发 “Index Out Of Range” 错误。运行它亲自看看!
# Run this code to trigger an "Index Out Of Range" Error
biggerZoo[6]列表中的切片表示法
当您刚接触编程或 Python 时,这可能是一个特别容易混淆的话题。
一般来说,当您想一次选择多个列表元素时,就会用到切片表示法。与只从列表中选择一个元素相似,您会使用方括号。特别之处在于,此时除了整数之外,您还要在方括号内放一个 :,像这样:
# Use the slice notation like this
someZooAnimals = biggerZoo[2: ]
# Print to see what you exactly select from `biggerZoo`
print(someZooAnimals)
# Try putting 2 on the other side of the colon
otherZooAnimals = biggerZoo[____]
# Print to see what you're getting back
print(otherZooAnimals)您会发现,我们得到了 biggerZoo 列表中从索引 2 的 panda 开始到末尾的部分。换言之,我们从索引 2 开始,一直到列表末尾,因为没有指定结束索引。
如果反过来会怎样呢?
运行上面的代码块看看您是否猜对了!
您会看到现在只返回了 bear 和 lion。现在我们从索引 0 开始到索引 2。与预期相反,结果不包含 panda。
一般而言,遵循以下规则:
# items start through the end (but the end is not included!)
a[start:end]
# items start through the rest of the array
a[start:]
# items from the beginning through the end (but the end is not included!)
a[:end]
提示:只传入一个冒号 :,就会复制整个列表!
除了基础的切片表示法外,还有步长(step)值,可以概括为:
# start through not past end, by step
a[start:end:step]
那么步长到底是做什么的?
它允许您按步长“跳跃”地遍历列表,只选择被步长覆盖到的那些元素。例如:
# Print to see how the step value influences your result
print(biggerZoo[2::2])
print(biggerZoo[1::3])注意,如果不指定步长,默认值为 1。此时遍历列表时不会跳过任何元素。
请记住,切片表示法中指定的结束索引本身不包含在结果中!
如何随机选择列表中的元素
您可以使用 random 包从列表中随机选择一个元素:
# Import `choice` from the `random` library
from ____ import ____
# Construct your `list` variable with a list of the first 4 letters of the alphabet
list = ['_','_','_','_']
# Print your random 'list' element
print(choice(list))如果您还想通过列表的索引来随机选择一个元素,可以使用 random 库中的 randrange 方法:
# Import `randrange` from the `random` library
from ____ import ____
# Construct your `randomLetters` variable with a list of the first 4 letters of the alphabet
randomLetters = ['_','_', '_', '_']
# Select a random index from 'randomLetters`
randomIndex = randrange(0,len(randomLetters))
# Print your random element from `random`
print(randomLetters[randomIndex])提示:建议了解一下 random 库,在 Python 编程时会很有用!
3. 如何将 Python 列表转换为其他数据结构
有时,列表并不是您所需要的。在这些情况下,您可能希望知道如何将列表转换为更合适的数据结构。下面列出了一些最常见的转换问题及其答案。
如何将列表转换为字符串
可以使用 ''.join() 将列表转换为字符串。该操作会把列表中的所有字符串拼接起来并返回一个字符串。更多信息见此处。
# List of Strings to a String
listOfStrings = ['One', 'Two', 'Three']
strOfStrings = ''.join(listOfStrings)
print(strOfStrings)
# List Of Integers to a String
listOfNumbers = [1, 2, 3]
strOfNumbers = ''.join(str(n) for n in listOfNumbers)
print(strOfNumbers)注意,如果列表只包含整数,则应先将元素转换为字符串,再进行拼接。上方交互式代码块的第二个示例演示了这一点。
从整数到字符串的转换,是通过遍历原始 listOfNumbers 列表完成的。如需进一步了解如何遍历列表,请查看问题 12 及其答案。
如何将列表转换为元组
在 Python 中可以使用 tuple() 函数将列表转换为元组。把列表作为参数传递给它,您就会得到一个元组!
请记住:元组是不可变的。转换后您无法再修改它!
提示:您可以在下一节的交互练习中尝试将 listOfStrings 转换为元组。
如何将列表转换为集合
如您所记得,集合是无序的唯一元素集合。这意味着,一旦将列表转换为集合,原列表中的任何重复元素都会被去除,而且列表元素的顺序也会丢失。
可以使用 set() 函数将列表转换为集合。只需把列表传给它即可!
现在在这里练习将 listOfStrings 转换为元组和集合:
# Pass your list to `tuple()`
tuple(___)
# Transform your list into a set
set(___)如何将列表转换为字典
字典使用键和值,因此从列表到字典的转换可能没那么直接。假设您有一个这样的列表:
helloWorld = ['hello','world','1','2']
您需要确保 'hello' 与 'world',以及 '1' 与 '2' 被解释为键值对。做法是用切片选出它们并传给 zip()。
zip() 的作用正如其名:把元素拉链式地配对。在本例中,当您对 helloWorld[0::2] 和 helloWorld[1::2] 做 zip 时,输出将是:
list(zip(helloWorld))注意,需要用 list() 才能打印出 zip() 函数的结果。
接着将其传递给 dict() 函数,hello 会被解释为键,world 为值。同样,1 为键,2 为值。
运行下方代码确认一下:
# Convert to a dictionary
helloWorldDictionary = dict(zip(helloWorld[0::2], helloWorld[1::2]))
# Print out the result
print(helloWorldDictionary)注意,传给 zip() 的第二个元素使用了步长值,以确保只选择 world 和 2。同理,第一个元素的步长选择了 hello 和 1。
如果列表很大,您可能会用类似下面的方法:
a = [1, 2, 3, 4, 5]
# Create a list iterator object
i = iter(a)
# Zip and create a dictionary
print(dict(zip(i, i)))注意,可迭代对象可以生成迭代器。迭代器具有 .__next__ 方法,保存了迭代中的当前位置:它知道下一个元素是什么。
4. 如何确定列表的大小
可以将列表传给 len() 函数以获得列表长度。
在下面试一试!我们已经为您准备了一个 justAList 列表可供使用。
# Pass `justAList` to `len()`
len(____)注意,len() 并不仅用于获取列表长度,也可用于其他序列或集合,例如字典、集合、字符串等。更多内置函数见这里。
5. Python 的 append() 与 extend() 有何区别?
先回顾一下开头提到的“可迭代对象”概念:
请记住,当程序可以对某个值进行迭代时,我们称其为可迭代。换言之,可迭代对象代表一个或多个值构成的序列。正如您在第一节中看到的,列表是序列类型,Python 中所有序列类型的实例都是可迭代的。
提示:您可以检查变量是否可迭代——看它是否有 .__iter__ 方法,例如下面这段代码:
# This is your list
list = [5, 6, 7, 8, 9]
# Check whether it's iterable
list.__iter__运行上面的代码,并在 IPython shell 中试试其他简单示例!
现在可迭代对象的概念已经清晰了,我们可以开始理解这两个方法的区别。extend() 接受一个可迭代对象(没错,它可以是列表、集合、元组或字符串!),并将该可迭代对象中的每个元素依次添加到列表中。
而 append() 会将其参数作为单个元素追加到列表末尾,这意味着当 append() 的参数是可迭代对象时,它会把整个对象当作一个元素处理。
学习下面的代码块,并自己动手尝试,以理解这两个方法的差异:
# Append [4,5] to `shortList`
shortList.append([4, 5])
# Use the print() method to show shortList
print(____)
# Extend `longerList` with [4,5]
longerList.extend([4, 5])
# Use the print() method to see longerList
print(____)您可以看到,extend() 比多次调用 append() 更高效。
6. 如何拼接列表
要拼接列表,使用 + 运算符。它会返回一个新的列表,包含两个列表的拼接结果,并且不会修改原列表。
注意,拼接与追加可能会让人困惑。append() 与 + 运算符不同,而 + 与 extend() 又很相似,但并不完全相同……使用 append() 和 extend() 会修改原列表,而 + 运算符可以创建一个新的列表变量。
自己试试,并与上一代码块中的示例进行比较:
# Concatenate `shortList` with `[4,5]`
plusList = shortList + [4,5]
#Use the `print()` method to see `plusList`
print(____)7. 如何对列表排序
有两种非常简单的方法可以将列表中的值按升序或降序排序:
- 使用
sort()方法 - 或使用
sorted()函数并将列表传给它
sorted() 可以应用于任何可迭代对象,这意味着它也接受字符串、集合和字典(当它们被传入时)!
提示:自己来试试吧!我们已经创建了两个列表 rooms 和 orders,您可以用上面两种技术进行排序 :)
# Use sort() on the rooms list
____.sort()
# Print out rooms to see the result
print(____)
# Now use the sorted() function on orders
sorted(____)
# Print out orders
print(____)注意,排序后原列表会被修改。建议在不再需要原列表时使用这种方式。
8. 如何克隆或复制列表
复制或克隆列表的方法很多:
- 您可以切片原列表并存入新变量:
newList = oldList[:] - 可以使用内置的
list()函数:newList = list(oldList) - 可以使用
copy库:- 使用
copy()方法:newList = copy.copy(oldList) - 如果列表包含对象且您希望也复制这些对象,可以使用
copy.deepcopy():copy.deepcopy(oldList)
- 使用
在下面试试吧!我们已经创建了一个 groceries 列表,您可以复制并分享给朋友。
# Copy the grocery list by slicing and store it in the `newGroceries` variable
newGroceries = groceries[__]
# Copy the grocery list with the `list()` function and store it in a `groceriesForFriends` variable
groceriesForFriends = list(____)
# Import the copy library as c
import ____ as __
# Create a `groceriesForFamily` variable and assign the copied grocery list to it
groceriesForFamily = c.copy(____)
# Use `deepcopy()` and assign the copied list to a `groceriesForKids` variable
groceriesForKids= c.deepcopy(____)注意,当您使用“简单”的复制方法时,原列表会被修改;而使用 deepcopy() 方法则可以避免这种情况。运行下面的代码看看会发生什么:
# This is your list
objectList = ['a','b',['ab','ba']]
# Copy the `objectList`
copiedList = objectList[:]
# Change the first list element of `copiedList`
copiedList[0] = 'c'
# Go to the third element (the nested list) and change the second element
copiedList[2][1] = 'd'
# Print out the original list to see what happened to it
print(objectList)如果您已经把 Python 列表的知识提升到了更深入的层次,下面的章节会为您的一些复杂问题提供简洁的答案。
不过,如果您想知道除了列表之外还有什么,或是内置列表结构之外的库能提供哪些优势,下一节可能更适合您。
继续阅读,了解更多!
9. Python 中的列表推导式如何工作?
简单来说,列表推导式是一种优雅构造列表的方式。对于热爱数学的人来说,最棒的是它看起来很像数学中的集合/列表。自己感受一下:
[x**2 for x in range(10)]这是什么意思呢?您取 0 到 10(不含 10)之间的所有数字,并将其中每个数字都乘以 2。运行上面的代码看看结果是否符合预期。
并不难,对吧?我们再来进阶一点!
假设您想构造与之前相同的列表,但只想把能被 2 整除的数字拿来乘 2。有了列表推导式,这也很简单:
[x**2 for x in range(10) if x%2==0]您只需在原表达式中添加一个条件,用 if 表示。如果条件为 TRUE,就会将该数字乘以 2。在这里,我们的条件是数字能够被 2 整除(即除法运算没有余数)。
从更一般的角度说,您也可以使用列表推导式将一个列表转换为另一个列表,听起来更难,对吗?
看看下面的例子:
myList = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[(lambda x: x*x)(x) for x in myList]在这个例子中,您已经有了一个列表 myList,并通过列表推导式对其进行转换:再次看到 for x in myList,意味着对 myList 中的每个元素执行某些操作。
接着,您看到包含了 (lambda x: x*x)。为简单起见,您只需知道 lambda 函数是运行时构造的匿名函数。把那段代码视为一个提示,您可以先忽略它,关注 x*x 即可。
这个函数取一个元素并与自身相乘。由于是匿名函数,您需要把列表元素传给它,确保该 lambda 函数把这个元素作为输入。这就是为什么您看到 (lambda x: x*x)(x)。
换句话说,您可以把上面代码块的第二行改写为:
f = lambda x: x*x
[f(x) for x in range(10)]
其中,您首先定义了一个将元素与自身相乘的 lambda 函数,然后在第二行将列表元素传给它,确保每个列表元素都与自身相乘。
注意,除了列表推导式,还有集合与字典推导式。
这已经相当丰富了,但关于列表推导式还有更多值得探索的内容。
继续阅读以下常见问题,了解嵌套列表推导式以及更多实用示例,看看它在 Python 编程中的意义!
10. 如何统计列表项出现的次数
由于列表不像集合那样要求元素唯一,您可能不仅想知道某个特定列表项在列表中出现了多少次,还想知道每个列表项各自出现了多少次。
下面您会找到这两个问题的答案!
统计某个元素在列表中的出现次数
要统计单个列表项的出现次数,可以使用 count() 方法:
# Count the occurrences of the number 4
print([1, 2, 9, 4, 5, 4, 1].count(4))
# Count the occurrences of the letter "a"
list = ["d", "a", "t", "a", "c", "a", "m", "p"]
list.count("a")统计所有列表项的出现次数也称为“计数”列表,或创建一个计数器。
结合 count() 统计所有列表项
要统计列表中各元素的出现次数,也可以将列表推导式与 count() 结合使用:
list= ["a","b","b"]
[[x,list.count(x)] for x in set(list)]在这段代码中,首先将列表转换为集合。这会确保只保留唯一的列表项,即 a 和 b。然后,您对集合中的每个元素,传入 count() 方法。
请记住,集合只包含唯一元素!
在这个例子中,首先会将 a 传给 list.count(x),并统计原列表中所有 a 的出现次数。
使用 Counter() 统计所有列表项
另外,也可以使用更快的 collections 库中的 Counter() 方法:
# Import `Counter` from the `collections` library
from collections import Counter
# This is your list
list = ["a","b","b"]
# Pass `list` to `Counter()`
Counter(list)注意,当要统计所有列表项时,Counter() 通常更快。
11. 如何将列表均匀切分为若干块
要把列表按相同大小切分为若干部分,可以将 zip() 与 iter() 结合使用:
# Your list `x`
x = [1,2,3,4,5,6,7,8,9]
# Split `x` up in chunks of 3
y = zip(*[iter(x)]*3)
# Use `list()` to print the result of `zip()`
list(y)上述代码的工作方式如下:
iter()是对一个序列的迭代器。[iter(x)] * 3会生成一个包含三个listiterator对象的列表:每个迭代器都迭代x。- 传给
zip()函数前面的*会把序列解包为参数,于是您向zip()三次传入了相同的迭代器,zip()每次从迭代器中取一个元素。
等等。最后一步可能还是不清楚。
我一步步来说明:
- 您会有三个列表迭代器对象,可以把它们想象为:
[1,2,3,4,5,6,7,8,9], [1,2,3,4,5,6,7,8,9], [1,2,3,4,5,6,7,8,9]
- 第一次,
zip()会按顺序各取一个元素,于是得到:
[1][2][3]
注意,迭代器对象会为您记录下一个要迭代的元素!
- 第二次,会把元素添加到刚创建的三个列表中,得到:
[1, 4], [2, 5], [3, 6]
- 最后一次,重复相同步骤,得到:
[1, 4, 7], [2, 5, 8], [3, 6, 9]
- 将这三个列表再拉链在一起,就得到:
[1, 2, 3], [4, 5, 6], [7, 8, 9]
如果您决定把这套逻辑封装成函数,可以按以下方式实现:
# Method to split up your lists into chunks
def chunks(list, chunkSize):
"""Yield successive chunkSize-sized chunks from list."""
for i n range(0, len(list), chunkSize):
yield list[i:i + chunkSize]
# Use your `chunks` function to print out chunks of the same size
import pprint
pprint.print(list(chunks(range(10, 75), 10)))chunks() 函数接受列表和块大小作为输入。它会打印:“从列表中连续产出(您的块大小)大小的块”。
然后,range(0, len(list), chunkSize):该序列从 0 开始,到列表长度结束,步长设置为传入 chunks() 的 chunkSize。
对 range() 生成的每个元素,您会从原列表中产出一个切片,从当前索引开始,到索引 + 块大小结束。
例如,如果索引 0 通过 for 循环,就会得到列表的 [0:2];在下一次迭代中,索引 2 通过 for 循环(注意步长!),会输出原列表的 [2:4] 这一块。
注意,这里使用了 pprint()(pretty print)来打印结果。
您也可以用列表推导式来代替编写函数:
12. 如何遍历列表
您可以很容易地用 for 循环遍历任何列表。对于不太熟悉 for 循环的读者,它的工作方式是:for 循环允许您对列表中的每个元素执行某个操作。
在 Python 中,for 循环看起来像这样:
# This is your list
mylist = [[1,2,3],[4,5,6,7],[8,9,10]]
# Loop over your list and print all elements that are of size 3
for x in mylist:
if len(x)==3:
print(x)您有一个列表 mylist,并告诉 Python 应检查每个列表元素的长度是否等于 3。由于我们的列表中包含两个长度为 3 的子列表,即 [1,2,3] 和 [8, 9,10],因此只有它们会被打印出来。
注意,上面的代码中 x 也可以改为 element、listElement 或任何您喜欢的名字。到控制台里试试!
您会注意到,这里的结果并未显示元素的索引。如果您希望通过索引来遍历列表,可以这样做:
# This is your list
myList = [3,4,5,6]
# Loop over `myList` and print tuples of all indices and values
for i, val in enumerate(myList):
print(i, val)因为您想打印出列表元素及其索引,当然需要在 for 循环中体现。这就是我们使用 enumerate(myList) 的原因,它会返回索引和值。自然地,我们也要在 for 语句开头显示这一点:您会看到 i, val,以表明我们期望得到索引和值。
注意,您也可以用列表推导式来遍历列表。在这里,结果会直接打印出来,无需再调用 print():
[x for x in myList if len(x)==3]13. 如何将“列表的列表”拍平成一维列表
要把一个列表的列表拍平成简单列表,您可以使用 sum() 函数。别忘了传入列表以及一个空列表作为起始值:
# Your initial list of lists
list = [[1,2],[3,4],[5,6]]
# Flatten out your original list of lists with `sum()`
sum(list, [])您也可以用另外两种方法来拍平列表:
# You can reduce the lists of lists of lists like this
from functools import reduce
print(reduce(lambda x,y: x+y,listOfLists))
# Or you can use list comprehension
print([item for sublist in listOfLists for item in sublist])- 可以使用
reduce()方法,并传入一个 lambda 函数。reduce()的作用是把可迭代对象“归约”为一个值。以何种方式归约,则由您传入的函数来决定。在这个例子中,您希望累加求和:也就是把[1,2]加到[3,4]上,然后再把结果加到[5,6]上。可以用下式表示:
([1,2]+[3,4])+[5,6]
请记住,+ 运算符会拼接列表。
- 第二种方法是使用列表推导式。这种嵌套的 for 循环写法不算直观。本质上,它先取
list中的每个sublist,也就是[1, 2]、[3, 4]、[5, 6],然后再对每个sublist里的item进行遍历并逐一输出。
另一种理解嵌套列表推导式的方式,是把它改写为常规的 for 循环:
list1 = []
for sublist in list:
for item in sublist:
list1.append(item)
14. 如何求两个列表的交集
使用列表推导式求交集
如果想获得两个列表的交集,可以使用 filter() 函数。
下方练习进行了演示。我们已为您定义了两个列表 list1 和 list2。如需查看内容,请使用 IPython shell。
# Intersect both lists with list comprehension
intersection = [list(filter(lambda x: x in list1, sublist)) for sublist in list2]
# Print the result of the intersection
print(____)在上面的代码中,filter() 会取每个子列表的元素并检查是否存在于源列表 list1 中。列表推导式则对 list2 中的每个子列表执行这一过程。
注意,由于 filter() 返回的是可迭代对象而不是列表,因此需要用 list() 包裹传给 filter() 的内容。试着去掉 list() 自己感受一下!
如果想提高可读性,您也可以使用嵌套列表推导式:
# An intersection of both lists, stored in `intersection`
intersection = [[x for x in sublist if x in list1] for sublist in list2]
# Print the result of the intersection
print(intersection)使用 set() 求交集
如果不关心元素的顺序,也不需要考虑重复元素,那么可以使用集合的交集:
# Make use of the list and set data structures
print(list(set(list1) & set(list2)))
# Use `intersection()`
print(list(set(list1).intersection(list2)))在第一个例子中,您将两个列表转换为集合,求交后再转换回列表。输出列表中的顺序将是任意的。您也可以将两个列表中更大的那个转换为集合,然后使用 intersection() 与任何可迭代对象求交集。
注意,要使这两种方法有效,列表中不应包含其他列表。这也是 list2 看起来不同的原因。打印出来确认一下!
15. 如何从列表中移除重复元素
如本博文第一节所述,如果您要存储唯一且无序的元素集合,最好使用集合。
要从任何可迭代对象创建集合,只需将其传给内置的 set() 函数。如果之后需要重新得到列表,可以同样将集合传给 list() 函数。
请记住,可迭代对象可以是列表、字典等……您可以通过调用 .__iter__ 方法来检查一个变量是否为可迭代对象。
下面的示例清晰地演示了如何使用 set() 删除列表中的所有重复元素:
# Your list with duplicate values
duplicates = [1, 2, 3, 1, 2, 5, 6, 7, 8]
# Print the unique `duplicates` list
print(list(set(duplicates)))
# A list with small numbers
smallNumbers = [1, 2, 3]
# Print the unique `duplicates` list without the small numbers
list(set(duplicates) - set(smallNumbers))如您所见,在最后一个结果中,当您从 smallNumbers 的集合元素中做差后,原列表的顺序不再保留。正如您现在所知道的,集合本身是无序的,因此列表中原有的顺序会丢失。当您把集合转换回列表时,会得到一个任意的顺序。
如果元素顺序对您很重要,那么您需要使用其他机制。此页面对此有更详细的讨论。
16. 为什么选择 NumPy 而不是 Python 列表?
总体而言,Python 程序员更喜欢使用 NumPy 数组而不是 Python 列表有四个原因:
- NumPy 数组比列表更紧凑。
- 读取与写入元素时,NumPy 访问更快。
- NumPy 更便于使用,因为很多向量与矩阵运算都“开箱即用”。
- NumPy 的实现更高效,因此使用起来更高效。
17. 如何创建空的 NumPy 数组
如果您只想创建一个空的 NumPy 数组,可以执行以下代码:
import numpy
numpy.array([])不过,如果您的目标是初始化一个 NumPy 数组,也可以使用以下方法之一:
# Make a NumPy array of four rows and two columns and filled with 0
numpy.zeros(shape=(4,2))
# Make a NumPy array of 1 values of three columns
numpy.ones(3)
# Make an empty NumPy array
numpy.empty(shape=(0,0))如果您在寻找其他初始化 NumPy 数组的方法,请查看这份文档。
18. 如何用列表做数学运算
很多时候,我们不仅用列表来存储一组值,还会对其执行一些数学运算。本节将涵盖关于在列表上进行数学运算的常见问题。
如何计算加权平均数
加权平均与普通平均类似,但略有不同:加权平均取决于数值与权重这两个变量。
这个定义起初可能有点抽象。让我们看一个例子:
cost = [0.424, 0.4221, 0.4185, 0.4132, 0.413]
cases = [10, 20, 30, 40, 50]
您可以用下面的代码轻松计算加权平均:
for c in range(len(cost)):
cost[c] = (cost[c] * cases[c] / sum(cases))
cost = sum(cost)
print(cost)当然,您也可以选择以下两种方式之一。首先是:
sum(cost[c] * cases[c] / sum(cases) for c in range(len(cost)))或者使用这个:
sum(cost[c] * cases[c] for c in range(len(cost))) / sum(cases)最后,您还可以借助 zip() 函数来计算加权平均。
提示:创建一个变量 costAndCases,将 zip() 函数赋给它,并把 cost 和 case 变量传入。您会看到创建了一个只包含元组的新列表。每个元组的第一个值表示 cost,第二个值表示对应的 cases。
# See what `zip()`d does to your `cost` and `cases`
print(list(zip(____, ____)))
# Calculate the weighted average
print(sum([x * y for x, y in zip(cost, cases)]) / sum(cases))请记住,zip() 对列表做了什么?很简单,就是把两个列表“拉链”在一起。例如,zip(cost, cases) 会输出 [(0.424, 10), (0.4221, 20), (0.4185, 30), (0.4132, 40), (0.413, 50)]。
如何计算分位数
在对数据集做摘要时,除了最小值与最大值外,分位数也至关重要。常用的有第 25、50、75 百分位数,也称为第一四分位数、中位数和第三四分位数。
这意味着,概括整个数据集需要五个数字:最小值、最大值、中位数和两个四分位数。
数据集的最小值和最大值很容易找到,但那些百分位数怎么求呢?
很简单。假设数据集中从小到大排序后有 25 个样本:
- 第 25 百分位(第一四分位)通过 0.25 × 25 计算得到。结果为 6.25,向上取整为 7,这表示第 7 个数即为第 25 百分位数。
- 第 75 百分位(第三四分位)通过 0.75 × 25 计算得到。结果为 18.75,向上取整为 19,表示第 19 个数是第 75 百分位数。
- 中位数通过 0.5 × 25 计算得到。因为 12.5 向上取整为 13,故第 13 个数是中位数。
但如何将其编程实现呢?
最简单的方法是使用 NumPy。按照下面交互练习中的说明来计算列表的百分位数:
# Import numpy as np
import ____ as __
# Make a NumPy array
a = np.array([1,2,3,4,5])
# Return the 50th percentile of our NumPy array
p = np.percentile(a, 50)
#Print the result
print(____)如何对两个列表做逐元素求和
假设您有两个列表:
list1=[1, 2, 3]
list2=[4, 5, 6]
您希望按元素位置一一相加:也就是 1+4、2+5、3+6。您有两种实现方法。
继续阅读了解更多!
……使用原生 Python
可以结合 operator.add 使用 map 来执行逐元素相加:
from operator import add
list(map(add, list1, list2))结果将是列表 [5, 7, 9]。
注意,需要使用 list() 才能打印出 map() 的结果。
或者,您可以将 zip() 与列表推导式结合使用:
[sum(x) for x in zip(list1, list2)]……使用 NumPy
前面的示例对这些小列表很合适,但当您处理的数据稍多一些时,最好使用 numpy 让过程更简单。
此时应确保先导入 numpy 库,并将列表转换为 NumPy 数组:
# Import numpy as np
import ____ as __
# Make your lists into NumPy arrays
vector1 = np.array([1, 2, 3])
vector2 = np.array([4, 5, 6])
# Element-wise addition
sum_vector = vector1 + vector2
# Print the result
print(____)您在 Python 数据科学之旅的下一步?
您已经完成了我们关于 Python 列表的交互式教程。内容不少、工作量也不小,但您做到了!
列表只是您 Python 数据科学之旅中一个小但必不可少的组成部分。还有太多值得探索!我们的交互式 数据科学 Python 入门 课程非常适合想进一步了解 Python 及其在数据科学中用法的读者:课程从 Python 的最基础开始,循序渐进地向您介绍 NumPy,将您温和地带到中级水平。非常适合在(Python)编程方面经验不多的读者!
如果您已经是更进阶的 Python 用户,可以考虑查看我们的 中级 Python:面向数据科学 课程,专为准备将数据科学与 Python 技能更上一层楼的学习者设计。本课程会进一步提升您的数据科学学习!课程将带您了解更多库(如 matplotlib 和 pandas)、概念(如逻辑、控制流与筛选),并包含一个数据科学案例研究,帮助您巩固技能。
您已经在用 Python 做数据科学,但还想走得更远?不妨看看我们的 Python 数据导入 课程,或阅读我们的 数据科学与机器学习流程中预处理的重要性 系列教程!
本文将提供简单的解决方案、易于理解的直观解释,以及一些您可以借助交互式练习即刻上手的技巧!