利用Python进行数据分析_01_内建数据结构、函数及文件(2)
1.1.2 列表
与元组不同,列表的长度是可变的,内容也是可以修改的。可以使用中括号[ ]或者list函数来定义列表:
In [30]: a_list = [2, 3, 7, None] In [31]: tup = ('foo', 'bar', 'baz') In [32]: b_list = list(tup) In [33]: b_list Out[33]: ['foo', 'bar', 'baz']
In [34]: b_list[1] = 'peekaboo'
In [35]: b_list
Out[35]: ['foo', 'peekaboo', 'baz']
列表与元组非常相似,它们的很多函数用法都是相似的。
list函数在数据处理中常用于将迭代器或者生成器转化为列表。
In [36]: gen = range(10) In [37]: gen Out[37]: range(0, 10) In [38]: list(gen) Out[38]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1.1.2.1 增加和移除元素
使用append方法可以将元素添加到列表的尾部:
In [39]: b_list.append('dwarf') In [40]: b_list Out[40]: ['foo', 'peekaboo', 'baz', 'dwarf']
使用insert方法可以将元素插入到指定的列表位置:
In [41]: b_list.insert(1, 'red') In [42]: b_list Out[42]: ['foo', 'red', 'peekaboo', 'baz', 'dwarf']
insert与append相比,计算代价更高。因为子序列元素不得不在内部移动为新元素提供空间。
insert的反操作是pop,该操作将特性位置的元素移除并返回:
In [43]: b_list.pop(2) Out[43]: 'peekaboo' In [44]: b_list Out[44]: ['foo', 'red', 'baz', 'dwarf']
元素可以通过remove的方法移除,该方法会定位第一个符合要求的值并移除它:
In [45]: b_list.append('foo') In [46]: b_list Out[46]: ['foo', 'red', 'baz', 'dwarf', 'foo'] In [47]: b_list.remove('foo') In [48]: b_list Out[48]: ['red', 'baz', 'dwarf', 'foo']
使用in关键字可以检查一个值是否在列表中,not关键字可以用作in的反义词,表示不在
In [49]: 'dwarf' in b_list Out[49]: True In [50]: 'dwarf' not in b_list Out[50]: False
1.1.2.2 连接和联合列表
与元组类似,两个列表可以使用 + 连接:
In [51]: [4, None, 'foo'] + [7, 8, (2,3)] Out[51]: [4, None, 'foo', 7, 8, (2, 3)]
可以用extend方法来向已有列表添加多个元素
In [52]: x = [4, None, 'foo'] In [53]: x.extend([7, 8, (2,3)]) In [54]: x Out[54]: [4, None, 'foo', 7, 8, (2, 3)]
通过添加内容来连接列表是一种相对高代价的操作,因为连接过程中创建了新列表,还要复制对象。使用extend将元素添加到已经存在的列表是更好的方式,尤其是在需要构建一个大型列表的时候。
everything = [] for chunk in list_of_lists: everything.extend(chunk)
1.1.2.3 排序
可以调用列表的sort方法对列表内部进行排序(无须新建一个对象)
In [55]: a = [7, 8, 2, 5, 3] In [56]: a.sort() In [57]: a Out[57]: [2, 3, 5, 7, 8]
可以通过字符串的长度进行排序:
In [58]: b = ['saw', 'small', 'He', 'foxes', 'six'] In [59]: b.sort(key = len) In [60]: b Out[60]: ['He', 'saw', 'six', 'small', 'foxes']
1.1.2.4 二分搜索和以排序列表的维护
内建的bisect模块实现了二分搜索和已排序列表的插值。bisect.bisect会找到元素应当被插入的位置,并保持序列排序,而bisect.insort将元素插入到相应位置
In [61]: import bisect In [62]: c = [1, 2, 2, 2, 2, 3, 4, 7] In [63]: bisect.bisect(c, 2) Out[63]: 5 In [64]: bisect.bisect(c, 5) Out[64]: 7 In [65]: bisect.insort(c, 6) In [66]: c Out[66]: [1, 2, 2, 2, 2, 3, 4, 6, 7]
1.1.2.5 切片
使用切片符号可以对大多数序列类型选取其子集,它的基本形式是将start:stop传入到索引符号[ ]中
In [67]: seq = [7, 2, 3, 7, 5, 6, 0, 1] In [68]: seq[1:5] Out[68]: [2, 3, 7, 5]
切片还可以将序列赋值给变量
In [69]: seq[3:4] = [6, 3] In [70]: seq Out[70]: [7, 2, 3, 6, 3, 5, 6, 0, 1]
由于起始位置start的索引是包含的,而结束位置的stop的索引并不会包含,因此元素的数量是stop-start.
start和stop是可以省略的,如果省略的话会默认传入序列的起始位置或结束位置。
In [75]: seq[:5] Out[75]: [7, 2, 3, 6, 3] In [76]: seq[3:] Out[76]: [6, 3, 5, 6, 0, 1]
负索引可以从序列的尾部进行索引
In [77]: seq[-4:] Out[77]: [5, 6, 0, 1] In [78]: seq[-6:-2] Out[78]: [6, 3, 5, 6]
步进值step可以在第二个冒号后面使用,意思是每隔多少个数取一个值
In [79]: seq[::2]
Out[79]: [7, 3, 3, 6, 1]
当需要对列表或元组进行翻转时,一种很聪明的用法就是向步进传值-1
In [80]: seq[::-1]
Out[80]: [1, 0, 6, 5, 3, 6, 3, 2, 7]
1.1.3 内建序列函数
1.1.3.1 enumerate
我们经常需要在遍历一个序列的同时追踪当前元素的索引,一种自行实现的方法如下
i = 0 for value in collection: # 使用值做点事 i += 1
由于这种场景很常见,所以python内建了enumerate函数,返回了(i, value)的序列,其中value是元素的值, i是元素的索引。
for i, value in enumerate(collection): # 使用值做点事情
当需要对数据建立索引时,一种有效的模式就是使用enumerate构造一个字典,将序列值映射到索引位置上。
In [86]: some_list = ['foo', 'bar', 'baz'] In [87]: mapping = {} In [88]: for i, v in enumerate(some_list): ...: mapping[v] = i In [89]: mapping Out[89]: {'foo': 0, 'bar': 1, 'baz': 2}
1.1.3.2 sorted
sorted函数返回一个根据任意序列中的元素新建的已排序列表:
In [90]: sorted([7, 1, 3, 4, 9, 3, 2]) Out[90]: [1, 2, 3, 3, 4, 7, 9] In [91]: sorted('horse race') Out[91]: [' ', 'a', 'c', 'e', 'e', 'h', 'o', 'r', 'r', 's']
sorted函数接受的参数与列表的sort方法一致
1.1.3.3 zip
zip将列表、元组或其他序列的元素配对,新建一个元组构成的列表
In [92]: seq1 = ['foo', 'bar', 'baz'] In [93]: seq2 = ['one', 'two', 'three'] In [94]: zipped = zip(seq1, seq2) In [95]: zipped Out[95]: <zip at 0x244e602ae48> In [96]: list(zipped) Out[96]: [('foo', 'one'), ('bar', 'two'), ('baz', 'three')]
zip 可以处理任意长度的序列,它生成的列表长度由最短的序列决定
In [97]: seq3 = [False, True] In [98]: list(zip(seq1, seq2, seq3)) Out[98]: [('foo', 'one', False), ('bar', 'two', True)]
zip的常用场景为同时遍历多个序列,有时会和enumerate同时使用
In [99]: for i, (a,b) in enumerate(zip(seq1, seq2)): ...: print('{0}: {1}, {2}'.format(i, a, b)) ...: 0: foo, one 1: bar, two 2: baz, three
给定一个已“配对”的序列时,zip函数有一种机智的方式去拆分序列。这种方式的另一种思路就是将行的列表转换为列的列表
In [100]: pitchers = [('Nolan', 'Ryan'), ('Roger', 'Clemens'), ('Schilling', 'Curt')] In [101]: first_names, last_names = zip(*pitchers) In [102]: first_names Out[102]: ('Nolan', 'Roger', 'Schilling') In [103]: last_names Out[103]: ('Ryan', 'Clemens', 'Curt')
1.1.3.4 reversed
reversed函数将序列的元素倒序排列
In [107]: list(reversed(range(10)))
Out[107]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0
reversed是一个生成器,如果没有实例化(如使用list函数或进行for循环)的时候,它并不会产生一个倒序的列表。