利用Python进行数据分析·第2版(1)

第3章 Python的数据结构、函数和文件
3.1 数据结构和序列
1)元组
元组是一个固定长度,不可改变的Python序列对象。创建元组的最简单方式,是用逗号分隔一列值。

tuple1 = ((1,2,3),(4,5,6))
print(tuple1)
type(tuple1)
===========================
((1, 2, 3), (4, 5, 6))
tuple


tuple2 = tuple('string')
print(tuple2)
===============================
('s', 't', 'r', 'i', 'n', 'g')

元组中存储的对象可能是可变对象。一旦创建了元组,元组中的对象就不能修改了:
tup3 = tuple(['foo', [1,2], True])
tup3
==================================
('foo', [1, 2], True)

如果元组中的某个对象是可变的,比如列表,可以在原位进行修改:
tup3[1]
=========
[1, 2]

tup3[1].append(3)
tup3
==================
('foo', [1, 2, 3], True)

可以用加号运算符将元组串联起来:
>>> (4, None,'foo') + (6,8) + ('bar',)
(4, None, 'foo', 6, 8, 'bar')

元组乘以一个整数,像列表一样,会将几个元组的复制串联起来:
>>> ('foo', 'bar')*4
('foo', 'bar', 'foo', 'bar', 'foo', 'bar', 'foo', 'bar')

拆分元组
如果你想将元组赋值给类似元组的变量,Python会试图拆分等号右边的值:
>>> tup = (4, 5, 6)
>>> a, b, c = tup
>>> print(a, b ,c)
4 5 6
>>> a
4
>>> b
5
>>> c
6

即使含有元组的元组也会被拆分:
>>> tup = 4, 5, (6, 7)
>>> a, b, (c, d) = tup
>>> c
6
>>> d
7

变量拆分常用来迭代元组或列表序列:
>>> seq = [(1,2,3), (4,5,6), (7,8,9)]
>>> 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

Python最近新增了更多高级的元组拆分功能,允许从元组的开头“摘取”几个元素。它使用了特殊的语法*rest,这也用在函数签名中以抓取任意长度列表的位置参数:
>>> values = (1, 2, 3, 4, 5)
>>> a, b, *rest = values
>>> a
1
>>> b
2
>>> a,b
(1, 2)
>>> rest
[3, 4, 5]

rest的部分是想要舍弃的部分,rest的名字不重要。作为惯用写法,许多Python程序员会将不需要的变量使用下划线:
>>> a, b, *_ = values

因为元组的大小和内容不能修改,它的实例方法都很轻量。其中一个很有用的就是count(也适用于列表),它可以统计某个值得出现频率:
>>> tup = (1, 2, 2, 2, 2, 3, 4, 2,)
>>> tup.count(2)
5

2)列表
与元组对比,列表的长度可变、内容可以被修改。你可以用方括号定义,或用list函数:
>>> a_list = [2, 3, 7, None]
>>> tup = ('foo', 'bar', 'baz')
>>> b_list = list(tup)
>>> print(a_list, b_list)
[2, 3, 7, None] ['foo', 'bar', 'baz']
>>> b_list[1] = 'peekaboo'
>>> b_list
['foo', 'peekaboo', 'baz']

list函数常用来在数据处理中实体化迭代器或生成器:
>>> gen = range(10)
>>> gen
range(0, 10)
>>> list(gen)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

可以用append在列表末尾添加元素。
insert可以在特定的位置插入元素,插入的序号必须在0和列表长度之间。
insert的逆运算是pop,它移除并返回指定位置的元素,可以用remove去除某个值,remove会先寻找第一个值并除去。
用in可以检查列表是否包含某个值,否定in可以再加一个not。

与元组类似,可以用加号将两个列表串联起来。如果已经定义了一个列表,用extend方法可以追加多个元素。
>>> x = [4, None, 'foo']
>>> y = [7, 8, (2,3)]
>>> x.extend(y)
>>> x
[4, None, 'foo', 7, 8, (2, 3)]
>>> x.extend(['a', (4,5)])
>>> x
[4, None, 'foo', 7, 8, (2, 3), 'a', (4, 5)]

通过加法将列表串联的计算量较大,因为要新建一个列表,并且要复制对象。用extend追加元素,尤其是到一个大列表中,更为可取。

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

你可以用sort函数将一个列表原地排序(不创建新的对象),sort有一些选项,有时会很好用。其中之一是二级排序key,可以用这个key进行排序。例如,我们可以按长度对字符串进行排序:
>>> c_list = ['saw', 'small', 'He', 'foxes', 'six']
>>> c_list.sort(key=len)
>>> c_list
['He', 'saw', 'six', 'small', 'foxes']


二分搜索和维护已排序的列表
bisect模块支持二分查找,和向已排序的列表插入值。bisect.bisect可以找到插入值后仍保证排序的位置,bisect.insort是向这个位置插入值.
>>> import bisect
>>> d_list = [1, 2, 2, 2, 3, 4, 7]
>>> bisect.bisect(d_list, 2)
4
>>> bisect.bisect(d_list, 5)
6
>>> bisect.insort(d_list, 6)
>>> d_list
[1, 2, 2, 2, 3, 4, 6, 7]

切片

用切边可以选取大多数序列类型的一部分,切片的基本形式是在方括号中使用start:stop:

切片也可以被序列赋值:
>>> seq = [7, 2, 3, 7, 5, 6, 0, 1]
>>> seq[3:4] = [6,3]
>>> seq
[7, 2, 3, 6, 3, 5, 6, 0, 1]
切片的起始元素是包括的,不包含结束元素。因此,结果中包含的元素个数是stop - start

序列函数

Python有一些有用的序列函数。

enumerate函数

迭代一个序列时,你可能想跟踪当前项的序号。手动的方法可能是下面这样:
i = 0
for value in collection:
    #do something with value
    i += 1

因为这么做很常见,Python内建了一个enumerate函数,可以返回(i, value)元组序列:
for i,value in enumerate(collection)

当你索引数据时,使用enumerate的一个好方法是计算序列(唯一的)dict映射到位置的值:
>>> some_list = ['foo', 'bar', 'baz']
>>> mapping = {}
>>> for i,v in enumerate(some_list):
    mapping[v] = i

>>> mapping
{'foo': 0, 'bar': 1, 'baz': 2}

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

zip函数zip可以将多个列表、元组或其它序列成对组合成一个元组列表.
>>> seq1 = ['foo', 'bar', 'baz']
>>> seq2 = ['one', 'two', 'three']
>>> zip1 = zip(seq1, seq2)
>>> zip1
<zip object at 0x0000011E6F0F6D48>
>>> list(zip1)
[('foo', 'one'), ('bar', 'two'), ('baz', 'three')]

>>> 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可以被用来解压序列。也可以当作把行的列表转换为列的列表。
>>> pitchers
[('Nolan', 'Ryan'), ('Roger', 'Clemens'), ('Schilling', 'Curt')]
>>> f_names, l_names = zip(*pitchers)
>>> f_names
('Nolan', 'Roger', 'Schilling')
>>> l_names
('Ryan', 'Clemens', 'Curt')

reversed函数reversed可以从后向前迭代一个序列.
>>> list(reversed(range(10)))
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

要记住reversed是一个生成器(后面详细介绍),只有实体化(即列表或for循环)之后才能创建翻转的序列。

3)字典
字典可能是Python最为重要的数据结构。它更为常见的名字是哈希映射或关联数组。它是键值对的大小可变集合,键和值都是Python对象。创建字典的方法之一是使用尖括号,用冒号分隔键和值

你可以像访问列表或元组中的元素一样,访问、插入或设定字典中的元素
>>> d1 = {'a':'some value', 'b':[1,2,3,4]}
>>> d1
{'b': [1, 2, 3, 4], 'a': 'some value'}
>>> d1[7] = 'an interger'
>>> d1
{'b': [1, 2, 3, 4], 'a': 'some value', 7: 'an interger'}
>>> d1['b']
[1, 2, 3, 4]

可以用del关键字或pop方法(返回值的同时删除键)删除值:
>>> d1[5] = 'some value'
>>> d1['dummy'] = 'another value'
>>> d1
{'b': [1, 2, 3, 4], 'dummy': 'another value', 'a': 'some value', 5: 'some value', 7: 'an interger'}
>>> del d1[5]
>>> d1
{'b': [1, 2, 3, 4], 'dummy': 'another value', 'a': 'some value', 7: 'an interger'}
>>> ret = d1.pop('dummy')
>>> ret
'another value'
>>> d1
{'b': [1, 2, 3, 4], 'a': 'some value', 7: 'an interger'}

用update方法可以将一个字典与另一个融合:
>>> d1
{'b': [1, 2, 3, 4], 'a': 'some value', 7: 'an interger'}
>>> d1.update({'b':'foo', 'c':12})
>>> d1
{7: 'an interger', 'b': 'foo', 'a': 'some value', 'c': 12}

用序列创建字典
你可能想将两个序列配对组合成字典。下面是一种写法:
mapping = {}
for key,value in zip(key_list, value_list):
    mapping[key] = value

因为字典本质上是2元元组的集合,dict可以接受2元元组的列表:
>>> mapping = dict(zip(range(5), reversed(range(5))))
>>> mapping
{0: 4, 1: 3, 2: 2, 3: 1, 4: 0}

默认值
if key in some_dict:
    value = some_dict[key]
else:
    value = default_value

因此,dict的方法get和pop可以取默认值进行返回,上面的if-else语句可以简写成下面:
value = some_dict.get(key, default_value)

get默认会返回None,如果不存在键,pop会抛出一个例外。关于设定值,常见的情况是在字典的值是属于其它集合
>>> words = ['apple', 'bat', 'bar', 'atom', 'book']
>>> by_letter = {}
>>> for word in words:
    letter = word[0]
    if letter not in by_letter:
        by_letter[letter] = [word]
    else:
        by_letter[letter].append(word)

>>> by_letter
{'b': ['bat', 'bar', 'book'], 'a': ['apple', 'atom']}

4)集合
集合是无序的不可重复的元素的集合。你可以把它当做字典,但是只有键没有值。可以用两种方式创建集合:通过set函数或使用尖括号set语句。
集合支持合并、交集、差分和对称差等数学集合运算。
合并是取两个集合中不重复的元素。可以用union方法,或者|运算符:
>>> a = {1, 2, 3, 4, 5}
>>> b = {3, 4, 5, 6, 7, 8}
>>> a.union(b)
{1, 2, 3, 4, 5, 6, 7, 8}
交集的元素包含在两个集合中。可以用intersection或&运算符:
>>> a.intersection(b)
{3, 4, 5}

5)列表、集合和字典推导式
列表推导式是Python最受喜爱的特性之一。它允许用户方便的从一个集合过滤元素,形成列表,在传递参数的过程中还可以修改元素。形式如下:
[expr for val in collection if condition]

它等同于下面的for循环:
result = []
for val in collection:
    if condition:
        result.append(expr)

==================================================
>>> strings = ['a', 'as', 'bat', 'car', 'dove', 'python']
>>> [x.upper() for x in strings if len(x) > 2]
['BAT', 'CAR', 'DOVE', 'PYTHON']
>>> result = []
>>> for x in strings:
    if len(x) > 2:
        result.append(x.upper())
        
>>> result
['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}

=================================================
>>> unique_lengths = {len(x) for x in strings}
>>> unique_lengths
{1, 2, 3, 4, 6}

6)嵌套列表推导式
假设我们有一个包含列表的列表,包含了一些英文名和西班牙名:
==================================================
>>> all_data = [['John', 'Emily', 'Micheal', '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)
    
>>> names_of_interest
['Steven']

可以用嵌套列表推导式的方法,将这些写在一起,如下所示:
>>> result = [name for names in all_data for name in names if name.count('e') >= 2]
>>> result
['Steven']

猜你喜欢

转载自blog.csdn.net/zhaocen_1230/article/details/82022695