本文是我个人学习《利用 Python 进行数据分析》一书的笔记。整个系列于今日起连载。
1.1 数据结构
Python 的基本数据结构包括元组、列表、字典、集合,此外还有一些特殊的数据结构(如 range 对象、字符串等)。
1.1.1 元组(tuple)
什么是元组?元组是固定长度、内容不可改变的序列。
如何创建元组?
# 用逗号分隔是创建元组最简单的方法
tup = 1, 2, 3
tup = "a", "b", "any"
tup = (2, 3, 4), (5, 6)
# 用 tuple() 可以将一个序列转换为元组
tup = tuple([2, 3, 4])
元组的内容是不可改变的,这句话如何理解呢?
in: tup
out: (2, 3, 4)
in: tup[0] = 5 # 该行命令是无法运行的,这是因为元组内的值是不能改变的
# 但是,如果元组内的某对象是可以改变的,可以在其对应位置上进行修改
in: tup = (1, [1, 2, 3], 2)
tup[1].append(4)
tup
out: (1, [1, 2, 3, 4], 2)
虽然元组的内容是不可改变的,但是元组可以串联和拆分。
# 串联元组
(1, 2, 4) + ('haha', [2, 3], True) # 利用加号可以串联元组
(1, 2) * 3 # 利用乘号可以对元组进行复制串联
# 拆分元组
a, b, c = (1, 2, 3)
a, b, (c, d) = (1, 2, (3, 4)) # 甚至元组内的元组也会被拆分
in: info = 'name', 'age', 'job', 'zipcode'
a, b, *_ = info # 可以从元组的开头选取想要的值,其余的值保存在 *_ 中
print('a={}, b={}'.format(a, b))
out: a=name, b=age
元组的方法不多,常用的是 count()。
in: num = 1, 2, 2, 2, 3, 4, 5
num.count(2) # count() 可以用于计数:计算元组对象中某个值出现的总次数
out: 3
1.1.2 列表(list)
与元组不同,列表的长度是可变的,内容也可以被修改。
如何创建列表?
list_fruit = ['apple', 'banana', 'orange', 'orange']
tup = 'apple', 'banana', 'orange', 'orange'
list_fruit = list(tup) # 可以利用 list() 将其他序列转换为列表
列表的可变性,让我们能够对列表进行诸多操作。
- 向列表中添加或移除元素:
# 添加元素
list_fruit.append('pear') # 在列表的末端添加元素
list_fruit.insert(1, 'durian') # 在列表的特定位置添加元素
# insert 比 append 计算量大,因为插入后,后续元素的引用必须在内部迁移
# 移除元素
list_fruit.pop(1) # 移除并返回指定位置的元素
list_fruit.remove('orange') # 移除指定的元素(从第一个开始,一次移除一个)
# 列表的串联
list_fruit + ['melon', 'grape']
list_fruit.extend(['durian', 'watermelon']) # 可以利用 extend() 追加多个元素
# 加法计算量更大,因为加法创建了一个新的列表,而 extend() 是在原列表上追加元素
- 排序
# sort() 可以将一个列表原地排序(而不创建新的对象)
in: a = [2, 4, 6, 3, 1]
a.sort()
a
out: [1, 2, 3, 4, 6] # 默认是升序排列
in: b = ['hah', 'kdjalkdjalk', 'd', 'ldkld']
b.sort(key=len) # 可以通过指定 key 参数,按元素长度排列
b
out: ['d', 'hah', 'ldkld', 'kdjalkdjalk']
# sorted() 也可以排序,但不会改变原序列,而是创建一个新列表
in: a = [1, 5, 7, 3, 4]
print('sorted_a = ',sorted(a))
print('a = ',a)
out: sorted_a = [1, 3, 4, 5, 7]
a = [1, 5, 7, 3, 4]
- 切片
in: seq = [1, 2, 4, 6, 2, 4, 8, 9]
seq[1:4]
out: [2, 4, 6]
in: seq[0:2] = [100, 60] # 可以直接对切片赋值,会改变原来的列表
seq
out: [100, 60, 4, 6, 2, 4, 8, 9]
in: seq[::2] # 在第二个冒号后面使用 step,可以隔一段距离取元素
seq[::-1] # 这种方法可以把列表颠倒过来
out: [100, 4, 2, 8]
[9, 8, 4, 2, 6, 4, 60, 100]
# PS: 元组也可以切片 :)
- enumerate() 序列函数
# 在迭代时,我们常常希望知晓当前项的序号,因此我们可能会这样写:
i = 0
for value in list_fruit:
print('第{}个元素是:{}'.format(i, value))
i += 1
# 上述代码可以被简化:
for i, value in enumerate(list_fruit): # 可以返回 (i, value) 样式的元组
print('第{}个元素是:{}'.format(i, value))
- zip() 成对组合
# zip() 可以将多个序列成对组合成一个 zip 对象(元祖列表)
in: seq1 = [1, 2, 3]
seq2 = ['happy', 'sad', 'angry', 'peace']
zipped = zip(seq1, seq2)
zipped
out: # 这是一个迭代器,可以调用 __next__()
in: list(zipped) # 利用 list() 将迭代器实体化
out: [(1, 'happy'), (2, 'sad'), (3, 'angry')] # 可以看到,zip 后元组的个数取决于最短的序列
- reversed() 颠倒序列
in: seq1 = [1, 2, 3]
r = reversed(seq1) # reversed 函数生成了一个 list_reverseiterator 对象——从后向前迭代的一个迭代器
list(r) # 想看到内容,还是需要使用 list() 将迭代器对象实体化
out: [3, 2, 1]
1.1.3 字典(dict)
字典的用法我记录得较为零散,直接罗列在下面:
# 可以用 in 检查字典中是否包含某个键
in: dict_test1 = {'a': 'hahaha', 'b': [1, 3, 4],3:(12,3)}
'a' in dict_test1
out: True
# 将键值移出字典
del dict_test1['b'] # del 可以删除一对键值
dict_test1.pop(3) # pop() 可以删除一对键值,并返回被删除的值
# 得到键列表和值列表
list(dict_test1.keys()) # 学会使用 list() 函数,可以将一些特殊序列实体化
list(dict_test1.values())
# dict_test1.keys() 得到的是一个 dict_keys 序列,它不是一个 list,不能用下标访问
keys = dict_test1.keys()
keys[0] # 该条命令不能运行
list(keys)[0] # 这样才可以
# 字典的更新
# update 函数可以更新字典(存在的键,值被替换;不存在的键,追加新的键值)
in: d = {'a':123,'b':456}
d.update({'b': 789, 'c': '更新'})
out: {'a': 123, 'b': 789, 'c': '更新'}
1.1.4 集合(set)
集合是一个无序的、不可重复的元素集。其概念类似于数学中的集合,可以进行合并、交集等数学运算。
# 创建集合
in: {2, 2, 2, 1, 3, 3}
set([2, 2, 2, 1, 3, 3]) # set() 可以把其他序列转为集合
out: {1, 2, 3} # 集合是不可重复的
a = {1, 2, 3, 4, 5}
b = {3, 4, 5, 6, 7, 8}
# 并集
a.union(b)
a | b
# 交集
a.intersection(b)
a & b
1.1.5 range 对象
range() 是在 for 循环中运用很频繁的函数,我在测试代码的时候偶然发现,range() 建立的对象既不是元组,也不是列表,而是独特的 range 对象。
range 对象像元组一样,其内容是不可修改的;但 range 对象不能像元组一样存储不同类型的内容,只能存储一个等差数列。
range 对象、字符串、元组、列表、字典、集合,都是可迭代的对象。我们可以使用 iter() 建立一个迭代器。
# 以 range 对象为例
in: a = range(2)
iter_test = iter(a) # 基于可迭代的对象,创建一个迭代器
iter_test
out:
in: iter_test.__next__() # 迭代器都有 __next__() 方法,可以返回下一个元素
out: 0
in: iter_test.__next__()
out: 1
in: iter_test.__next__()
out: Traceback (most recent call last):
File "", line 1, in
StopIteration # 迭代器是一次性使用的,从头迭代到尾,然后就不能再迭代了
for 循环的本质就是基于可迭代的对象建立一个迭代器,依次迭代到末尾。
1.1.6 列表推导式
可以用列表推导式简化 for 循环代码:
in: string = ['a', 'apple', 'orange', 'banana']
[i.upper() for i in string if len(i) > 2]
out: ['APPLE', 'ORANGE', 'BANANA']
推导式还可以嵌套:
in: all_data = [['Eureka', 'Nirvash', 'Landon'], ['Bill', 'Steve']]
[name for names in all_data for name in names if len(name) > 5]
out: ['Eureka', 'Nirvash', 'Landon']
1.2 函数
Python 中的函数有几个特殊的使用方法:
# 函数可以返回多个值
def f():
a = 5
b = 6
c = 7
return a, b, c
a, b, c = f()
# Lambda 函数(匿名函数)
def short_function(x):
return x * 2
equiv_anon = lambda x: x * 2 # 这个匿名函数和上面的常规函数有相同的作用
# 函数作为参数传递到另一个函数
def apply_to_list(some_list, f): # 此处的 f 是一个函数
return [f(x) for x in some_list]
ints = [4, 0, 1, 5, 6]
apply_to_list(ints, lambda x: x * 2) # 此处我们把一个匿名函数作为参数传进去了
1.3 文件操作
Python 中可以新建、打开本地文件。我更偏好下面这种方式:
with open('tmp.txt', 'a') as f: # a:追加模式
f.write('add some words.')