《利用Python进行数据分析·第2版》【Chapter 3.1】Python 的数据结构

【Chapter 3】 Python的数据结构、函数和文件

本章讨论 Python 的内置功能,我们会从Python最基础的数据结构开始:元组、列表、字典和集合。然后会讨论创建你自己的、可重复使用的Python函数。最后,会学习Python的文件对象,以及如何与本地硬盘交互。

3.1 数据结构和序列

元组(Tuple)

元组是长度固定,不可改变的序列(可以看做长度固定,且不可变的列表)。创建元祖的方法是用逗号:

1. 合并元组

用 “+” 合并元组。

In [11]: (4,5,6)+(7,8)
Out[11]: (4, 5, 6, 7, 8)

2. 取出元组

In [14]: tup = 4, 5, (6, 7)
    ...: a, b, (c, d) = tup
    ...: 

In [15]: d
Out[15]: 7

交换变量名

In [19]: a
Out[19]: 4

In [20]: b
Out[20]: 5

In [21]: b,a = a,b

In [22]: a
Out[22]: 5

In [23]: b
Out[23]: 4

类似上例的 unpacking 方法常用来迭代元组或列表序列:

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

另一种更高级的 unpacking 方法是用于只取出tuple中开头几个元素,剩下的元素直接赋给*rest

In [36]: values = 1,2,3,4,5

In [37]: a,b,*rest = values

In [38]: a,b
Out[38]: (1, 2)

In [39]: rest
Out[39]: [3, 4, 5]

rest 部分是你想要丢弃的,名字本身无所谓,通常用下划线来代替:

In [40]: a, b, *_ = values

In [41]: _
Out[41]: [3, 4, 5]
3. Tuple methods(元组方法)

因为tuple的大小和内容都不能改变,所以方法也很少。count用来计算某个值出现的次数,list中也有这个方法:

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

List(列表)

列表的灵活性就很强了,大小和内容都可以变。

list 函数通常用来具现化迭代器或生成器:

In [43]: gen =range(10)

In [44]: lis(gen)

In [45]: list(gen)
Out[45]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1. 添加

list.append() 在列表末尾添加;

list.insert(index,element) 可以在特定的位置插入元素 ;

警告:与append相比,insert耗费的计算量大,因为对后续元素的引用必须在内部迁移,以便为新元素提供空间。如果要在序列的头部和尾部插入元素,你可能需要使用collections.deque,一个双尾部队列。

2.删除

insert的逆运算是pop(index),它移除并返回指定位置的元素;

可以用remove去除某个值,remove('element')会先寻找第一个值并除去;

如果不考虑性能,使用appendremove,可以把Python的列表当做完美的“多重集”数据结构。

3.包含

in可以检查列表是否包含某个值:

In [55]: 'dwarf' in b_list
Out[55]: True
4. 串联和组合列表

与元组类似,可以用加号将两个列表串联起来:

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

如果已经定义了一个列表,用extend方法可以追加多个元素:

In [58]: x = [4, None, 'foo']

In [59]: x.extend([7, 8, (2, 3)])

In [60]: x
Out[60]: [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

总结:

append 和 extend 的区别。

  • append 是把元素添加到一个list里
  • extend 是把两个 list (多个元素)结合在一起

extend 和 + 的区别

  • + 是创建了一个新的list并返回,运算量大
  • extend 是在原本的list上做了更改,运算量小
5. 排序

你可以用sort函数将一个列表原地排序(不创建新的对象):

In [61]: a = [7, 2, 5, 1, 3]

In [62]: a.sort()

In [63]: a
Out[63]: [1, 2, 3, 5, 7]

sort有一些选项,有时会很好用。其中之一是二级排序key,可以用这个key进行排序。例如,我们可以按长度对字符串进行排序:

In [64]: b = ['saw', 'small', 'He', 'foxes', 'six']

In [65]: b.sort(key=len)

In [66]: b
Out[66]: ['He', 'saw', 'six', 'small', 'foxes']

二分搜索和维持一个排好序的 list 列表

bisect模块支持二分查找,和向已排序的列表插入值。:

bisect.bisect 其目的在于查找该数值将会插入的位置并返回,而不会插入。

bisect.insort 是插入一个值,而插入后的列表还是整齐的。

In [47]: c= [1,2,2,2,3,4,7]

In [48]: bisect.bisect(c,2)
Out[48]: 4

In [49]: bisect.bisect(c,5)
Out[49]: 6

In [50]: bisect.bisect(c,6)
Out[50]: 6

In [51]: c
Out[51]: [1, 2, 2, 2, 3, 4, 7]

注意:bisect模块不会检查list是否是排好序的,对未排序的列表使用bisect不会产生错误,但结果不一定正确 ,所以用这个模块之前要先把list排序。

Slicing (切片)

[start:stop], 输出的结果包含开头,不包含结尾。所以输出的结果的数量是stop-start 。即区间为[start,stop)。

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

In [74]: seq[1:5]
Out[74]: [2, 3, 7, 5]

startstop 都可以被省略,省略之后,分别默认序列的开头和结尾(负索引表示倒数开始多少个的意思):

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

In [78]: seq[-4:]
Out[78]: [5, 6, 0, 1]

两个冒号后面的数代表步长(step),就是隔(step-1)个元素取一次:

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

In [81]: seq[::2]
Out[81]: [7, 3, 3, 6, 1]

序列函数

1. enumerate(枚举)函数

迭代一个序列时,你可能想跟踪当前项的序号 。一个比较直白的方法是:

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

但 Python 内建了一个enumerate函数,可以返回(i, value)元组序列:

for i, value in enumerate(collection):

enumerate 通常用来把一个 list 中的位置和值映射到一个 dcit 字典里:

In [57]: some_list = ['foo','bar','baz']

In [58]: mapping={}

In [59]: for i,v in enumerate(some_list):
    ...:     mapping[v] = i
    ...:     

In [60]: mapping
Out[60]: {'bar': 1, 'baz': 2, 'foo': 0}
2. sorted()

sorted函数可以从任意序列的元素返回一个新的排好序的列表:

In [71]: sorted([1,23,4,5,2,7])
Out[71]: [1, 2, 4, 5, 7, 23]

In [72]: sorted('jadon sunshine')
Out[72]: [' ', 'a', 'd', 'e', 'h', 'i', 'j', 'n', 'n', 'n', 'o', 's', 's', 'u']

sorted函数可以接受和sort相同的参数。

3. zip 函数

用于"pairs"(成对)。把多个列表、元组或其它序列每个对应的元素变成一对,最后返回一个含有 tuple 的 list:

In [73]: seq1 = ['foo', 'bar', 'baz']

In [74]: seq2 = ['one', 'two', 'three']

In [75]: zipped = zip(seq1,seq2)

In [76]: list(zipped)
Out[76]: [('foo', 'one'), ('bar', 'two'), ('baz', 'three')]

zip 可以接收任意长度的序列,最后返回的结果取决于最短的序列:

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

zip 的一个常见用法是同时迭代多个序列,可以和 enumerate 搭配起来用:

In [79]: 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

如果给我们一个压缩过的序列,我们可以将其解压:


In [81]: pitchers = [('Nolan', 'Ryan'), ('Roger', 'Clemens'), 
    ...:             ('Schilling', 'Curt')]
    ...:             

In [82]: first_names,last_names = zip(*pitchers) #解压
In [84]: first_names
Out[84]: ('Nolan', 'Roger', 'Schilling')
In [85]: last_names
Out[85]: ('Ryan', 'Clemens', 'Curt')
5.reversed

reverse可以倒叙迭代序列:

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

注意,revered是一个generator(生成器,之后会详细讲),所以必须需要(list 或 for循环)来具现化 。

字典

1. 字典创建、访问、插入、更改

字典也被叫做hash map 或 associative array。结构就是key-value pairs.创建方式是用{key : value}:

In [101]: empty_dict = {}

In [102]: d1 = {'a' : 'some value', 'b' : [1, 2, 3, 4]}

In [103]: d1
Out[103]: {'a': 'some value', 'b': [1, 2, 3, 4]}

你可以像访问列表或元组中的元素一样,访问、插入或设定字典中的元素:

In [87]: d1 = {'a': 'some value', 'b': [1, 2, 3, 4]}

In [88]: d1[7] ='an integer'#插入

In [90]: d1
Out[90]: {7: 'an integer', 'a': 'some value', 'b': [1, 2, 3, 4]}

In [91]: d1['b']#访问
Out[91]: [1, 2, 3, 4]

你可以用检查列表和元组是否包含某个值得方法,检查字典中是否包含某个键:

In [93]: 'b' in d1
Out[93]: True

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

In [94]: d1[5] = 'some value'

In [95]: d1
Out[95]: {5: 'some value', 7: 'an integer', 'a': 'some value', 'b': [1, 2, 3, 4]}

In [96]: del d1[5]

In [97]: d1
Out[97]: {7: 'an integer', 'a': 'some value', 'b': [1, 2, 3, 4]}

In [101]: d1
Out[101]: {7: 'an integer', 'a': 'some value', 'b': [1, 2, 3, 4]}

In [102]: ret = d1.pop(7)

In [103]: d1
Out[103]: {'a': 'some value', 'b': [1, 2, 3, 4]}

keys 和 values 方法能返回dict中key-value组合的迭代器,不过并不按什么顺序。如果想让 keys 和 values 每次打印的顺序相同的话:

In [106]: d1
Out[106]: {7: 'integer', 'a': 'some value', 'b': [1, 2, 3, 4]}

In [108]: list(d1.keys())
Out[108]: ['a', 'b', 7]

In [110]: list(d1.values())
Out[110]: ['some value', [1, 2, 3, 4], 'integer']

update方法可以将一个字典与另一个融合:

In [119]: d1.update({'b' : 'foo', 'c' : 12})

In [120]: d1
Out[120]: {'a': 'some value', 'b': 'foo', 7: 'an integer', 'c': 12}

update方法是原地改变字典,因此任何传递给update的键的旧的值都会被舍弃。

2.用序列创建字典

常常,你可能想将两个序列配对组合成字典。下面是一种写法:

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

因为字典本质上是 2元元组 的集合,dict可以接受 2元元组的列表:

In [121]: mapping = dict(zip(range(5), reversed(range(5))))

In [122]: mapping
Out[122]: {0: 4, 1: 3, 2: 2, 3: 1, 4: 0}
3. Default value(默认值)

如果 dict 中某个key存在的话,就返回该value,否则的话,就返回一个默认值:

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

不过dict中的get 和pop方法能设置默认值,即能把上面的代码简写为:

value = some_dict.get(key, default_value)

如果key不存在的话,get方法默认会返回None,而pop则会引发一个错误。

通过设定值,一个常用的场景是一个dict中的value也是其他集合,比如list。举例说明,我们想要把一些单词按首字母归类:

In [128]: words = ['apple', 'bat', 'bar', 'atom', 'book']
     ...: by_letter = {}
     ...: 

In [129]: for word in words:
     ...:     letter = word[0]
     ...:     if letter not in by_letter:
     ...:         by_letter[letter] = [word]#新建一个 key:value
     ...:     else :
     ...:         by_letter[letter].append(word)#如果已经存在这个 key 直接添加 value
     ...:         

In [129]: 

In [130]: by_letter
Out[130]: {'a': ['apple', 'atom'], 'b': ['bat', 'bar', 'book']}

而 setdefault 方法则是专门为这个用途存在的,上面的循环可以写为:

for word in words:
    letter = word[0]
    by_letter.setdefault(letter, []).append(word)

使用setdefault() 初始化字典键值. 使用字典的时候经常会遇到这样一种应用场景:动态更新字典,像如上面代码,如果 key 不在 dictionary 中那么就添加它并把它对应的值初始为空列表[] ,然后把元素append到空列表中。

内建的collections模块有一个有用的class,defaultdict,这个能让上述过程更简单。创建方法是传递一个type或是函数:

from collections import defaultdict
by_letter = defaultdict(list)
for word in words:
    by_letter[word[0]].append(word)

Valid dict key types(有效的key类型)

通常key的类型是不可更改的常量类型(int,float,string)或tuple。专业的叫法是hashability。可以查看一个object是否是hashable,只要是hashable的,就可以当做dict中的key。这里用hash函数查看:

In [127]: hash('string')
Out[127]: 5023931463650008331

In [128]: hash((1, 2, (2, 3)))
Out[128]: 1097636502276347782

In [129]: hash((1, 2, [2, 3])) # fails because lists are mutable
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
 in ()
----> 1 hash((1, 2, [2, 3])) # fails because lists are mutable
TypeError: unhashable type: 'list'

要用列表当做键,一种方法是将列表转化为元组,只要内部元素可以被哈希,它也就可以被哈希:

In [131]: d = {}

In [132]: d[tuple([1,2,3])]=5

In [133]: d
Out[133]: {(1, 2, 3): 5}

Set 集合

集合是无序的不可重复的元素的集合。你可以把它当做字典,但是只有键没有值。可以用两种方式创建集合:通过 se t函数或使用尖括号 set 语句:

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

In [134]: {2, 2, 2, 1, 3, 3}
Out[134]: {1, 2, 3}

集合支持合并、交集、差分和对称差等数学集合运算。考虑两个示例集合:

In [135]: a = {1, 2, 3, 4, 5}

In [136]: b = {3, 4, 5, 6, 7, 8}

合并是取两个集合中不重复的元素。可以用union方法,或者|运算符:

In [136]: # 并集
    ...: a.union(b)
Out[136]: {1, 2, 3, 4, 5, 6, 7, 8}
In [137]: a|b
Out[137]: {1, 2, 3, 4, 5, 6, 7, 8}

交集的元素包含在两个集合中。可以用intersection&运算符:

In [138]: # 交集
     ...: a.intersection(b)
Out[138]: {3, 4, 5}
In [139]: a & b
Out[139]: {3, 4, 5}
《利用Python进行数据分析·第2版》【Chapter 3.1】Python 的数据结构_第1张图片

所有逻辑集合操作都有另外原地实现方法,它可以直接用结果替代集合的内容。对于大的集合,这么做效率更高:

In [141]: c = a.copy()

In [142]: c |= b

In [143]: c
Out[143]: {1, 2, 3, 4, 5, 6, 7, 8}

In [144]: d = a.copy()

In [145]: d &= b

In [146]: d
Out[146]: {3, 4, 5}

set 的元素必须是不可更改的。如果想要 list 一样的元素,只能变为tuple:

In [157]: my_data = [1,2,3,4]

In [158]: my_set = {tuple(my_data)}

In [159]: my_set
Out[159]: {(1, 2, 3, 4)}

你还可以检测一个集合是否是另一个集合的子集或父集:

In [150]: a_set = {1, 2, 3, 4, 5}

In [151]: {1, 2, 3}.issubset(a_set)
Out[151]: True

In [152]: a_set.issuperset({1, 2, 3})
Out[152]: True

集合的内容相同时,集合才对等:

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

列表、集合和字典推导式

list comprehension(列表推导式)是python里最受喜爱的一个特色。我们能简洁地构造一个list:

[expr for val in collection if condiction]
相当于:
result = []
for val in collection:
    if condition:
        result.append(expr)

比如,给定一个list,里面有很多string,我们只要留下string长度超过2的,并将其转换为大写:

In [160]: strings = ['a', 'as', 'bat', 'car', 'dove', 'python']

In [161]: [x.upper() for x in strings if len(x) > 2]
Out[161]: ['BAT', 'CAR', 'DOVE', 'PYTHON']

dict推导式:

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

set的推导式:(集合的推导式与列表很像,只不过用的是尖括号: )

set_comp = {expr for value in collection if condition}

基于上面的例子,我们想要一个集合来保存string的长度:


map函数可以进一步简化:

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

下面一个简单的字典表达式例子,查找 字符串和其在list中对应的index:

In [159]: loc_mapping = {val : index for index, val in enumerate(strings)}

In [160]: loc_mapping
Out[160]: {'a': 0, 'as': 1, 'bat': 2, 'car': 3, 'dove': 4, 'python': 5}

作者:SeanCheney
链接:https://www.jianshu.com/p/b444cda10aa0
來源:
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Nested list comprehensions(嵌套列表表达式)

假设我们有一个list,list中又有不同的list表示英语和西班牙语的姓名:

all_data = [['John', 'Emily', 'Michael', 'Mary', 'Steven'], 
            ['Maria', 'Juan', 'Javier', 'Natalia', 'Pilar']]

你可能是从一些文件得到的这些名字,然后想按照语言进行分类。现在假设我们想用一个列表包含所有的名字,这些名字中包含两个或更多的e。可以用for循环来做:

names_of_interest = []
for names in all_data:
    enough_es = [name for name in names if name.count('e') >= 2]
    names_of_interest.extend(enough_es)

可以用嵌套列表推导式的方法,将这些写在一起,如下所示:

In [169]: result = [name for names in all_data for name in names if name.count('e')>=2]

In [170]: result
Out[170]: ['Steven']

for部分是根据嵌套的顺序来写的,从外层的loop到内层的loop。这里一个例子是把tuple扁平化成一个整数列表:

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

一定要记住顺序是和我们写for loop一样的:

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

列表表达式里再有一个列表表达式也是可以的,可以生成a list of lists:

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

你可能感兴趣的:(《利用Python进行数据分析·第2版》【Chapter 3.1】Python 的数据结构)