Python 数据分析(PYDA)第三版(一)(3)

简介: Python 数据分析(PYDA)第三版(一)

Python 数据分析(PYDA)第三版(一)(2)https://developer.aliyun.com/article/1482369


三、内置数据结构、函数和文件

原文:wesmckinney.com/book/python-builtin

译者:飞龙

协议:CC BY-NC-SA 4.0

此开放访问网络版本的《Python 数据分析第三版》现已作为印刷版和数字版的伴侣提供。如果您发现任何勘误,请在此处报告。请注意,由 Quarto 生成的本站点的某些方面与 O’Reilly 的印刷版和电子书版本的格式不同。

如果您发现本书的在线版本有用,请考虑订购纸质版无 DRM 的电子书以支持作者。本网站的内容不得复制或再生产。代码示例采用 MIT 许可,可在 GitHub 或 Gitee 上找到。

本章讨论了内置到 Python 语言中的功能,这些功能将在整本书中被广泛使用。虽然像 pandas 和 NumPy 这样的附加库为更大的数据集添加了高级计算功能,但它们旨在与 Python 的内置数据操作工具一起使用。

我们将从 Python 的主要数据结构开始:元组、列表、字典和集合。然后,我们将讨论如何创建自己可重用的 Python 函数。最后,我们将看看 Python 文件对象的机制以及如何与本地硬盘交互。

3.1 数据结构和序列

Python 的数据结构简单而强大。掌握它们的使用是成为熟练的 Python 程序员的关键部分。我们从元组、列表和字典开始,它们是一些最常用的序列类型。

元组

元组是 Python 对象的固定长度、不可变序列,一旦分配,就无法更改。创建元组的最简单方法是使用括号括起的逗号分隔的值序列:

In [2]: tup = (4, 5, 6)
In [3]: tup
Out[3]: (4, 5, 6)

在许多情况下,括号可以省略,所以这里我们也可以这样写:

In [4]: tup = 4, 5, 6
In [5]: tup
Out[5]: (4, 5, 6)

您可以通过调用tuple将任何序列或迭代器转换为元组:

In [6]: tuple([4, 0, 2])
Out[6]: (4, 0, 2)
In [7]: tup = tuple('string')
In [8]: tup
Out[8]: ('s', 't', 'r', 'i', 'n', 'g')

元素可以使用方括号[]访问,就像大多数其他序列类型一样。与 C、C++、Java 和许多其他语言一样,在 Python 中,序列是从 0 开始索引的:

In [9]: tup[0]
Out[9]: 's'

当您在更复杂的表达式中定义元组时,通常需要将值括在括号中,就像在创建元组的示例中一样:

In [10]: nested_tup = (4, 5, 6), (7, 8)
In [11]: nested_tup
Out[11]: ((4, 5, 6), (7, 8))
In [12]: nested_tup[0]
Out[12]: (4, 5, 6)
In [13]: nested_tup[1]
Out[13]: (7, 8)

虽然存储在元组中的对象本身可能是可变的,但一旦创建了元组,就无法修改存储在每个槽中的对象:

In [14]: tup = tuple(['foo', [1, 2], True])
In [15]: tup[2] = False
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-15-b89d0c4ae599> in <module>
----> 1 tup[2] = False
TypeError: 'tuple' object does not support item assignment

如果元组中的对象是可变的,比如列表,您可以就地修改它:

In [16]: tup[1].append(3)
In [17]: tup
Out[17]: ('foo', [1, 2, 3], True)

您可以使用+运算符连接元组以生成更长的元组:

In [18]: (4, None, 'foo') + (6, 0) + ('bar',)
Out[18]: (4, None, 'foo', 6, 0, 'bar')

将元组乘以一个整数,与列表一样,会产生该元组的多个副本的效果:

In [19]: ('foo', 'bar') * 4
Out[19]: ('foo', 'bar', 'foo', 'bar', 'foo', 'bar', 'foo', 'bar')

请注意,对象本身并没有被复制,只有对它们的引用。

解包元组

如果您尝试对类似元组的变量表达式进行赋值,Python 将尝试在等号右侧解包值:

In [20]: tup = (4, 5, 6)
In [21]: a, b, c = tup
In [22]: b
Out[22]: 5

即使包含嵌套元组的序列也可以解包:

In [23]: tup = 4, 5, (6, 7)
In [24]: a, b, (c, d) = tup
In [25]: d
Out[25]: 7

使用这个功能,您可以轻松交换变量名,这在许多语言中可能看起来像:

tmp = a
a = b
b = tmp

但是,在 Python 中,交换可以这样做:

In [26]: a, b = 1, 2
In [27]: a
Out[27]: 1
In [28]: b
Out[28]: 2
In [29]: b, a = a, b
In [30]: a
Out[30]: 2
In [31]: b
Out[31]: 1

变量解包的常见用途是迭代元组或列表的序列:

In [32]: seq = [(1, 2, 3), (4, 5, 6), (7, 8, 9)]
In [33]: for a, b, c in seq:
 ....:     print(f'a={a}, b={b}, c={c}')
a=1, b=2, c=3
a=4, b=5, c=6
a=7, b=8, c=9

另一个常见用途是从函数返回多个值。我稍后会更详细地介绍这个问题。

有一些情况下,您可能希望从元组的开头“摘取”一些元素。有一种特殊的语法可以做到这一点,*rest,这也用于函数签名中捕获任意长的位置参数:

In [34]: values = 1, 2, 3, 4, 5
In [35]: a, b, *rest = values
In [36]: a
Out[36]: 1
In [37]: b
Out[37]: 2
In [38]: rest
Out[38]: [3, 4, 5]

这个rest位有时是您想要丢弃的内容;rest名称没有特殊之处。作为一种惯例,许多 Python 程序员会使用下划线(_)表示不需要的变量:

In [39]: a, b, *_ = values
元组方法

由于元组的大小和内容不能被修改,因此实例方法非常少。一个特别有用的方法(也适用于列表)是count,它计算值的出现次数:

In [40]: a = (1, 2, 2, 2, 3, 4, 2)
In [41]: a.count(2)
Out[41]: 4

列表

与元组相反,列表是可变长度的,其内容可以就地修改。列表是可变的。您可以使用方括号[]定义它们,也可以使用list类型函数:

In [42]: a_list = [2, 3, 7, None]
In [43]: tup = ("foo", "bar", "baz")
In [44]: b_list = list(tup)
In [45]: b_list
Out[45]: ['foo', 'bar', 'baz']
In [46]: b_list[1] = "peekaboo"
In [47]: b_list
Out[47]: ['foo', 'peekaboo', 'baz']

列表和元组在语义上是相似的(尽管元组不能被修改),并且可以在许多函数中互换使用。

list内置函数在数据处理中经常用作实例化迭代器或生成器表达式的方法:

In [48]: gen = range(10)
In [49]: gen
Out[49]: range(0, 10)
In [50]: list(gen)
Out[50]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
添加和删除元素

元素可以使用append方法附加到列表的末尾:

In [51]: b_list.append("dwarf")
In [52]: b_list
Out[52]: ['foo', 'peekaboo', 'baz', 'dwarf']

使用insert可以在列表中的特定位置插入元素:

In [53]: b_list.insert(1, "red")
In [54]: b_list
Out[54]: ['foo', 'red', 'peekaboo', 'baz', 'dwarf']

插入索引必须在列表的长度之间,包括 0 和长度。

警告:

append相比,insert的计算成本较高,因为必须在内部移动后续元素的引用以为新元素腾出空间。如果需要在序列的开头和结尾插入元素,您可能希望探索collections.deque,这是一个双端队列,专为此目的进行了优化,并且包含在 Python 标准库中。

insert的反向操作是pop,它会删除并返回特定索引处的元素:

In [55]: b_list.pop(2)
Out[55]: 'peekaboo'
In [56]: b_list
Out[56]: ['foo', 'red', 'baz', 'dwarf']

可以使用remove按值删除元素,它会定位第一个这样的值并将其从列表中删除:

In [57]: b_list.append("foo")
In [58]: b_list
Out[58]: ['foo', 'red', 'baz', 'dwarf', 'foo']
In [59]: b_list.remove("foo")
In [60]: b_list
Out[60]: ['red', 'baz', 'dwarf', 'foo']

如果不关心性能,通过使用appendremove,可以使用 Python 列表作为类似集合的数据结构(尽管 Python 有实际的集合对象,稍后讨论)。

使用in关键字检查列表是否包含一个值:

In [61]: "dwarf" in b_list
Out[61]: True

关键字not可以用来否定in

In [62]: "dwarf" not in b_list
Out[62]: False

检查列表是否包含一个值比使用字典和集合慢得多(即将介绍),因为 Python 会在线性扫描列表的值,而可以在常量时间内检查其他值(基于哈希表)。

连接和组合列表

与元组类似,使用+将两个列表相加会将它们连接起来:

In [63]: [4, None, "foo"] + [7, 8, (2, 3)]
Out[63]: [4, None, 'foo', 7, 8, (2, 3)]

如果已经定义了一个列表,可以使用extend方法将多个元素附加到其中:

In [64]: x = [4, None, "foo"]
In [65]: x.extend([7, 8, (2, 3)])
In [66]: x
Out[66]: [4, None, 'foo', 7, 8, (2, 3)]

请注意,通过加法进行列表连接是一种相对昂贵的操作,因为必须创建一个新列表并复制对象。通常最好使用extend将元素附加到现有列表中,特别是如果您正在构建一个大列表。因此:

everything = []
for chunk in list_of_lists:
 everything.extend(chunk)

比连接替代方案更快:

everything = []
for chunk in list_of_lists:
 everything = everything + chunk
排序

您可以通过调用其sort函数就地对列表进行排序(而不创建新对象):

In [67]: a = [7, 2, 5, 1, 3]
In [68]: a.sort()
In [69]: a
Out[69]: [1, 2, 3, 5, 7]

sort有一些选项,偶尔会派上用场。其中之一是能够传递一个次要排序键——即生成用于对对象进行排序的值的函数。例如,我们可以按字符串的长度对字符串集合进行排序:

In [70]: b = ["saw", "small", "He", "foxes", "six"]
In [71]: b.sort(key=len)
In [72]: b
Out[72]: ['He', 'saw', 'six', 'small', 'foxes']

很快,我们将看一下sorted函数,它可以生成一份排序后的一般序列的副本。

切片

您可以使用切片表示法选择大多数序列类型的部分,其基本形式是将start:stop传递给索引运算符[]

In [73]: seq = [7, 2, 3, 7, 5, 6, 0, 1]
In [74]: seq[1:5]
Out[74]: [2, 3, 7, 5]

切片也可以用序列赋值:

In [75]: seq[3:5] = [6, 3]
In [76]: seq
Out[76]: [7, 2, 3, 6, 3, 6, 0, 1]

虽然start索引处的元素被包括在内,但stop索引不包括在内,因此结果中的元素数量为stop - start

startstop可以省略,此时它们分别默认为序列的开头和序列的结尾:

In [77]: seq[:5]
Out[77]: [7, 2, 3, 6, 3]
In [78]: seq[3:]
Out[78]: [6, 3, 6, 0, 1]

负索引相对于末尾切片序列:

In [79]: seq[-4:]
Out[79]: [3, 6, 0, 1]
In [80]: seq[-6:-2]
Out[80]: [3, 6, 3, 6]

切片语义需要一点时间来适应,特别是如果你是从 R 或 MATLAB 过来的。参见图 3.1 以了解使用正整数和负整数进行切片的有用示例。在图中,索引显示在“箱边缘”,以帮助显示使用正整数或负整数索引开始和停止的切片选择。

图 3.1:Python 切片约定的示例

第二个冒号后也可以使用step,比如,每隔一个元素取一个:

In [81]: seq[::2]
Out[81]: [7, 3, 3, 0]

这种方法的一个巧妙用法是传递-1,这样可以有效地反转列表或元组:

In [82]: seq[::-1]
Out[82]: [1, 0, 6, 3, 6, 3, 2, 7]

字典

字典或dict可能是 Python 中最重要的内置数据结构。在其他编程语言中,字典有时被称为哈希映射关联数组。字典存储一组键-值对,其中是 Python 对象。每个键都与一个值关联,以便可以方便地检索、插入、修改或删除给定特定键的值。创建字典的一种方法是使用大括号{}和冒号来分隔键和值:

In [83]: empty_dict = {}
In [84]: d1 = {"a": "some value", "b": [1, 2, 3, 4]}
In [85]: d1
Out[85]: {'a': 'some value', 'b': [1, 2, 3, 4]}

可以使用与访问列表或元组元素相同的语法来访问、插入或设置元素:

In [86]: d1[7] = "an integer"
In [87]: d1
Out[87]: {'a': 'some value', 'b': [1, 2, 3, 4], 7: 'an integer'}
In [88]: d1["b"]
Out[88]: [1, 2, 3, 4]

你可以使用与检查列表或元组是否包含值相同的语法来检查字典是否包含键:

In [89]: "b" in d1
Out[89]: True

可以使用del关键字或pop方法(同时返回值并删除键)来删除值:

In [90]: d1[5] = "some value"
In [91]: d1
Out[91]: 
{'a': 'some value',
 'b': [1, 2, 3, 4],
 7: 'an integer',
 5: 'some value'}
In [92]: d1["dummy"] = "another value"
In [93]: d1
Out[93]: 
{'a': 'some value',
 'b': [1, 2, 3, 4],
 7: 'an integer',
 5: 'some value',
 'dummy': 'another value'}
In [94]: del d1[5]
In [95]: d1
Out[95]: 
{'a': 'some value',
 'b': [1, 2, 3, 4],
 7: 'an integer',
 'dummy': 'another value'}
In [96]: ret = d1.pop("dummy")
In [97]: ret
Out[97]: 'another value'
In [98]: d1
Out[98]: {'a': 'some value', 'b': [1, 2, 3, 4], 7: 'an integer'}

keysvalues方法分别为你提供字典的键和值的迭代器。键的顺序取决于它们插入的顺序,这些函数以相同的顺序输出键和值:

In [99]: list(d1.keys())
Out[99]: ['a', 'b', 7]
In [100]: list(d1.values())
Out[100]: ['some value', [1, 2, 3, 4], 'an integer']

如果需要同时迭代键和值,可以使用items方法以 2 元组的形式迭代键和值:

In [101]: list(d1.items())
Out[101]: [('a', 'some value'), ('b', [1, 2, 3, 4]), (7, 'an integer')]

可以使用update方法将一个字典合并到另一个字典中:

In [102]: d1.update({"b": "foo", "c": 12})
In [103]: d1
Out[103]: {'a': 'some value', 'b': 'foo', 7: 'an integer', 'c': 12}

update方法会直接更改字典,因此传递给update的数据中的任何现有键都将丢弃其旧值。

从序列创建字典

通常会偶尔出现两个你想要逐个元素配对的序列。作为第一步,你可能会编写这样的代码:

mapping = {}
for key, value in zip(key_list, value_list):
 mapping[key] = value

由于字典本质上是 2 元组的集合,dict函数接受一个 2 元组的列表:

In [104]: tuples = zip(range(5), reversed(range(5)))
In [105]: tuples
Out[105]: <zip at 0x17d604d00>
In [106]: mapping = dict(tuples)
In [107]: mapping
Out[107]: {0: 4, 1: 3, 2: 2, 3: 1, 4: 0}

稍后我们将讨论字典推导,这是构建字典的另一种方法。

默认值

通常会有类似以下逻辑:

if key in some_dict:
 value = some_dict[key]
else:
 value = default_value

因此,字典方法getpop可以接受要返回的默认值,因此上述if-else块可以简单地写为:

value = some_dict.get(key, default_value)

get默认情况下会返回None,如果键不存在,而pop会引发异常。在设置值时,可能字典中的值是另一种集合,比如列表。例如,你可以想象将单词列表按照它们的首字母分类为列表的字典:

In [108]: words = ["apple", "bat", "bar", "atom", "book"]
In [109]: by_letter = {}
In [110]: for word in words:
 .....:     letter = word[0]
 .....:     if letter not in by_letter:
 .....:         by_letter[letter] = [word]
 .....:     else:
 .....:         by_letter[letter].append(word)
 .....:
In [111]: by_letter
Out[111]: {'a': ['apple', 'atom'], 'b': ['bat', 'bar', 'book']}

setdefault字典方法可用于简化此工作流程。前面的for循环可以重写为:

In [112]: by_letter = {}
In [113]: for word in words:
 .....:     letter = word[0]
 .....:     by_letter.setdefault(letter, []).append(word)
 .....:
In [114]: by_letter
Out[114]: {'a': ['apple', 'atom'], 'b': ['bat', 'bar', 'book']}

内置的collections模块有一个有用的类defaultdict,使这更加容易。要创建一个,你需要传递一个类型或函数,用于为字典中的每个槽生成默认值:

In [115]: from collections import defaultdict
In [116]: by_letter = defaultdict(list)
In [117]: for word in words:
 .....:     by_letter[word[0]].append(word)
有效的字典键类型

虽然字典的值可以是任何 Python 对象,但键通常必须是不可变对象,如标量类型(int、float、string)或元组(元组中的所有对象也必须是不可变的)。这里的技术术语是可哈希性。你可以使用hash函数检查对象是否可哈希(可以用作字典中的键):

In [118]: hash("string")
Out[118]: 4022908869268713487
In [119]: hash((1, 2, (2, 3)))
Out[119]: -9209053662355515447
In [120]: hash((1, 2, [2, 3])) # fails because lists are mutable
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-120-473c35a62c0b> in <module>
----> 1 hash((1, 2, [2, 3])) # fails because lists are mutable
TypeError: unhashable type: 'list'

通常情况下,使用hash函数时看到的哈希值将取决于你使用的 Python 版本。

要将列表用作键,一种选择是将其转换为元组,只要其元素也可以被散列:

In [121]: d = {}
In [122]: d[tuple([1, 2, 3])] = 5
In [123]: d
Out[123]: {(1, 2, 3): 5}

集合

集合是一个无序的唯一元素集合。可以通过set函数或使用花括号的集合字面值来创建集合:

In [124]: set([2, 2, 2, 1, 3, 3])
Out[124]: {1, 2, 3}
In [125]: {2, 2, 2, 1, 3, 3}
Out[125]: {1, 2, 3}

集合支持数学集合操作,如并集、交集、差集和对称差集。考虑这两个示例集合:

In [126]: a = {1, 2, 3, 4, 5}
In [127]: b = {3, 4, 5, 6, 7, 8}

这两个集合的并集是两个集合中出现的不同元素的集合。可以使用union方法或|二进制运算符来计算:

In [128]: a.union(b)
Out[128]: {1, 2, 3, 4, 5, 6, 7, 8}
In [129]: a | b
Out[129]: {1, 2, 3, 4, 5, 6, 7, 8}

交集包含两个集合中都出现的元素。可以使用&运算符或intersection方法:

In [130]: a.intersection(b)
Out[130]: {3, 4, 5}
In [131]: a & b
Out[131]: {3, 4, 5}

请参见表 3.1 以获取常用集合方法的列表。

表 3.1:Python 集合操作

函数 替代语法 描述
a.add(x) N/A 将元素x添加到集合a
a.clear() N/A 将集合a重置为空状态,丢弃所有元素
a.remove(x) N/A 从集合a中删除元素x
a.pop() N/A 从集合a中删除一个任意元素,如果集合为空则引发KeyError
a.union(b) a &#124; b ab中所有唯一的元素
a.update(b) a &#124;= b a的内容设置为ab中元素的并集
a.intersection(b) a & b ab存在的所有元素
a.intersection_update(b) a &= b a的内容设置为ab中元素的交集
a.difference(b) a - b a中不在b中的元素
a.difference_update(b) a -= b a设置为a中不在b中的元素
a.symmetric_difference(b) a ^ b ab中的所有元素,但不是两者都有的
a.symmetric_difference_update(b) a ^= b a设置为ab中的元素,但不是两者都有的
a.issubset(b) <= 如果a的元素都包含在b中,则为True
a.issuperset(b) >= 如果b的元素都包含在a中,则为True
a.isdisjoint(b) N/A 如果ab没有共同元素,则为True

注意

如果将不是集合的输入传递给unionintersection等方法,Python 将在执行操作之前将输入转换为集合。在使用二进制运算符时,两个对象必须已经是集合。

所有逻辑集合操作都有原地对应物,这使您可以用结果替换操作左侧集合的内容。对于非常大的集合,这可能更有效率:*

In [132]: c = a.copy()
In [133]: c |= b
In [134]: c
Out[134]: {1, 2, 3, 4, 5, 6, 7, 8}
In [135]: d = a.copy()
In [136]: d &= b
In [137]: d
Out[137]: {3, 4, 5}

与字典键类似,集合元素通常必须是不可变的,并且它们必须是可散列的(这意味着对值调用hash不会引发异常)。为了将类似列表的元素(或其他可变序列)存储在集合中,可以将它们转换为元组:

In [138]: my_data = [1, 2, 3, 4]
In [139]: my_set = {tuple(my_data)}
In [140]: my_set
Out[140]: {(1, 2, 3, 4)}

您还可以检查一个集合是否是另一个集合的子集(包含在内)或超集(包含所有元素):

In [141]: a_set = {1, 2, 3, 4, 5}
In [142]: {1, 2, 3}.issubset(a_set)
Out[142]: True
In [143]: a_set.issuperset({1, 2, 3})
Out[143]: True

只有当集合的内容相等时,集合才相等:

In [144]: {1, 2, 3} == {3, 2, 1}
Out[144]: True

内置序列函数

Python 有一些有用的序列函数,您应该熟悉并在任何机会使用。

enumerate

在迭代序列时,通常希望跟踪当前项目的索引。自己动手的方法如下:

index = 0
for value in collection:
 # do something with value
 index += 1

由于这种情况很常见,Python 有一个内置函数enumerate,它返回一个(i, value)元组序列:

for index, value in enumerate(collection):
 # do something with value
sorted

sorted函数从任何序列的元素返回一个新的排序列表:

In [145]: sorted([7, 1, 2, 6, 0, 3, 2])
Out[145]: [0, 1, 2, 2, 3, 6, 7]
In [146]: sorted("horse race")
Out[146]: [' ', 'a', 'c', 'e', 'e', 'h', 'o', 'r', 'r', 's']

sorted函数接受与列表的sort方法相同的参数。

zip

zip将多个列表、元组或其他序列的元素“配对”起来,以创建一个元组列表:

In [147]: seq1 = ["foo", "bar", "baz"]
In [148]: seq2 = ["one", "two", "three"]
In [149]: zipped = zip(seq1, seq2)
In [150]: list(zipped)
Out[150]: [('foo', 'one'), ('bar', 'two'), ('baz', 'three')]

zip可以接受任意数量的序列,并且它生成的元素数量由最短的序列决定:

In [151]: seq3 = [False, True]
In [152]: list(zip(seq1, seq2, seq3))
Out[152]: [('foo', 'one', False), ('bar', 'two', True)]

zip的一个常见用法是同时迭代多个序列,可能还与enumerate结合使用:

In [153]: for index, (a, b) in enumerate(zip(seq1, seq2)):
 .....:     print(f"{index}: {a}, {b}")
 .....:
0: foo, one
1: bar, two
2: baz, three
反转

reversed以相反的顺序迭代序列的元素:

In [154]: list(reversed(range(10)))
Out[154]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

请记住,reversed是一个生成器(稍后将更详细讨论),因此它不会创建反转的序列,直到实现(例如,使用listfor循环)。

列表、集合和字典推导

列表推导是 Python 语言中一个方便且广泛使用的特性。它们允许您通过过滤集合的元素,将通过过滤的元素转换为一个简洁的表达式来简洁地形成一个新列表。它们的基本形式如下:

[expr for value in collection if condition]

这等同于以下的for循环:

result = []
for value in collection:
 if condition:
 result.append(expr)

过滤条件可以被省略,只留下表达式。例如,给定一个字符串列表,我们可以过滤出长度为2或更少的字符串,并将它们转换为大写:

In [155]: strings = ["a", "as", "bat", "car", "dove", "python"]
In [156]: [x.upper() for x in strings if len(x) > 2]
Out[156]: ['BAT', 'CAR', 'DOVE', 'PYTHON']

集合和字典推导是一个自然的扩展,以一种类似的方式产生集合和字典,而不是列表。

字典推导看起来像这样:

dict_comp = {key-expr: value-expr for value in collection
 if condition}

集合推导看起来与等效的列表推导相同,只是用花括号代替方括号:

set_comp = {expr for value in collection if condition}

与列表推导类似,集合和字典推导大多是便利性的,但它们同样可以使代码更易于编写和阅读。考虑之前的字符串列表。假设我们想要一个集合,其中只包含集合中包含的字符串的长度;我们可以很容易地使用集合推导来计算:

In [157]: unique_lengths = {len(x) for x in strings}
In [158]: unique_lengths
Out[158]: {1, 2, 3, 4, 6}

我们也可以更加功能化地使用map函数,稍后介绍:

In [159]: set(map(len, strings))
Out[159]: {1, 2, 3, 4, 6}

作为一个简单的字典推导示例,我们可以创建一个查找这些字符串在列表中位置的查找映射:

In [160]: loc_mapping = {value: index for index, value in enumerate(strings)}
In [161]: loc_mapping
Out[161]: {'a': 0, 'as': 1, 'bat': 2, 'car': 3, 'dove': 4, 'python': 5}
嵌套列表推导

假设我们有一个包含一些英文和西班牙名字的列表列表:

In [162]: all_data = [["John", "Emily", "Michael", "Mary", "Steven"],
 .....:             ["Maria", "Juan", "Javier", "Natalia", "Pilar"]]

假设我们想要获得一个包含所有包含两个或更多个a的名称的单个列表。我们可以通过一个简单的for循环来实现:

In [163]: names_of_interest = []
In [164]: for names in all_data:
 .....:     enough_as = [name for name in names if name.count("a") >= 2]
 .....:     names_of_interest.extend(enough_as)
 .....:
In [165]: names_of_interest
Out[165]: ['Maria', 'Natalia']

实际上,您可以将整个操作封装在一个单独的嵌套列表推导中,看起来像:

In [166]: result = [name for names in all_data for name in names
 .....:           if name.count("a") >= 2]
In [167]: result
Out[167]: ['Maria', 'Natalia']

起初,嵌套列表推导可能有点难以理解。列表推导的for部分按照嵌套的顺序排列,任何过滤条件都放在最后。这里是另一个示例,我们将整数元组的列表“展平”为一个简单的整数列表:

In [168]: some_tuples = [(1, 2, 3), (4, 5, 6), (7, 8, 9)]
In [169]: flattened = [x for tup in some_tuples for x in tup]
In [170]: flattened
Out[170]: [1, 2, 3, 4, 5, 6, 7, 8, 9]

请记住,如果您写一个嵌套的for循环而不是列表推导,for表达式的顺序将是相同的:

flattened = []
for tup in some_tuples:
 for x in tup:
 flattened.append(x)

您可以有任意多层的嵌套,尽管如果您有超过两三层的嵌套,您可能应该开始质疑这是否在代码可读性方面是有意义的。重要的是要区分刚刚显示的语法与列表推导内部的列表推导,后者也是完全有效的:

In [172]: [[x for x in tup] for tup in some_tuples]
Out[172]: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

这将产生一个列表的列表,而不是所有内部元素的扁平化列表。


Python 数据分析(PYDA)第三版(一)(4)https://developer.aliyun.com/article/1482371

相关文章
|
2天前
|
机器学习/深度学习 数据采集 算法
Python用逻辑回归、决策树、SVM、XGBoost 算法机器学习预测用户信贷行为数据分析报告
Python用逻辑回归、决策树、SVM、XGBoost 算法机器学习预测用户信贷行为数据分析报告
|
5天前
|
数据采集 数据可视化 数据挖掘
R语言与Python:比较两种数据分析工具
【4月更文挑战第25天】R语言和Python是目前最流行的两种数据分析工具。本文将对这两种工具进行比较,包括它们的历史、特点、应用场景、社区支持、学习资源、性能等方面,以帮助读者更好地了解和选择适合自己的数据分析工具。
|
11天前
|
机器学习/深度学习 数据挖掘 计算机视觉
python数据分析工具SciPy
【4月更文挑战第15天】SciPy是Python的开源库,用于数学、科学和工程计算,基于NumPy扩展了优化、线性代数、积分、插值、特殊函数、信号处理、图像处理和常微分方程求解等功能。它包含优化、线性代数、积分、信号和图像处理等多个模块。通过SciPy,可以方便地执行各种科学计算任务。例如,计算高斯分布的PDF,需要结合NumPy使用。要安装SciPy,可以使用`pip install scipy`命令。这个库极大地丰富了Python在科学计算领域的应用。
13 1
|
11天前
|
数据可视化 数据挖掘 Linux
python数据分析工具Seaborn
【4月更文挑战第15天】Seaborn是Python的数据可视化库,基于matplotlib,提供统计图形绘制。它包含内置数据集,支持风格控制和多种图形类型如散点图、分布图,擅长处理分类数据并能可视化统计估计。Seaborn还能绘制热图、聚类图及交互式图形。要使用它,先用`pip install seaborn`安装,然后导入并应用到数据集上,如示例中展示的散点图绘制。
10 1
|
12天前
|
数据可视化 数据挖掘 API
Python数据分析工具Seaborn
【4月更文挑战第14天】Seaborn是Python的数据可视化库,基于matplotlib,为数据科学家提供高级接口创建统计图形。其特点包括简洁的API、丰富的图形类型(如散点图、直方图)、内置统计功能、数据集集成和与pandas的紧密配合。使用时需先安装Seaborn和matplotlib,然后通过Seaborn的函数(如`scatterplot()`、`histplot()`)绘制图形。它是数据分析和可视化的一个强大工具。
21 8
Python数据分析工具Seaborn
|
12天前
|
数据可视化 数据挖掘 Python
Python中数据分析工具Matplotlib
【4月更文挑战第14天】Matplotlib是Python的数据可视化库,能生成多种图表,如折线图、柱状图等。以下是一个绘制简单折线图的代码示例: ```python import matplotlib.pyplot as plt x = [1, 2, 3, 4, 5] y = [2, 4, 6, 8, 10] plt.figure() plt.plot(x, y) plt.title(&#39;简单折线图&#39;) plt.xlabel(&#39;X轴&#39;) plt.ylabel(&#39;Y轴&#39;) plt.show() ```
13 1
|
12天前
|
存储 数据挖掘 数据处理
Python中数据分析工具NumPy
【4月更文挑战第14天】NumPy是Python的数值计算扩展库,专注于处理大型多维数组和矩阵。其主要特性包括ndarray对象(存储同类型元素的高效数据容器)、广播机制(处理不同形状数组运算)、线性代数与统计函数、随机数生成及与C/C++/Fortran代码集成。作为Pandas等数据分析工具的基础,掌握NumPy对提升数据处理能力至关重要。
9 1
|
12天前
|
数据采集 SQL 数据可视化
Python数据分析工具Pandas
【4月更文挑战第14天】Pandas是Python的数据分析库,提供Series和DataFrame数据结构,用于高效处理标记数据。它支持从多种数据源加载数据,包括CSV、Excel和SQL。功能包括数据清洗(处理缺失值、异常值)、数据操作(切片、过滤、分组)、时间序列分析及与Matplotlib等库集成进行数据可视化。其高性能底层基于NumPy,适合大型数据集处理。通过加载数据、清洗、分析和可视化,Pandas简化了数据分析流程。广泛的学习资源使其成为数据分析初学者的理想选择。
15 1
|
6月前
|
数据可视化 数据挖掘 Python
【Python】数据分析:matplotlib折线图
【Python】数据分析:matplotlib折线图
57 0
|
8月前
|
机器学习/深度学习 数据采集 数据挖掘
Python 数据分析入门教程:Numpy、Pandas、Matplotlib和Scikit-Learn详解
Python 数据分析入门教程:Numpy、Pandas、Matplotlib和Scikit-Learn详解
143 0