0. 概述

一共包括三个第三方库:numpy,matplotlib,pandas
目的是入个门,以后慢慢看文档啥的

主要参考内容:Python for Data Analysis, 2nd Edition

1. Numpy

ndarray(array) 即多维数组,是 numpy 的核心概念,其有如下属性:

属性 作用
ndarray.ndim 多维数组的数组
ndarray.shape 多维数组的形状
ndarray.size 所有元素在内存中的大小
ndarray.dtype 元素的数据类型
ndarray.itemsize 每个元素的大小
ndarray.data 数据缓冲区

数组中元素的数据类型参考:NumPy 数据类型
更详细的内容可以参考 Numpy 的教程:NumPy 基础
关羽 ndarray 的各种例程可以参考:Numpy routine

1.1 ndarray 的创建

常用的五种创建方法:

  • 从 python 数据中创建
  • 直接创建一些特殊的数组(全零,全一等)
  • 从磁盘读取
  • 从字节流创建
  • 使用库函数创建

1.1.1 创建特殊数组

后缀为_like的函数为根据给定的数组创建同形状的数组

函数 作用
empty/empty_like 未初始化数组
eye 单位矩阵数组
ones/ones_like 全 1 数组
zeros/zeros_like 全 0 数组
full/full_like 创建数组并填充给定内容

1.1.2 其他创建方法

最常用的方法就是从 python 的可迭代对象中创建

函数 作用
array 创建数组
asarray 尽量不复制创建数组
frombuffer 从字节流读取数组
fromfile 从文件读取数组
fromstring 从字符串创建数组
arange 数组版 range

1.2 ndarray 的运算

  • 数组与标量进行运算,会用每个元素进行这个运算
  • 相同形状数组之间的加减乘除,会转换成对应元素运算
  • 相同形状数组之间比较,会生成布尔数组

不同形状的数组之间的运算会通过广播机制转换成相同形状的数组之间的运算:

广播会逐个检查各个维度,并在这个维度上进行广播:

  • 如果当前维度两个数组的值相同,则无需广播
  • 如果当前维度两个数组的值不同,且都不为 1,则无法广播触发错误
  • 如果当前维度两个数组的值不同,但有一个为 1,则小数组沿着此维度运算时都用此维度上的第一组值

1.3 ndarray 索引与切片

和 python 中的切片类似,基本操作是一样的,所以部分内容略过

同时也有一些不同:

  • numpy 中的切片不会复制数组的内容,而是创建一个引用(即视图)
  • 通过逗号合并多级引用

    arr2d[0][2]arr2d[0, 2]是等价的

  • 布尔索引

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # 定义数据数组
    data = np.random.randn(7, 4)
    # 定义一个数组,其长度需要与目标数组被选择的维度长度相同
    names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])

    # 布尔索引数组
    names == 'Bob'
    # array([ True, False, False, True, False, False, False])

    # 通过布尔索引内容
    data[names=='Bob']
    # array([[ 0.52414459, 1.54335133, 0.16256081, -0.15833941],
    # [-0.62186793, 0.27771581, -0.61924424, 0.58594636]])
  • 花式索引

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    arr = np.empty((8, 4))
    for i in range(8):
    arr[i] = i

    # 单个花式索引
    # 负数为从末尾开始
    arr[[4, 3, 0, 6]]
    # array([[4., 4., 4., 4.],
    # [3., 3., 3., 3.],
    # [0., 0., 0., 0.],
    # [6., 6., 6., 6.]])

    # 多个花式索引
    # 取两个坐标确定的点的元素
    arr = np.arange(32).reshape((8, 4))
    arr[[1, 5, 7, 2], [0, 3, 1, 2]]
    array([ 4, 23, 29, 10])

    # 结合逗号和花式索引
    arr[[1, 5, 7, 2]][:, [0, 3, 1, 2]]
    # array([[ 4, 7, 5, 6],
    # [20, 23, 21, 22],
    # [28, 31, 29, 30],
    # [ 8, 11, 9, 10]])

    1.4 ndarray 的转置

一个多维的数组可以看成一个有多个轴的张量
其转置即轴的替换,而对于二维的张量(即矩阵),为对角交换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
arr = np.arange(16).reshape((2, 2, 4))
# array([[[ 0, 1, 2, 3],
# [ 4, 5, 6, 7]],
# [[ 8, 9, 10, 11],
# [12, 13, 14, 15]]])

# 原本的 0 轴变成 1 轴,1 轴 变成 0 轴
arr.transpose((1, 0, 2))
# array([[[ 0, 1, 2, 3],
# [ 8, 9, 10, 11]],
# [[ 4, 5, 6, 7],
# [12, 13, 14, 15]]])

# 对于矩阵来说,可以直接使用.T
arr = np.array([[1,2],[3,4]])
arr.T
# array([[1, 3],
# [2, 4]])

1.5 ndarray 的通用函数

通用函数可以看成是普通元素级函数的向量化包装
参考资料:ndarray 通用函数

部分函数如下:

一元函数 作用
abs/fabs 取绝对值
sqrt 开方
square 平方
exp 自然指数幂
log/log10/log2/log1p 各种对数
sign 符号函数
ceil/floor 计算上下整数
rint 四舍五入
modf 把小数和整数分成两个数组
isnan 判断非数字(返回布尔数组)
isfinite/isinf 判断是否是无限
cos/cosh/sin/sinh/tan/tanh 普通/双曲 三角函数
arcos/arcosh/… 反三角函数
二元函数 作用
add/subtract 加减
multiply/divide 乘除
foor_divide 除后向下取整
power 指数
maximum/fmax 取最大
minimum/fmin 取最小
mod 元素级取余
copysign 复制符号
比较与逻辑函数 作用
greater/greater_equal/… 比较操作
logical_and/… 逻辑操作

1.6 ndarray 版三元表达式

python 本身是不支持三元表达式的,同时用 python 的 if-else 做又太慢(当数组元素非常多的时候)
ndarray 中有一个函数提供了三元表达式的功能:where

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 对两个数组使用三元表达式
xarr = np.array([1, 1, 1, 1, 1])
yarr = np.array([2, 2, 2, 2, 2])
cond = np.array([True, False, True, True, False])
result = np.where(cond, xarr, yarr)
# array([1, 2, 1, 1, 2])

# 对标量使用三元表达式(结合随机生成的矩阵)
arr = np.random.randn(4, 4)
np.where(arr > 0, 2, -2)
# array([[ 2, 2, -2, -2],
# [-2, 2, 2, -2],
# [-2, -2, -2, 2],
# [ 2, -2, 2, -2]])

# 对数组和标量使用三元表达式
arr = np.random.randn(4, 4)
np.where(arr > 0, 2, arr)
# array([[-1.51898509, -0.49276267, -0.55755485, -0.19935523],
# [ 2. , -0.55909959, -0.41529875, -2.45146259],
# [-0.04827576, 2. , -1.12266989, 2. ],
# [-0.65111798, -0.19119092, -1.0972157 , 2. ]])

1.7 ndarray 统计方法

这些函数可以直接通过 ndarray 对象调用

函数 作用
mean 取平均(可指定轴)
sum 取和
std,var 标准差和方差(可调自由度)
min,max 最小最大值
cumsum 累计和
cumprod 累计积
any 布尔数组有任意一个值为 True(其他类型自动转化)
all 布尔数组所有值都为 True
sort 排序

1.8 ndarray 间操作

函数 作用
unique(x) 计算给定数组中的唯一元素,并返回有序结果
intersect1d(x,y) 求两个给定数组的唯一交集,返回有序结果
union1d(x,y) 求两个数组的唯一并集,返回有序结果
in1x(x,y) 返回布尔数组,指定 x 中元素是否在 y 中
setdiff1d(x,y) 在 x 中不在 y 中的元素
setxor1d(x,y) 并集减交集

1.9 ndarray 的保存与读取

1
2
3
4
5
6
7
8
9
10
11
# 保存数组到文件(会自动加上.npy扩展名)
np.save('some_array', arr)
# 从文件中读取数组
np.load('some_array.npy')

# 保存多个数组后读取(可以以压缩格式保存)
np.savez('array_archive.npz', a=arr, b=arr)
# np.savez_compressed('arrays_compressed.npz', a=arr, b=arr)
arch = np.load('array_archive.npz')
arch['a']
arch['b']

1.10 ndarray 线性代数

函数 作用
diag 矩阵求对角元素向量/向量转对角矩阵
dot 矩阵乘法(可以用@符号简化)
trace 求对角元素之和
det 求矩阵行列式
eig 求本征值和本征向量
inv 求矩阵的逆
pinv 求矩阵的 Moore-Penrose 伪逆
qr 求矩阵的 QR 分解
svd 求奇异值分解
solve 解线性方程 Ax=b,A 为矩阵
lstsq 计算 Ax=b 的最小二乘解

1.11 伪随机数生成

参考资料:Numpy 随机数生成

numpy 的伪随机数与 python 本身的基本一致
通过RandomState可以创建一个隔离的随机数生成器

1
2
rng = np.random.RandomState(1234)
rng.randn(10)

随机生成器的状态

函数 作用
get_state 获取生成器的状态(一个元组)
set_state 设置生成器状态

随机数生成函数

函数 作用
rand 生成指定形状的数组
randn 以正态分布生成指定形状的数组
randint 生成整数
random_sample 批量生成[0,1)的浮点数
choice 从给定数组中生成随机数
bytes 生成随机字节
shuffle 打乱一个数组
permutation 打乱一个数组或生成一个随机数组

根据指定分布生成随机样本:指定分布随机样本

2. Pandas

Pandas 是一个用来处理结构化数据的库,尤其擅长处理各种表格数据
其核心类有两个:一维数组Series,表格型数组DataFrame

参考资料:
User Guide
User Guide 中文版
Api Reference
Api Reference 中文版

2.1 Series

Series 即一个一维数组,同时其每个元素对应一个索引(index).如果没有指定索引,Series 会自动生成一个从零开始的整型索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import pandas as pd
import numpy as np

# ===============================
# ---------- 自动索引 -----------
# ===============================
data = pd.Series([4, 7, -5, 3])
print(data)
# 自动添加整型索引
# 0 4
# 1 7
# 2 -5
# 3 3
# dtype: int64
print(data.values)
# [ 4 7 -5 3]
print(data.index)
# RangeIndex(start=0, stop=4, step=1)

# ===============================
# ---------- 手动索引 -----------
# ===============================
# 提供的值的数量和索引数量不同的时候会触发错误
data = pd.Series([4, 7, -5, 3], index=['d', 'b', 'a', 'c'])
# 也可以进行修改
# data.index = ['d', 'b', 'a', 'c']
print(data)
# 自动添加整型索引
# d 4
# b 7
# a -5
# c 3
# dtype: int64
print(data.values)
# [ 4 7 -5 3]
print(data.index)
# Index(['d', 'b', 'a', 'c'], dtype='object')

# ===============================
# ---------- 索引方法 -----------
# ===============================
# 可以通过数字索引和手动索引找到数据
print(data[0] == data['d'])
# True

# 和numpy一样可以进行布尔索引
print(data[data > 2].values)
# [4 7 3]

# ===============================
# ---------- 标量计算 ------------
# ===============================
# 标量计算和numpy是一样的
print((data*2).values)
# [ 8 14 -10 6]

# 还支持和numpy的联动
print(np.exp(data).values)
# [5.45981500e+01 1.09663316e+03 6.73794700e-03 2.00855369e+01]

# ===============================
# ---------- 集合与字典 ----------
# ===============================
# Series可以看成是一个以索引为键的字典
# 如果索引不存在的键,则会触发KeyError
print('d' in data)
print(data['d'])
# print(data['ppp'])
# True
# 4

# 可以直接用字典创建Series
sdata = {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000}
states = ['California', 'Ohio', 'Oregon', 'Texas']
# 直接用字典创建,会以字典的键为序(雾,键有序吗?)
data = pd.Series(sdata)
print(data)
# Ohio 35000
# Texas 71000
# Oregon 16000
# Utah 5000
# dtype: int64
# 可以额外传入一个索引数组,指定顺序
# 如果字典中的键不再索引数组中,则忽略这个键值对
# 如果索引数组中的元素不是字典的键,则对应的值为Nan(缺失值)
data = pd.Series(sdata, index=states)
print(data)
# California NaN
# Ohio 35000.0
# Oregon 16000.0
# Texas 71000.0
# dtype: float64

# ===============================
# ---------- 缺失值处理 ----------
# ===============================
# 在后面会有专门部分讲这个
# pandas本身有isnull和notnull用来检测缺失值,这两个函数Series本身可以直接调用
print(pd.isnull(data))
# California True
# Ohio False
# Oregon False
# Texas False
# dtype: bool
print(data.notnull())
# California False
# Ohio True
# Oregon True
# Texas True
# dtype: bool

# ===============================
# ------------ 命名 -------------
# ===============================
# 名命与Series的一些功能有关,后面会说
data.name = 'polulation'
data.index.name = 'state'
print(data)
# state
# California NaN
# Ohio 35000.0
# Oregon 16000.0
# Texas 71000.0
# Name: polulation, dtype: float64

2.2 DataFrame

DataFrame 是一个表格型的数据结构,可以先看成是多个共用索引的 Series 对象构成的字典

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import pandas as pd
import numpy as np


# ===============================
# --------- 创建DataFrame --------
# ===============================
# 首先可以用嵌套字典来创建(字典嵌套Series同理)
# 外层为列索引,里层为行索引,缺失的数据使用缺失值填充
data = {'Nevada': {2001: 2.4, 2002: 2.9},
'Ohio': {2000: 1.5, 2001: 1.7, 2002: 3.6}}
frame = pd.DataFrame(data)
print(frame)
# Nevada Ohio
# 2001 2.4 1.7
# 2002 2.9 3.6
# 2000 NaN 1.5

# 最常用的还是字典套列表
data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada', 'Nevada'],
'year': [2000, 2001, 2002, 2001, 2002, 2003],
'pop': [1.5, 1.7, 3.6, 2.4, 2.9, 3.2]}
frame = pd.DataFrame(data)
print(frame)
# state year pop
# 0 Ohio 2000 1.5
# 1 Ohio 2001 1.7
# 2 Ohio 2002 3.6
# 3 Nevada 2001 2.4
# 4 Nevada 2002 2.9
# 5 Nevada 2003 3.2

# 可以通过head函数和tail函数查看一个DataFrame的部分数据(前后5行)
# print(frame.head())
# print(frame.tail())

# 创建DataFrame时,如果指定索引,则会按照索引排序
# 如果传入的列索引在数据中找不到,则填充为缺失值
# 如果已有的数据没有在传入的列中指定,删除这个数据
print(pd.DataFrame(data, columns=['year', 'state', 'pop']))
# year state pop
# 0 2000 Ohio 1.5
# 1 2001 Ohio 1.7
# 2 2002 Ohio 3.6
# 3 2001 Nevada 2.4
# 4 2002 Nevada 2.9
# 5 2003 Nevada 3.2


# ===============================
# ----------- 获取列数据 ----------
# ===============================
# 可以通过字典索引或者属性索引的方式获取列数据
# 获得的数据是一个Series对象,同时其名字会自动设置
print(frame['year'])
print(frame.year)
# 0 2000
# 1 2001
# 2 2002
# 3 2001
# 4 2002
# 5 2003
# Name: year, dtype: int64

# ===============================
# ----------- 获取行数据 ----------
# ===============================
# 可以通过索引获取行数据
# 手动设置索引后无法使用原本的索引
print(frame.loc[1])
# state Ohio
# year 2001
# pop 1.7
# Name: 1, dtype: object
frame = pd.DataFrame(data, columns=['year', 'state', 'pop', 'debt'],
index=['one', 'two', 'three', 'four', 'five', 'six'])
print(frame.loc['one'])
# print(frame.loc[1]) 此时会引发错误
# 但是可以使用iloc函数替代(恒有效)
# print(frame.iloc[1])
# 为了得到指定坐标的标量,可以使用at/iat函数获取指定位置元素
# 这两个函数和loc/iloc函数是相似的
# year 2000
# state Ohio
# pop 1.5
# debt NaN
# Name: one, dtype: object


# ===============================
# ----------- 修改数据 -----------
# ===============================
# 直接赋值
frame['debt'] = 0
print(frame)
# year state pop debt
# one 2000 Ohio 1.5 0
# two 2001 Ohio 1.7 0
# three 2002 Ohio 3.6 0
# four 2001 Nevada 2.4 0
# five 2002 Nevada 2.9 0
# six 2003 Nevada 3.2 0

# 如果目标属性不存在,则会创建一个新的列
# 这种情况下,不能使用属性来创建列
frame['GDP'] = np.arange(6.)

# 使用del关键字可以删除一个列
del frame['GDP']

# 索引数量和传入数据数量不匹配也会触发错误
# frame['debt'] = np.arange(7.)
frame['debt'] = np.arange(6.)
print(frame)
# year state pop debt
# one 2000 Ohio 1.5 0.0
# two 2001 Ohio 1.7 1.0
# three 2002 Ohio 3.6 2.0
# four 2001 Nevada 2.4 3.0
# five 2002 Nevada 2.9 4.0
# six 2003 Nevada 3.2 5.0

# 可以传入Series对象,没有的数据设成缺失值,多余的数据会忽略
val = pd.Series([-1.2, -1.5, -1.7, 3.3], index=['two', 'four', 'five', 'nine'])
frame['debt'] = val
print(frame)
# year state pop debt
# one 2000 Ohio 1.5 NaN
# two 2001 Ohio 1.7 -1.2
# three 2002 Ohio 3.6 NaN
# four 2001 Nevada 2.4 -1.5
# five 2002 Nevada 2.9 -1.7
# six 2003 Nevada 3.2 NaN

# 对于DataFrame可以执行转置操作,交换行列顺序
print(frame.T)
# one two three four five six
# year 2000 2001 2002 2001 2002 2003
# state Ohio Ohio Ohio Nevada Nevada Nevada
# pop 1.5 1.7 3.6 2.4 2.9 3.2
# debt NaN -1.2 NaN -1.5 -1.7 NaN

# 最后,DataFrame也可以对本身以及行列索引名命
frame.name = "frame"
frame.columns.name = 'state'
frame.index.name = 'year'

# 可以轻松把DataFrame转化成Numpy的数组
print(frame.values)
# <class 'numpy.ndarray'>
# [[2000 'Ohio' 1.5 nan]
# [2001 'Ohio' 1.7 -1.2]
# [2002 'Ohio' 3.6 nan]
# [2001 'Nevada' 2.4 -1.5]
# [2002 'Nevada' 2.9 -1.7]
# [2003 'Nevada' 3.2 nan]]

2.3 索引对象

索引对象即 Series 对象和 DataFrame 对象中索引
为了使得多个数据结构中可以共享同一个索引对象,索引对象内的元素是不可修改的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import pandas as pd
import numpy as np

labels = pd.Index(np.arange(3))
print(labels)
# Int64Index([0, 1, 2], dtype='int64')

# 索引内容不可修改
# labels[0] = 5

# 同时索引内容是可以重复的
row = pd.Index(['foo', 'foo', 'bar', 'bar'])
col = pd.Index(['one', 'two', 'three', 'four'])
data = [[1, 2, 3, 4], [4, 5, 6, 7], [9, 10, 11, 12], [13, 14, 15, 16]]
frame = pd.DataFrame(data, index=row, columns=col)
print(frame)
# one two three four
# foo 1 2 3 4
# foo 4 5 6 7
# bar 9 10 11 12
# bar 13 14 15 16

# 在选择的重复元素的时候,重复的行都会被选到
print(frame.loc['foo'])
# one two three four
# foo 1 2 3 4
# foo 4 5 6 7
print(frame.T['foo'])
# foo foo
# one 1 4
# two 2 5
# three 3 6
# four 4 7

然后索引对象有一系列操作方法(类似 python 中的集合)
同时以下函数依然不会修改本身的元素,而是重新创造一个新的对象

函数 作用
append 连接集合
difference 计算差集
intersection 计算交集
union 计算并集
isin 计算索引中元素是否在目标中
delete 删除指定位置元素
drop 删除指定元素
insert 在指定位置插入元素
is_monotonic 查询是否递增
is_unique 查询键是否唯一
unique 合并重复项

2.4 重新索引

除了在初始化的时候进行索引,可以通过其他方法更改一个 Series/DataFrame 对象的索引(同时修改各个元素的顺序)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import pandas as pd
import numpy as np


# ===============================
# ------------ Series -----------
# ===============================
# 重新索引会连带更改元素的顺序
# 原本没有的索引位置会填充缺失值
# 但是不会原地修改Series对象,而是创建一个新对象
series = pd.Series([4.5, 7.2, -5.3, 3.6],
index=['d', 'b', 'a', 'c'])
print(series.values)
print(series.reindex(['b', 'c', 'd', 'e']).values)
print(series.values)
# [ 4.5 7.2 -5.3 3.6]
# [7.2 3.6 4.5 nan]
# [ 4.5 7.2 -5.3 3.6]

# 对于一些时间序列,可以通过method选项选择插值填充方法
series = pd.Series(['blue', 'purple', 'yellow'], index=[0, 2, 4])
print(series)
series = series.reindex(range(6), method='ffill')
print(series)
# 0 blue
# 2 purple
# 4 yellow
# dtype: object
# 0 blue
# 1 blue
# 2 purple
# 3 purple
# 4 yellow
# 5 yellow
# dtype: object

# ===============================
# ---------- DataFrame ----------
# ===============================
frame = pd.DataFrame(np.arange(9).reshape((3, 3)),
index=['a', 'c', 'd'],
columns=['Ohio', 'Texas', 'California'])
# 默认情况下,DataFrame重新索引会重新修改行索引
print(frame.reindex(['a', 'b', 'c', 'd']))
# Ohio Texas California
# a 0.0 1.0 2.0
# b NaN NaN NaN
# c 3.0 4.0 5.0
# d 6.0 7.0 8.0
# 可以指定重新索引列索引
print(frame.reindex(columns=['Texas', 'Utah', 'California']))
# Texas Utah California
# a 1 NaN 2
# c 4 NaN 5
# d 7 NaN 8
# 也可以同时修改
print(frame.reindex(['a', 'b', 'c', 'd'],
columns=['Texas', 'Utah', 'California']))
# Texas Utah California
# a 1.0 NaN 2.0
# b NaN NaN NaN
# c 4.0 NaN 5.0
# d 7.0 NaN 8.0

2.5 算术运算和数据对齐

一些算术运算时的基本规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import pandas as pd
import numpy as np


# ===============================
# ----------- 算数运算 -----------
# ===============================
# Series的算术运算会变成对应元素运算,同时在不重叠的部分填充缺失值
s1 = pd.Series([7.3, -2.5, 3.4, 1.5],
index=['a', 'c', 'd', 'e'])
s2 = pd.Series([-2.1, 3.6, -1.5, 4, 3.1],
index=['a', 'c', 'e', 'f', 'g'])
print(s1+s2)
# a 5.2
# c 1.1
# d NaN
# e 0.0
# f NaN
# g NaN
# dtype: float64
print(s1*s2)
# a -15.33
# c -9.00
# d NaN
# e -2.25
# f NaN
# g NaN
# dtype: float64

# 对于DataFrame,基本和Series是一样的,所以略过
df1 = pd.DataFrame(np.arange(9.).reshape((3, 3)), columns=list('bcd'),
index=['Ohio', 'Texas', 'Colorado'])
df2 = pd.DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'),
index=['Utah', 'Ohio', 'Texas', 'Oregon'])

# ===============================
# ------------ 填充值 ------------
# ===============================
# 可以使用数据结构对象的函数代替运算符, 并在其中填充fill_value选项来填充默认值
# DataFrame同理
print(s1.add(s2, fill_value=0))
# a 5.2
# c 1.1
# d 3.4
# e 0.0
# f 4.0
# g 3.1
# dtype: float64

# ===============================
# ---- Series与DataFrame运算 -----
# ===============================
frame = pd.DataFrame(np.arange(12.).reshape((4, 3)),
columns=list('bde'),
index=['Utah', 'Ohio', 'Texas', 'Oregon'])
series = frame.iloc[0]
print(series)
# b 0.0
# d 1.0
# e 2.0
print(frame)
# b d e
# Utah 0.0 1.0 2.0
# Ohio 3.0 4.0 5.0
# Texas 6.0 7.0 8.0
# Oregon 9.0 10.0 11.0
# Name: Utah, dtype: float64
# 二者运算会执行和Numpy一样的广播操作
print(series + frame)
# b d e
# Utah 0.0 2.0 4.0
# Ohio 3.0 5.0 7.0
# Texas 6.0 8.0 10.0
# Oregon 9.0 11.0 13.0


# 默认情况下,Series和DataFrame运算会在行上匹配
# 可以通过函数指定轴完成在列上匹配
print(frame.add(series, axis='index'))
# b d e
# Ohio NaN NaN NaN
# Oregon NaN NaN NaN
# Texas NaN NaN NaN
# Utah NaN NaN NaN
# b NaN NaN NaN
# d NaN NaN NaN
# e NaN NaN NaN

2.6 函数和映射

首先 Numpy 里的通用函数都可以作用到 Pandas 的对象里(参考 1.5)
然后 Pandas 对象可以根据函数对象对齐数据进行处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import pandas as pd
import numpy as np

frame = pd.DataFrame(np.random.randn(4, 3), columns=list('bde'),
index=['Utah', 'Ohio', 'Texas', 'Oregon'])
print(frame)
# 会随机生成,后面的不是这些值
# b d e
# Utah -1.334661 0.880250 -1.826803
# Ohio -0.892157 -2.210526 -0.621231
# Texas 0.134015 -0.538865 0.216243
# Oregon 0.865990 1.895041 -1.10335

# ===============================
# ------- apply/applymap --------
# ===============================
# f = lambda x: x.max() - x.min()


def f(x): return x.max() - x.min()


# 默认情况就是 index
print(frame.apply(f, axis='index'))
# b 1.815567
# d 2.726259
# e 2.515571
# dtype: float64
print(frame.apply(f, axis='columns'))
# Utah 1.932741
# Ohio 1.658354
# Texas 1.182421
# Oregon 3.461843
# dtype: float64


def g(x):
return pd.Series([x.min(), x.max()], index=['min', 'max'])


# 函数对象也可以返回一个Series对象(但是不能返回列表,否则会被认为是一个对象元素)
print(frame.apply(g, axis='index'))
# b d e
# min -1.899057 -1.231518 -1.671121
# max 1.447318 -0.086511 0.707834


def format(x): return '%.2f' % x


# applymap 函数会对每个元素执行
print(frame.applymap(format))
# b d e
# Utah 0.43 0.28 0.90
# Ohio 0.04 0.14 1.64
# Texas -1.03 0.54 0.90
# Oregon 0.19 -0.64 -1.62

# Series的map函数会对每个元素执行
print(frame['e'].map(format))
# Utah -0.90
# Ohio 0.05
# Texas -0.91
# Oregon -0.68
# Name: e, dtype: object

2.7 数据排序

Series 和 DataFrame 分别由方法和``来进行排名和排序

  • sort_index:根据索引名排序
  • sort_values:根据数据值排序
  • rank:数据排序不变,值变成原本数据的排名值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import pandas as pd
import numpy as np

data = pd.Series([7, -5, 7, 4, 2, 0, 4])

# 默认使用原本数据的平均排名
print(data.rank())
# 0 6.5
# 1 1.0
# 2 6.5
# 3 4.5
# 4 3.0
# 5 2.0
# 6 4.5
# dtype: float64

# 指定使用原本数据的最小排名
# average => 默认值,平均排名
# min => 最小排名
# max => 最大排名
# first => 组内根据原始数据出现顺序排名
# dense => 同min,但是不同组之间的间隔缩小为1
print(data.rank(method='first'))
# dtype: float64
# 0 6.0
# 1 1.0
# 2 7.0
# 3 4.0
# 4 3.0
# 5 2.0
# 6 5.0
# dtype: float64

# 降序排名
print(data.rank(ascending=False))
# 0 1.5
# 1 7.0
# 2 1.5
# 3 3.5
# 4 5.0
# 5 6.0
# 6 3.5
# dtype: float64

2.8 读写文本数据

关于不同格式的文本数据的读写方式,这不可能记得住,也没必要
在用到某个格式文件的时候直接参考下属资料即可

参考资料:
IO tools
IO tools 中文版
Input/output API reference

3. Matplotlib

Matplotlib 是一个画图库,这里只写一些常用的功能和对应的方法,细节可以查看参考资料
参考链接:
API 文档
教程
例程

3.1 基本使用

Matplotlib 画图后会阻塞当前程序,在 Ipython 和 jupyter notebook 中可以通过魔法命令来使其不阻塞

1
2
3
4
5
6
# jupyter notebook
%matplotlib inline
# jupyter notebook 交互式绘图
%matplotlib notebook
# Ipython
%matplotlib

matplotlib 最常用的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import numpy as np
import matplotlib.pyplot as plt

# Matplotlib 的图片存放于 subplot 中
# 多个 subplot 组成一个 Figure 对象
# 首先需要声明一个Figure对象
# plt也可以直接绘图,其有一个缺省Figure和subplot
fig = plt.figure()

# 然后在Figure对象中声明subplot对象
# 其中的参数含义为: Figure分为2行3列 编号为1
ax1 = fig.add_subplot(2, 3, 1)
ax2 = fig.add_subplot(2, 3, 2)
ax3 = fig.add_subplot(2, 3, 3)
ax4 = fig.add_subplot(2, 3, 4)

# 直接绘图会在最后一个声明的subslot上绘制
# 前面是被绘制的数据,后面是线型,具体参数可以参考参考资料
plt.plot(np.random.randn(50).cumsum(), 'k--')
# 也可以直接指定subplot进行绘制
ax1.hist(np.random.randn(100), bins=20, color='k', alpha=0.3)
ax2.scatter(np.arange(30), np.arange(30) + 3 * np.random.randn(30))

# 画完图后进行展示
plt.show()

# 也可以通过subplots函数快速创建Figure和subslot
# 其中fig是Figure对象,axes是subslot列表(二维数组)
# fig, axes = plt.subplots(2, 3)

3.2 细节调整

最好

函数 作用
subplots_adjust 调整 subplot 边距
legend 设置图例标签
set_xticks 设置 x 轴标签位置
set_xticklabels 设置 x 轴标签名
set_yticks 设置 y 轴标签位置
set_yticklabels 设置 y 轴标签名
xlabel 设置 x 轴名字
ylabel 设置 y 轴名字
set_xlim 设置 x 轴边界
set_ylim 设置 y 轴边界
set_title 设置图名
set 传入具名参数设置图标
text 在图上添加文字
annotate 设置标注
add_patch 在图上添加小图形
matplotlib.patches 生成小图形
savefig 保存图像
rc 默认参数设置(run control)