Python其他小知识

一、编码格式规范

1 编码

无特殊情况,建议Python脚本程序一律使用 UTF-8 编码,并且在文件头部必须加入#-*-coding:utf-8-*-标识,声明文件编码方式,程序文件编码要和声明编码保持一致。

2 代码格式

  • 缩进: 统一使用 4 个空格进行缩进。(默认一个tab键就是4个空格,有时程序格式问题可能出现在这里,tab键不一定就是4个空格)
  • 行宽: 每行代码尽量不超过 80 个字符(在特殊情况下可以略微超过 80 ,但最长不得超过 120)。如果代码过长,说明代码设计方面可能不合理。除此之外也方便在控制台查看代码(linux)以及通过对side-by-side的diff时有帮助。
  • 引号:自然语言,也就是说一般使用双引号;机器标示使用单引号,例如字典中的key,正则表达式使用原生的双引号 r”…”;文档字符串 (docstring) 使用三个双引号 “”” ……”””。当然实际中需要灵活运用,毕竟三种引号使用是有区别的。
  • 空行:
    • 模块级函数和类定义之间空两行;
    • 类成员函数之间空一行;

示例

1
2
3
4
5
6
7
8
9
10
11
class A:
# one line
def __init__(self):
pass
# one line
def hello(self):
pass
# one line
# one line
def main():
pass

3 import语句

  • import 语句应该分行书写;
  • import语句应该放在文件头部,置于模块说明及docstring之后,于全局变量之前;
  • import语句应该按照顺序排列,每组之间用一个空行分隔(也就是说同一类型的在一块);
  • 导入其他模块的类定义时,可以使用相对导入;

4 空格使用

(1) 在二元运算符两边各空一格[=,-,+=,==,>,in,is not, and]
  • 函数的参数列表中,,之后要有空格
  • 函数的参数列表中,默认值等号两边不要添加空格
  • 左括号之后,右括号之前不要加多余的空格
  • 字典对象的左括号之前不要多余的空格
  • 不要为对齐赋值语句而使用的额外空格

5 换行

Python 支持括号内的换行。这时有两种情况,如下:

(1) 第二行缩进到括号的起始处

1
2
foo = long_function_name(var_one, var_two,
var_three, var_four)

(2) 第二行缩进 4 个空格,适用于起始括号就换行的情

1
2
3
4
def long_function_name(
var_one, var_two, var_three,
var_four):
print(var_one)

(3) 使用反斜杠\换行

二元运算符+.等应出现在行末;长字符串也可以用此法换行

1
2
3
4
5
6
7
8
session.query(MyTable).\
filter_by(id=1).\
one()

print 'Hello, '\
'%s %s!' %\
('Harry', 'Potter')

(4) 禁止复合语句

即一行中包含多个语句:

1
2
3
4
5
6
7
8
# 正确的写法
do_first()
do_second()
do_third()

# 不推荐的写法
do_first();do_second();do_third();

6 注释

块注释 :“#”号后空一格,段落件用空行分开(同样需要“#”号)

行注释: 至少使用两个空格和语句分开,不要使用无意义的注释

补充: 在代码的关键部分(或比较复杂的地方), 能写注释的要尽量写注释;比较重要的注释段, 使用多个等号隔开, 可以更加醒目, 突出重要性。

7 命名规范

模块: 模块尽量使用小写命名,首字母保持小写,尽量不要用下划线(除非多个单词,且数量不多的情况)

类名: 类名使用驼峰(CamelCase)命名风格,首字母大写,私有类可用一个下划线开头;将相关的类和顶级函数放在同一个模块里. 不像Java, 没必要限制一个类一个模块。

1
2
3
4
5
6
7
8
9
class Farm():
pass

class AnimalFarm(Farm):
pass

class _PrivateFarm(Farm):
pass

函数名:

函数名一律小写,如有多个单词,用下划线隔开;

1
2
3
4
5
def run():
pass

def run_with_env():
pass

变量名

  • 变量名尽量小写, 如有多个单词,用下划线隔开
1
2
3
if __name__ == '__main__':
count = 0
school_name = ''
  • 常量采用全大写,如有多个单词,使用下划线隔开
1
2
3
MAX_CLIENT = 100
MAX_CONNECTION = 1000
CONNECTION_TIMEOUT = 600

【参考资料1】:【Python】编程代码书写规范! - 科皮子菊 - CSDN

【参考资料2】:PEP8中文版 — Python编码风格指南

二、深度学习的相关库

image.png

① NumPy 包为Python 加上了关键的数组变量类型,弥补了Python 的不足;
② Pandas 包在NumPy 数组的基础上添加了与Excel 类似的行列标签;
③ Matplotlib 库借鉴Matlab,帮Python 具备了绘图能力,使其如虎添翼;
④ Scikit-learn 库是机器学习库,内含分类、回归、聚类、降维等多种算法;
⑤ TensorFlow 库是Google 公司开发的深度学习框架,于2015 年问世;
⑥ PyTorch 库是Facebook 公司开发的深度学习框架,于2017 年问世。

2.1 Numpy学习

2.1.1 数组基础

导入NumPy 时,通常给其一个别名“np”,即import numpy as np
NumPy 库中的函数,要在函数名前加上导入的库名 np. 才能使用。

Ⅰ数据类型

(1) 整数型数组与浮点型数组

python中的列表存在缺点,列表内的每一个元素可以是不同的数据类型。为克服列表的缺点,一个NumPy数组只容纳一种数据类型,以节约内存。为方便起见,可将NumPy数组简单分为整数型数组浮点型数组

示例

1
2
3
4
5
6
7
8
9
10
11
import numpy as np
# 利用np.array将python的list转化为Numpy数组,产生一个实例化类对象,如下面的arr1、arr2,
# 实例化对象里面有许多可以调用的成员方法,例如 .astype( )

# 创建整数型数组:
arr1 = np.array( [1, 2, 3] ) # 元素若都是整数,则为整数型数组
print(arr1) # 输出 [1 2 3]

# 创建浮点型数组:
arr2 = np.array( [1.0, 2, 3] ) # 内含浮点数,则为浮点型数组
print(arr2) # 输出 [1. 2. 3.]

(2) 整数型数组和浮点型数组相互转换

规范的方法是使用.astype( )方法。

示例

1
2
3
4
5
6
7
8
9
10
import numpy as np
# 整数型数组
arr1 = np.array( [1, 2, 3] )
print(arr1) # 输出 [1 2 3]
# 整数型数组 ——> 浮点型数组
arr2 = arr1.astype(float)
print(arr2) # 输出 [1. 2. 3.]
# 浮点型数组 ——> 整数型数组
arr3 = arr2.astype(int)
print(arr3) # 输出 [1 2 3]

整数型数组在程序的运算中会很容易变成浮点型,但浮点型数组在运算过程中一般不会降级为整数型。

Ⅱ 数组维度

(1) 一维数组与二维数组
考虑到深度学习中三维及其以上的数组出现次数少,后续主要讲解NumPy 中的一维数组和二维数组,学了一维和二维后,很好类推到三维。

  • 不同维度的数组之间,从外形上的本质区别是
    • 一维数组使用1层中括号表示;
    • 二维数组使用2层中括号表示;
    • 三维数组使用3层中括号表示。
  • 有些函数需要传入数组的形状参数,不同维度数组的形状参数为
    • 一维数组的形状参数形如: x 或 (x,) ;
    • 二维数组的形状参数形如: (x,y) ;
    • 三维数组的形状参数形如: (x,y,z) 。
  • 现在以同一个序列进行举例
    • 当数组有1 层中括号,如[1 2 3],则其为一维数组,其形状是 3 或 (3,) ;
    • 当数组有2 层中括号,如[[1 2 3]],则其为二维数组,其形状是 (1,3) ;
    • 当数组有3 层中括号,如[[[1 2 3]]],则其为三维数组,其形状是 (1,1,3) ;

这里用后面要讲的np.ones( )函数进行演示,只因其刚好需要传入形状参数。

示例

1
2
3
4
5
6
7
8
9
10
import numpy as np

arr1 = np.ones(3) # 传入形状3
print(arr1) # 造出一维数组 [1. 1. 1.]

arr2 = np.ones((1,3)) # 传入形状(1,3)
print(arr2) # 造出二维数组 [[1. 1. 1.]]

arr3 = np.ones((1,1,3)) # 传入形状(1,1,3)
print(arr3) # 造出三维数组 [[[1. 1. 1.]]]

同时,我们还可以使用数组的.shape 属性查看arr1和arr2的形状。

示例

1
2
3
print( arr1.shape ) # 输出 (3,)
print( arr2.shape ) # 输出(1, 3)
print( arr3.shape ) # 输出(1, 1, 3)

大家可以随时留意一下数组的维度(通过中括号的数量),后面有些函数(比如数组的拼接函数)需要两个数组是同维度的。

(2) 不同维度数组之间的转换
一维数组转二维数组,还是二维数组转一维数组,均要使用的是数组的重塑方法.reshape( ) ,该方法需要传入重塑后的形状(shape)参数。
这个方法神奇的是,给定了其他维度的数值,剩下一个维度可以填-1,让它自己去计算。比如把一个5 行6 列的矩阵重塑为3 行10 列的矩阵,当列的参数10告诉它,行的参数直接可以用-1 来替代,它会自己去用30 除以10 来计算。

示例1:将一维数组升级为二维数组

1
2
3
4
5
6
7
8
9
import numpy as np

# 创建一维数组
arr1 = np.arange(10)
print(arr1) # 输出[0 1 2 3 4 5 6 7 8 9]

# 升级为二维数组
arr2 = arr1.reshape( (1,-1) )
print(arr2) # [[0 1 2 3 4 5 6 7 8 9]]

示例2:将二维数组降级为一维数组

1
2
3
4
5
6
7
8
9
10
11
12
import numpy as np

# 创建二维数组
arr2 = np.arange(10).reshape(2,5)
print(arr2)
# 输出 [[0 1 2 3 4]
# [5 6 7 8 9]]

# 降级为一维数组
arr1 = arr2.reshape( -1 )
print(arr1)
# 输出 [0 1 2 3 4 5 6 7 8 9]

2.1.2 数组的创建

Ⅰ 创建指定数组

当明确知道数组每一个元素的具体数值时,可以使用np.array( )函数,将Python列表转化为NumPy数组。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import numpy as np

# 创建一维数组——向量
arr1 = np.array( [1,2,3] )
print(arr1) # [1 2 3]

# 创建二维数组——行矩阵
arr2 = np.array( [ [1,2,3] ] )
print(arr2) # [[1 2 3]]

# 创建二维数组——列矩阵
arr3 = np.array( [ [1],[2],[3] ] )
print(arr3)
# [[1]
# [2]
# [3]]

# 创建二维数组——矩阵
arr4 = np.array( [ [1,2,3],[4,5,6] ] )
print(arr4)
# [[1 2 3]
# [4 5 6]]
Ⅱ 创建递增数组

递增数组使用 np.arange( ) 函数进行创建(arange 全称是array_range)。

1
2
3
4
5
6
7
8
9
10
11
12
import numpy as np
# 递增数组
arr1 = np.arange(10) # 从0 开始,到10 之前结束
print(arr1) # [0 1 2 3 4 5 6 7 8 9]

# 递增数组
arr2 = np.arange(10, 20) # 从10 开始,到20 之前结束
print(arr2) # [10 11 12 13 14 15 16 17 18 19]

# 递增数组
arr3 = np.arange(1,21,2) # 从1 开始,到21 之前结束,步长为2
print(arr3) # [1 3 5 7 9 11 13 15 17 19]
Ⅲ 创建同值数组

需要创建同值数组时,使用 np.zeros( ) 函数以及 np.ones( ) 函数,如示例。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import numpy as np

# 全0 数组
arr1 = np.zeros( 3 ) # 形状为3 的向量
print(arr1) # [0. 0. 0.]

# 全1 数组
arr2 = np.ones( (1,3) ) # 形状为(1,3)的矩阵
print(arr2) # [[1. 1. 1.]]

# 全3.14 数组
arr3 = 3.14 * np.ones( (2,3) ) # 形状为(2,3)的矩阵
print(arr3)
# [[3.14 3.14 3.14]
# [3.14 3.14 3.14]]

Ⅳ 创建随机数组

有时需要创建随机数组,那么可以使用 np.random 系列函数,如示例所示。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 import numpy as np

# 0-1 均匀分布的浮点型随机数组
arr1 = np.random.random( 5 ) # 形状为5 的向量
print( arr1 ) # [0.59699399 0.89113584 0.00695752 0.49089431 0.32050609]

# 整数型随机数组
arr2 = np.random.randint( 10,100,(1,15) ) # 范围为10~100的形状为(1,15)的矩阵
print( arr2 ) # [[17 65 54 48 82 57 52 26 28 27 53 36 61 92 13]]

# 服从正态分布的随机数组
arr3 = np.random.normal( 0,1,(2,3) ) # 均值为0、标准差为1的形状为(2,3)的二维高斯矩阵
print( arr3 )
# [[-0.43163964 -1.56817412 0.5460523 ]
# [-2.93093358 0.42577899 -1.69842077]]

2.1.3 数组的索引

Ⅰ 访问数组元素

与Python列表一致,访问NumPy数组元素时使用中括号,索引由0开始。

(1) 访问向量(一维数组)

示例

1
2
3
4
5
6
7
8
9
10
11
12
import numpy as np
# 创建向量
arr1 = np.arange( 1,10 )
print( arr1 ) # [1 2 3 4 5 6 7 8 9]

# 访问元素
print( arr1[3] ) # 正着访问 4
print( arr1[-1] ) # 倒着访问 9

# 修改数组元素
arr1[3] = 100;
print( arr1 ) # [1 2 3 100 5 6 7 8

(2) 访问矩阵

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 创建矩阵
arr2 = np.array( [ [1,2,3],[4,5,6] ] )
print(arr2)
# [[1 2 3]
# [4 5 6]]

# 访问元素
print( arr2[0,2] ) # 3
print( arr2[1,-2] )# 5

# 修改元素
arr2[1,1] = 100.9
print( arr2 ) # 浮点数100.9 插入到整数型数组时被截断了
# [[001 002 003]
# [004 100 006]]
Ⅱ 花式索引

花式索引(Fancy indexing)又名“花哨的索引”,上一小节访问单个元素时,向量用arr1[x],矩阵用arr2[x,y]。逗号在矩阵里用于区分行与列,这一小节,逗号新增一个功能,且不会与矩阵里的逗号混淆。
普通索引用一层中括号,花式索引用两层中括号

(1) 向量的花式索引

1
2
3
4
5
6
7
8
import numpy as np

# 创建向量
arr1 = np.arange(0,90,10)
print(arr1) # [ 0 10 20 30 40 50 60 70 80]

# 花式索引
print( arr1[ [0,2] ] ) # [0 20]

(2) 矩阵的花式索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 创建矩阵
arr2 = np.arange(1,17).reshape(4,4)
print(arr2)
# [[01 02 03 04]
# [05 06 07 08]
# [09 10 11 12]
# [13 14 15 16]]

# 花式索引
print( arr2[ [0,1] , [0,1] ] ) # [1 6]
print( arr2[ [0,1,2] , [2,1,0] ] ) # [3 6 9]

# 修改数组元素
arr2[ [0,1,2,3] , [3,2,1,0] ] = 100
print(arr2)
# [[001 002 003 100]
# [005006 100 008]
# [009 100 011 012]
# [100 014 015 016]]

image.png

根据以上实例,花式索引输出的仍然是一个向量。

Ⅲ 访问数组切片

(1) 向量的切片

向量与列表切片的操作完全一致,因此本页内容在Python 基础中均有涉及。

image.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import numpy as np
arr1 = np.arange(10)
print( arr1 ) # [1, 2, 3, 4, 5, 6, 7, 8, 9]

# 当明确知道从第x个元素切到第y个元素,如示例所示。
print( arr1[ 1 : 4 ] ) # 从索引[1]开始,切到索引[4]之前 [1 2 3]
print( arr1[ 1 : ] ) # 从索引[1]开始,切到结尾 [1 2 3 4 5 6 7 8 9]
print( arr1[ : 4 ] ) # 从数组开头开始,切到索引[4]之前 [0 1 2 3]

# 当明确切除数组的开头与结尾,如示例所示。
print( arr1[ 2 : -2 ] ) # 切除开头2个和结尾2个 [2 3 4 5 6 7]
print( arr1[ 2 : ] ) # 切除开头2个 [2 3 4 5 6 7 8 9]
print( arr1[ : -2 ] ) # 切除结尾2个 [0 1 2 3 4 5 6 7]

# 当明确隔几个元素采样一次时,示例如下。
print( arr1[ : : 2 ] ) # 每2 个元素采样一次 [0 2 4 6 8]
print( arr1[ : : 3 ] ) # 每3 个元素采样一次 [0 3 6 9]
print( arr1[ 1 : -1 : 2 ] ) # 切除一头一尾后,每2个元素采样一次 [1 3 5 7]

(2) 矩阵的切片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import numpy as np

arr2 = np.arange(1,21).reshape(4,5)
print( arr2 )
# [[01 02 03 04 05]
# [06 07 08 09 10]
# [11 12 13 14 15]
# [16 17 18 19 20]]
print( arr2[ 1:3 , 1:-1 ] )
# [[07 08 09]
# [12 13 14]]
print( arr2[ ::3 , ::2 ] ) # 跳跃采样
# [[01 03 05]
# [16 18 20]]

(3) 提取矩阵的行

基于矩阵的切片功能,我们可以提取其部分行,如示例所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import numpy as np

arr3 = np.arange(1,21).reshape(4,5)
print( arr3 )
# [[ 01 02 03 04 05]
# [06 07 08 09 10]
# [11 12 13 14 15]
# [16 17 18 19 20]]

print( arr3[ 2 , : ] ) # 提取第2行
# [11 12 13 14 15]

print( arr3[ 1:3 , : ] ) # 提取1至2行
# [[06 07 08 09 10]
# [11 12 13 14 15]]

# 考虑代码的简洁,当提取矩阵的某几行时可简写(但提取列的时候不可简写)。
print( arr3[ 2 , : ] ) # 规范的提取行 [11 12 13 14 15]
print( arr3[2] ) # 简便的提取行 [11 12 13 14 15]

所以,有时你可能看到诸如 arr[1][2] 这样的语法,不必吃惊,其实这只是先提取了第1 行,再提取该行中第2 个元素。提一句,UP 并不推荐这样的写法。

(4) 提取矩阵的列

基于矩阵的切片功能,我们可以提取其部分列,如示例所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import numpy as np

arr4 = np.arange(1,21).reshape(4,5)
print( arr4 )
# [[ 01 02 03 04 05]
# [06 07 08 09 10]
# [11 12 13 14 15]
# [16 17 18 19 20]]

print( arr4[ : , 2 ] ) # 提取第2列(注意,输出的是向量)
# [3 8 13 18]

print( arr4[ : , 1:3 ] ) # 提取1 至2 列
# [[ 02 03]
# [07 08]
# [12 13]
# [17 18]]

值得注意的是,提取某一个单独的列时,出来的结果是一个向量。其实这么做只是为了省空间,我们知道,列矩阵必须用两层中括号来存储,而形状为1000的向量,自然比形状为(1000,1)的列矩阵更省空间(节约了1000 对括号)。
如果你真的想要提取一个列矩阵出来,示例如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import numpy as np

arr5 = np.arange(1,16).reshape(3,5)
print( arr5 )
# [[ 01 02 03 04 05]
# [06 07 08 09 10]
# [11 12 13 14 15]]

cut = arr5[ : , 2 ] # 提取第2列为向量
print( cut )
# [3 8 13]

cut = cut.reshape( (-1,1) ) # 升级为列矩阵
print(cut)
# [[ 3]
# [ 8]
# [13]]
Ⅳ 数组切片仅是视图

(1) 数组切片仅是视图

与Python列表和Matlab不同,NumPy数组的切片仅仅是原数组的一个视图。换言之,NumPy切片并不会创建新的变量,示例如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import numpy as np

arr = np.arange(10) # 创建原数组arr
print(arr)
# [0 1 2 3 4 5 6 7 8 9]

cut = arr[ : 3 ] # 创建arr的切片cut
print(cut)
# [0 1 2]

cut[0] = 100 # 对切片的数值进行修改
print(cut)
# [100 1 2]

print(arr) # 原数组也被修改
# [100 1 2 3 4 5 6 7 8 9]

习惯Matlab 的用户可能无法理解,但其实这正是NumPy 的精妙之处。试想一下,一个几百万条数据的数组,每次切片时都创建一个新变量,势必造成大量的内存浪费。因此,NumPy 的切片被设计为原数组的视图是极好的。
深度学习中为节省内存,将多次使用arr[:] = <表达式> 来替代arr = <表达式>

(2) 备份切片为新变量

如果真的需要为切片创建新变量(这种情况很稀少),使用 .copy( ) 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
import numpy as np

arr = np.arange(10) # 创建一个0 到10 的向量arr
print(arr) # [0 1 2 3 4 5 6 7 8 9]

copy = arr[ : 3 ] .copy() # 创建arr 的拷贝切片
print(copy) # [0 1 2]

copy [0] = 100 # 对拷贝切片的数值进行修改
print(copy) # [100 1 2]

print(arr) # 原数组不为所动 [0 1 2 3 4 5 6 7 8 9]

Ⅴ 数组赋值仅是绑定

(1) 数组赋值仅是绑定(不可变数据)

与NumPy 数组的切片一样,NumPy 数组完整的赋值给另一个数组,也只是绑定。换言之,NumPy数组之间的赋值并不会创建新的变量,示例如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
import numpy as np

arr1 = np.arange(10) # 创建一个0 到10 的数组变量arr
print(arr1) # [0 1 2 3 4 5 6 7 8 9]

arr2 = arr1 # 把数组1 赋值给另一个数组2
print(arr2) # [0 1 2 3 4 5 6 7 8 9]

arr2[0] = 100 # 修改数组2
print(arr2) # [100 1 2 3 4 5 6 7 8 9]

print(arr1) # 原数组也被修改 [100 1 2 3 4 5 6 7 8 9]

此特性的出现仍然是为了节约空间,破局的方法仍然与前面相同。

(2) 复制数组为新变量

如果真的需要赋给一个新数组,使用.copy( )方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
import numpy as np

arr1 = np.arange(10) # 创建一个0 到10 的数组变量arr

print(arr1) # [0 1 2 3 4 5 6 7 8 9]

arr2 = arr1.copy() # 把数组1 的拷贝赋值给另一个数组2
print(arr2) # [0 1 2 3 4 5 6 7 8 9]

arr2[0] = 100 # 修改数组2
print(arr2) # [100 1 2 3 4 5 6 7 8 9]

print(arr1) # 查看数组1 [0 1 2 3 4 5 6 7 8 9]

2.1.4 数组的变形

Ⅰ 数组的转置

数组的转置方法为.T,其只对矩阵有效,因此遇到向量要先将其转化为矩阵。

(1) 向量的转置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import numpy as np

arr1 = np.arange(1,4) # 创建向量
print(arr1) # [1 2 3]

arr2 = arr1.reshape( (1,-1) ) # 升级为矩阵
print(arr2) # [[1 2 3]]

arr3 = arr2.T # 行矩阵的转置
print(arr3)
# [[1]
# [2]
# [3]]

(2) 矩阵的转置

行矩阵的转置刚演示了,列矩阵和普通矩阵的转置如示例所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import numpy as np

arr1 = np.arange(3).reshape(3,1) # 创建列矩阵
print(arr1)
# [[0]
# [1]
# [2]]

arr2 = arr1.T # 列矩阵的转置
print(arr2) # 结果为行矩阵
# [[0 1 2]]

# 矩阵的转置如示例所示。
import numpy as np

arr1 = np.arange(4).reshape(2,2) # 创建矩阵
print(arr1)
# [[0 1]
# [2 3]]

arr2 = arr1.T # 矩阵的转置
print(arr2)
# [[0 2]
# [1 3]]
Ⅱ 数组的翻转

数组的翻转方法有两个,一个是上下翻转的 np.flipud( ) ,表示up-down;一个是左右翻转的 np.fliplr( ),表示left-right。其中,向量只能使用np.flipud( ),在数学中,向量并不是横着排的,而是竖着排的。

(1) 向量的翻转

1
2
3
4
5
6
7
8
9
import numpy as np

# 创建向量
arr1 = np.arange(10)
print( arr1 ) # [0 1 2 3 4 5 6 7 8 9]

# 翻转向量
arr_ud = np.flipud(arr1)
print( arr_ud ) #[9 8 7 6 5 4 3 2 1 0]

(2) 矩阵的翻转

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
import numpy as np

# 创建矩阵
arr2 = np.arange(1,21).reshape(4,5)
print( arr2 )
# [[ 01 02 03 04 05]
# [06 07 08 09 10]
# [11 12 13 14 15]
# [16 17 18 19 20]]

# 左右翻转
arr_lr = np.fliplr(arr2)
print( arr_lr )
# [[05 04 03 02 01]
# [10 09 08 07 06]
# [15 14 13 12 11]
# [20 19 18 17 16]]

# 上下翻转
arr_ud = np.flipud(arr2)
print( arr_ud )
# [[16 17 18 19 20]
# [11 12 13 14 15]
# [06 07 08 09 10]
# [01 02 03 04 05]]
Ⅲ 数组的重塑

想要重塑数组的形状,需要用到 .reshape( ) 方法。前面说过,给定了其他维度的数值,剩下一个维度可以填-1,让它自己去计算。比如把一个5 行6 列的矩阵重塑为3 行10 列的矩阵,当列的参数10 告诉它,行的参数直接可以用-1 来替代,它会自己去用30 除以10 来计算。

(1) 向量的变形

1
2
3
4
5
6
7
8
9
10
import numpy as np

arr1 = np.arange(1,10) # 创建向量
print(arr1) # [1 2 3 4 5 6 7 8 9]

arr2 = arr1.reshape(3,3) # 变形为矩阵
print(arr2)
# [[1 2 3]
# [4 5 6]
# [7 8 9]]

(2) 矩阵的变形

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import numpy as np

arr1 = np.array( [ [1,2,3],[4,5,6] ] ) # 创建矩阵
print(arr1)
# [[1 2 3]
# [4 5 6]]

arr2 = arr1.reshape(6) # 变形为向量
print(arr2)
# [1 2 3 4 5 6]

arr3 = arr1.reshape(1,6) # 变形为矩阵
print(arr3)
# [[1 2 3 4 5 6]]
Ⅳ 数组的拼接

(1) 向量的拼接

两个向量拼接,使用np.concatenate( [arr1, arr2] ),将得到一个新的加长版向量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import numpy as np

# 创建向量1
arr1 = np.array( [1,2,3] )
print(arr1) # [1 2 3]

# 创建向量2
arr2 = np.array( [4,5,6] )
print(arr2) # [4 5 6]

# 拼接
arr3 = np.concatenate( [arr1, arr2] )
print(arr3) # [1 2 3 4 5 6]

(2) 矩阵的拼接

两个矩阵可以按不同的维度进行拼接,但拼接时必须注意维度的吻合。

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
import numpy as np

# 创建矩阵1
arr1 = np.array( [[1,2,3],[ 4,5,6]] )
print(arr1)
# [[1 2 3]
# [4 5 6]]

# 创建矩阵2
arr2 = np.array( [[7,8,9],[10,11,12]] )
print(arr2)
# [[07 08 09]
# [10 11 12]]

# 按第一个维度(行)拼接
arr3 = np.concatenate( [arr1,arr2] ) # 默认参数axis=0
print(arr3)
# [[01 02 03]
# [04 05 06]
# [07 08 09]
# [10 11 12]]

# 按第二个维度(列)拼接
arr4 = np.concatenate( [arr1,arr2] ,axis=1 )
print(arr4)
# [[01 02 03 07 08 09]
# [04 05 06 10 11 12]]

最后要说明的是,向量和矩阵不能直接进行拼接,必须先把向量升级为矩阵。

Ⅴ 数组的分裂

(1) 向量的分裂

向量分裂使用np.split( ),将得到若干个更短的向量。

1
2
3
4
5
6
7
8
9
10
11
import numpy as np

# 创建向量
arr = np.arange(10,100,10)
print(arr) # [10 20 30 40 50 60 70 80 90]

# 分裂数组
arr1,arr2,arr3 = np.split( arr , [2,8] )
print(arr1) # [10 20]
print(arr2) # [30 40 50 60 70 80]
print(arr3) # [90]

np.split( )函数中,给出的第二个参数[2,8]表示在索引[2]和索引[8]的位置截断。

(2) 矩阵的分裂

矩阵的分裂同样可以按不同的维度进行,分裂出来的均为矩阵。

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
import numpy as np

# 创建矩阵
arr = np.arange(1,9).reshape(2,4)
print(arr)
# [[1 2 3 4]
# [5 6 7 8]]

# 按第一个维度(行)分裂
arr1, arr2 = np.split(arr,[1]) # 默认参数axis=0
print(arr1 , '\n\n' , arr2) # 注意输出的是矩阵
# [[1 2 3 4]]

# [[5 6 7 8]]

# 按第二个维度(列)分裂
arr1,arr2,arr3 = np.split( arr , [1,3] , axis=1 )
print( arr1 , '\n\n' , arr2 , '\n\n' , arr3 )
# [[1]
# [5]]

# [[2 3]
# [6 7]]

# [[4]
# [8]]

2.1.5 数组的运算

Ⅰ 数组与系数之间的运算

Python 基础中,常用的运算符如下表所示,NumPy的运算符与之相同。

image.png

这里仅以矩阵为例,向量与系数的操作与之相同。

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
import numpy as np

# 创建矩阵
arr = np.arange(1,9).reshape(2,4)
print( arr )
# [[1 2 3 4]
# [5 6 7 8]]

print( arr + 10 ) # 加法
# [[11 12 13 14]
# [15 16 17 18]]

print( arr - 10 ) # 减法
# [[-9 -8 -7 -6]
# [-5 -4 -3 -2]]

print( arr * 10 ) # 乘法
# [[10 20 30 40]
# [50 60 70 80]]

print( arr / 10 ) # 除法
# [[0.1 0.2 0.3 0.4]
# [0.5 0.6 0.7 0.8]]

print( arr ** 2) # 平方
# [[01 04 09 16]
# [25 36 49 64]]

print( arr // 6) # 取整
# [[0 0 0 0]
# [0 1 1 1]]

print( arr % 6) # 取余
# [[1 2 3 4]
# [5 0 1 2]]
Ⅱ 数组与数组之间的运算

同维度数组间的运算即对应元素之间的运算,这里仅以矩阵为例,向量与向量的操作与之相同。

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
import numpy as np

# 创建矩阵
arr1 = np.arange(-1,-9,-1).reshape(2,4)
arr2 = -arr1
print( arr1 )
# [[-1 -2 -3 -4]
# [-5 -6 -7 -8]]
print( arr2 )
# [[1 2 3 4]
# [5 6 7 8]]

print( arr1 + arr2 ) # 加法
# [[0 0 0 0]
# [0 0 0 0]]

print( arr1 - arr2 ) # 减法
# [[ 0-2 0-4 0-6 0-8]
# [-10 -12 -14 -16]]

print( arr1 * arr2 ) # 乘法
# [[ -1 -4 -9 -16]
# [-25 -36 -49 -64]]

print( arr1 / arr2 ) # 除法
# [[-1. -1. -1. -1.]
# [-1. -1. -1. -1.]]

print( arr1 ** arr2) # 幂方
# [[ -1 4 -27 256]
# [ -3125 46656 -823543 16777216]]

上述示例中,乘法是遵循对应元素相乘的,你可以称之为“逐元素乘积/哈达玛积(Hadamard product) ”

那么如何实现线性代数中的“矩阵级乘法”呢?6.1 会介绍到相关函数。

Ⅲ 广播

Ⅱ是同形状数组之间的逐元素运算,本节讲解不同形状的数组之间的运算。本课件仅讨论二维数组之内的情况,不同形状的数组之间的运算有以下规则:

  • 如果是向量与矩阵之间做运算,向量自动升级为行矩阵;
  • 如果某矩阵是行矩阵或列矩阵,则其被广播,以适配另一个矩阵的形状。

(1) 向量被广播

当一个形状为(x,y)的矩阵与一个向量做运算时,要求该向量的形状必须为y,运算时向量会自动升级成形状为(1,y)的行矩阵,该形状为(1,y)的行矩阵再自动被广播为形状为(x,y)的矩阵,这样就与另一个矩阵的形状适配了。

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
import numpy as np

# 向量
arr1 = np.array([-100,0,100])
print(arr1)
# [-100 0 100]

# 矩阵
arr2 = np.random.random( (10,3) )
print(arr2)
# [[0.60755301 0.47875215 0.70909527]
# [0.12946037 0.78380689 0.7771824 ]
# [0.2658308 0.34287368 0.21176781]
# [0.93920876 0.73860266 0.32531675]
# [0.92474339 0.97997977 0.98410076]
# [0.52791609 0.17325381 0.04736612]
# [0.8543378 0.707902 0.36518268]
# [0.72053659 0.71830332 0.12972364]
# [0.86540524 0.95537187 0.66062319]
# [0.46449401 0.88824093 0.77800761]]

# 广播
print(arr1*arr2)
# [[-60.75530134 0. 70.90952713]
# [-12.94603684 0. 77.71823953]
# [-26.58307957 0. 21.17678114]
# [-93.92087564 0. 32.53167491]
# [-92.47433933 0. 98.41007632]
# [-52.7916095 0. 4.73661185]
# [-85.43377956 0. 36.51826765]
# [-72.05365932 0. 12.97236352]
# [-86.54052368 0. 66.06231879]
# [-46.44940114 0. 77.80076055]]

(2) 列矩阵被广播

当一个形状为(x,y)的矩阵与一个列矩阵做运算时,要求该列矩阵的形状必须为(x,1),该形状为(x,1)的列矩阵再自动被广播为形状为(x,y)的矩阵,这样就与另一个矩阵的形状适配了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import numpy as np

# 列矩阵
arr1 = np.arange(3).reshape(3,1)
print(arr1)
# [[0]
# [1]
# [2]]

# 矩阵
arr2 = np.ones( (3,5) )
print(arr2)
# [[1. 1. 1. 1. 1.]
# [1. 1. 1. 1. 1.]
# [1. 1. 1. 1. 1.]]

# 广播
print(arr1*arr2)
# [[0. 0. 0. 0. 0.]
# [1. 1. 1. 1. 1.]
# [2. 2. 2. 2. 2.]]

(3) 行矩阵与列矩阵同时被广播

当一个形状为(1,y)的行矩阵与一个形状为(x,1) 的列矩阵做运算时,这俩矩阵都会被自动广播为形状为(x,y)的矩阵,这样就互相适配了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import numpy as np

# 向量
arr1 = np.arange(3)
print(arr1)
# [0 1 2]

# 列矩阵
arr2 = np.arange(3).reshape(3,1)
print(arr2)
# [[0]
# [1]
# [2]]

# 广播
print(arr1*arr2)
# [[0 0 0]
# [0 1 2]
# [0 2 4]]

2.1.6 数组的函数

Ⅰ 矩阵乘积
  • 2.1.5 中的乘法都是“逐元素相乘”,这里介绍线性代数中的矩阵乘积,本节只需要使用 np.dot( ) 函数。
  • 当矩阵乘积中混有向量时,根据需求,其可充当行矩阵,也可充当列矩阵,但混有向量时输出结果必为向量。

(1) 向量与向量的乘积

设两个向量的形状按前后顺序分别是 5 以及 5 。从矩阵乘法的角度,有$(1,5) \times (5,1) = (1,1)$,因此输出的应该是形状为 1 的向量。

1
2
3
4
5
6
7
8
9
10
11
import numpy as np

# 创建向量
arr1 = np.arange(5)
arr2 = np.arange(5)
print(arr1) # [0 1 2 3 4]
print(arr2) # [0 1 2 3 4]

# 矩阵乘积
print( np.dot(arr1,arr2) )
30

(2) 向量与矩阵的乘积

设向量的形状是5 , 矩阵的形状是 (5,3) 。从矩阵乘法的角度, 有$(1,5)\times(5,3) = (1,3)$,因此输出的应该是形状为 3 的向量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import numpy as np

# 创建数组
arr1 = np.arange(5)
arr2 = np.arange(15).reshape(5,3)
print(arr1) # [0 1 2 3 4]
print(arr2)
# [[00 01 02]
# [03 04 05]
# [06 07 08]
# [09 10 11]
# [12 13 14]]

# 矩阵乘积
print( np.dot(arr1,arr2) )
# [90 100 110]

(3) 矩阵与矩阵的乘积

设两个矩阵的形状按前后顺序分别是$(5,2)$以及$(2,8)$ 。从矩阵乘法的角度,有$(5, 2)\times(2,8) = (5,8)$,因此输出的应该是形状为$(5,8)$的矩阵。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import numpy as np
# 创建数组1
arr1 = np.arange(10).reshape(5,2)
print(arr1)
# [[0 1]
# [2 3]
# [4 5]
# [6 7]
# [8 9]]

# 创建数组2
arr2 = np.arange(16).reshape(2,8)
print(arr2)
# [[00 01 02 03 04 05 06 07]
# [08 09 10 11 12 13 14 15]]

# 矩阵乘积
print( np.dot(arr1,arr2) )
# [[008 009 010 011 012 013 014 015]
# [024 029 034 039 044 049 054 059]
# [040 049 058 067 076 085 094 103]
# [056 069 082 095 108 121 134 147]
# [072 089 106 123 140 157 174 191]]
Ⅱ 数学函数

NumPy 设计了很多数学函数,这里列举其中最重要、最常见的几个。

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
import numpy as np

# 绝对值函数
arr_v = np.array( [-10, 0, 10] )
abs_v = np.abs(arr_v)
print( '原数组是:' , arr_v )
print( '绝对值是:' , abs_v )
# 原数组是: [-10 0 10]
# 绝对值是: [10 0 10]

# 三角函数
theta = np.arange(3) * np.pi / 2
sin_v = np.sin(theta)
cos_v = np.cos(theta)
tan_v = np.tan(theta)
print( '原数组是:' , theta )
print( '正弦值是:' , sin_v )
print( '余弦值是:' , cos_v )
print( '正切值是:' , tan_v )
# 原数组是: [0. 1.57079633 3.14159265]
# 正弦值是: [0.0000000e+00 1.0000000e+00 1.2246468e-16]
# 余弦值是: [ 1.000000e+00 6.123234e-17 -1.000000e+00]
# 正切值是: [ 0.00000000e+00 1.63312394e+16 -1.22464680e-16]

# 指数函数
x = np.arange(1,4)
print( 'x =' , x )
print( 'e^x =' , np.exp(x) )
print( '2^x =' , 2**x )
print( '10^x = ' , 10**x )
# x = [1 2 3]
# e^x = [ 2.71828183 7.3890561 20.08553692]
# 2^x = [2 4 8]
# 10^x = [ 10 100 1000]

# 对数函数
x = np.array( [1,10,100,1000] )
print( 'x =' , x )
print( 'ln(x) =' , np.log(x) )
print( 'log2(x) =' , np.log(x) / np.log(2) )
print( 'log10(x) =' , np.log(x) / np.log(10) )
# x = [ 1 10 100 1000]
# ln(x) = [0. 2.30258509 4.60517019 6.90775528]
# log2(x) = [0. 3.32192809 6.64385619 9.96578428]
# log10(x) = [0. 1. 2. 3.]
Ⅲ 聚合函数

聚合很有用,这里用矩阵演示。向量与之一致,但没有axis 参数。以下在注释中介绍了6个最重要的聚合函数,其用法完全一致,仅演示其中3 个。

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
import numpy as np

# 最大值函数np.max( )与最小值函数np.min( )
arr = np.random.random((2,3))
print( arr )
# [[0.54312818 0.57067295 0.11898755]
# [0.85857494 0.33915753 0.4742594 ]]

print( '按维度一求最大值:' , np.max(arr,axis=0) )
print( '按维度二求最大值:' , np.max(arr,axis=1) )
print( '整体求最大值:' , np.max(arr) )
# 按维度一求最大值: [0.85857494 0.57067295 0.4742594 ]
# 按维度二求最大值: [0.57067295 0.85857494]
# 整体求最大值: 0.8585749445359108

# 求和函数np.sum( )与求积函数np.prod( )
arr = np.arange(10).reshape(2,5)
print( arr )
# [[0 1 2 3 4]
# [5 6 7 8 9]]
print( '按维度一求和:' , np.sum(arr,axis=0) )
print( '按维度二求和:' , np.sum(arr,axis=1) )
print( '整体求和:' , np.sum(arr) )
# 按维度一求和: [ 5 7 9 11 13]
# 按维度二求和: [10 35]
# 整体求和: 45

# 均值函数np.mean( )与标准差函数np.std( )
arr = np.arange(10).reshape(2,5)
print( arr )
# [[0 1 2 3 4]
# [5 6 7 8 9]]
print( '按维度一求平均:' , np.mean(arr,axis=0) )
print( '按维度二求平均:' , np.mean(arr,axis=1) )
print( '整体求平均:' , np.mean(arr) )
# 按维度一求平均: [2.5 3.5 4.5 5.5 6.5]
# 按维度二求平均: [2. 7.]
# 整体求平均: 4.5
  • axis=0 时,最终结果与每一行的元素个数一致;
  • axis=1 时,最终结果与每一列的元素个数一致。

考虑到大型数组难免有缺失值,以上聚合函数碰到缺失值时会报错,因此出现了聚合函数的安全版本,即计算时忽略缺失值:np.nansum( )np.nanprod( )np.nanmean( )np.nanstd( )np.nanmax( )np.nanmin( )

2.1.7 布尔型数组

除了整数型数组和浮点型数组,还有一种有用的数组类型——布尔型数组。

Ⅰ 创建布尔型数组

由于NumPy 的主要数据类型是整数型数组或浮点型数组,因此布尔型数组的产生离不开:大于>、大于等于>=、等于==、不等号!=、小于<、小于等于<=。

首先,我们将数组与系数作比较,以产生布尔型数组,示例如下。

1
2
3
4
5
6
7
8
9
10
11
12
import numpy as np

# 创建数组
arr = np.arange(1,7).reshape(2,3)
print(arr)
# [[1 2 3]
# [4 5 6]]

# 数组与数字作比较
print( arr >= 4 )
# [[False False False]
# [True True True]]

其次,我们将同维数组作比较,以产生布尔型数组,示例如下。

1
2
3
4
5
6
7
8
9
10
11
import numpy as np

# 创建同维数组
arr1 = np.arange(1,6)
arr2 = np.flipud(arr1)
print(arr1) # [1 2 3 4 5]
print(arr2) # [5 4 3 2 1]

# 同维度数组作比较
print( arr1 > arr2)
# [False False False True True]

最后,还可以同时比较多个条件。Python 基础里,同时检查多个条件使用的与、或、非是and、or、not。但NumPy 中使用的与、或、非是 & 、 | 、 ~ 。

1
2
3
4
5
6
7
8
9
10
import numpy as np

# 创建数组
arr = np.arange(1,10)
print(arr)
# [1 2 3 4 5 6 7 8 9]

# 多个条件
print( (arr < 4) | (arr > 6) )
# [ True True True False False False True True True]
Ⅱ 布尔型数组中True 的数量

有三个关于Ture 数量的有用函数,分别是np.sum( )np.any( )np.all( )

np.sum( )函数:统计布尔型数组里True 的个数。示例如下。

1
2
3
4
5
6
7
8
import numpy as np

# 创建一个形状为10000 的标准正态分布数组
arr = np.random.normal( 0,1,10000 )

# 统计该分布中绝对值小于1 的元素个数
num = np.sum( np.abs(arr) < 1 )
print( num ) # 6814

上述示例里,np.abs(arr) < 1 可以替换为 (arr>-1) & (arr<1)。此外,最终统计的数量为6814,其概率近似为0.6827,这符合统计学中的3σ 准则。

np.any( )函数:只要布尔型数组里含有一个及其以上的True,就返回True。

1
2
3
4
5
6
7
8
9
10
11
import numpy as np

# 创建同维数组
arr1 = np.arange(1,10)
arr2 = np.flipud(arr1)
print(arr1) # [1 2 3 4 5 6 7 8 9]
print(arr2) # [9 8 7 6 5 4 3 2 1]

# 统计这两个数组里是否有共同元素
print( np.any( arr1 == arr2 ) )
True

从结果来看,arr1 与arr2 里含有共同元素,那就是5。

np.all( )函数:当布尔型数组里全是True时,才返回True,示例如下。

1
2
3
4
5
6
7
8
import numpy as np

# 模拟英语六级的成绩,创建100000 个样本
arr = np.random.normal( 500,70,100000 )

# 判断是否所有考生的分数都高于250
print( np.all( arr > 250 ) )
# False

从结果来看,尽管3σ 准则告诉我们有99.73%的考生成绩高于290 分(290通过$500−3\times70$计算得到),但仍然有最终成绩低于 250 分的裸考者。

Ⅲ 布尔型数组作为掩码

若一个普通数组和一个布尔型数组的维度相同,可以将布尔型数组作为普通数组的掩码,这样可以对普通数组中的元素作筛选。给出两个示例。

第一个示例,筛选出数组中大于、等于或小于某个数字的元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import numpy as np

# 创建数组
arr = np.arange(1,13).reshape(3,4)
print(arr)
# [[01 02 03 04]
# [05 06 07 08]
# [09 10 11 12]]

# 数组与数字作比较
print( arr > 4 )
# [[False False False False]
# [ True True True True]
# [ True True True True]]

# 筛选出 arr > 4 的元素
print( arr[ arr > 4 ] )
# [05 06 07 08 09 10 11 12]

注意,这个矩阵进行掩码操作后,退化为了向量。

第二个示例,筛选出数组逐元素比较的结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
In [1] : import numpy as np

# 创建同维数组
arr1 = np.arange(1,10)
arr2 = np.flipud(arr1)
print(arr1) # [1 2 3 4 5 6 7 8 9]
print(arr2) # [9 8 7 6 5 4 3 2 1]

# 同维度数组作比较
print( arr1 > arr2)
# [False False False False False True True True True]

# 筛选出 arr1 > arr2 位置上的元素
print( arr1[ arr1 > arr2 ] )
# [6 7 8 9]
print( arr2[ arr1 > arr2 ] )
# [4 3 2 1]
Ⅳ 满足条件的元素所在位置

现在我们来思考一种情况:假设一个很长的数组,我想知道满足某个条件的元素们所在的索引位置,此时使用np.where( )函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
In [1] : import numpy as np

# 模拟英语六级成绩的随机数组,取10000 个样本
arr = np.random.normal( 500,70,1000 )

# 找出六级成绩超过650 的元素所在位置
print( np.where( arr > 650 ) )
# (array([127, 129, 317, 342, 484, 490, 634, 658, 677, 755, 763, 819, 820,
# 853, 926, 932, 982], dtype=int64),)

# 找出六级成绩最高分的元素所在位置
print( np.where( arr == np.max(arr) ) )
# (array([342], dtype=int64),)

np.where( )函数的输出看起来比较怪异,它是输出了一个元组。元组第一个元素是“满足条件的元素所在位置”;第二个元素是数组类型,可忽略掉。

2.1.8 其他补充

Ⅰ np.array和np.ndarry 的区别

np.array和np.ndarray都是NumPy中用于创建多维数组的函数。

np.ndarray是NumPy中的多维数组类,它是一种可变的数组,可以通过修改数组中的元素来改变其内容。使用np.ndarray创建的数组必须指定形状、元素类型和存储器位置等参数。例如,可以使用以下代码创建一个2x2的整数数组:

1
a = np.ndarray(shape=(2, 2), dtype=int, buffer=np.array([1, 2, 3, 4]))

np.array是用于创建多维数组的函数,它会将输入的数据转换为一个NumPy数组。与np.ndarray不同,使用np.array创建的数组不需要指定形状、元素类型和存储器位置等参数。例如,以下代码将一个Python列表转换为一个NumPy数组:

1
a = np.array([1, 2, 3, 4])

在实际使用中,np.array更加常用,因为它可以从Python列表、元组、其他序列类型等对象自动推断形状和数据类型,并且不需要手动指定存储器位置。但如果你需要创建一些特定的数组,比如指定从已有的数据缓冲区中创建数组,或者指定存储器位置,那么np.ndarray可能更适合你的需求。

总之,np.ndarray是NumPy中的一个多维数组类,而np.array是一个用于创建多维数组的函数,两者的使用场景略有不同。

参考:np.array和np.ndarry 的区别 - 个人博客

2.1.9 从数组到张量

本视频中,numpy为1.21.5版本,torch为1.12.0版本。PyTorch不同版本的发行日志:https://pytorch.org/blog/。

Ⅰ 数组与张量
  • 本次课属于《Python 深度学习》系列视频,PyTorch 作为当前首屈一指的深度学习库,其将NumPy 的语法尽数吸收,作为自己处理数组的基本语法,且运算速度从使用CPU 的数组进步到使用GPU 的张量。
  • NumPy 和PyTorch 的基础语法几乎一致,具体表现为:
    • ① np 对应torch;
    • ② 数组array 对应张量tensor;
    • ③ NumPy 的n 维数组对应着PyTorch 的n 阶张量。
  • 数组与张量之间可以相互转换:
    • 数组arr 转为张量ts:ts = torch.tensor(arr);
    • 张量ts 转为数组arr:arr = np.array(ts)。
Ⅱ 语法不同点

为找到NumPy 和PyTorch哪些语法不同,UP对本文档进行了替换操作,将np改为torch,将array改为tensor,并重新运行所有代码,得出结论:PyTorch只是少量修改了NumPy 的部分函数或方法,现对其中不同的地方进行罗列。

image.png

2.2 Tensor学习

Tensor基本概念的理解
我们都知道Numpy是支持大量维度数组与矩阵运算的常用扩展库(只能在cpu上跑numpy)。但是对于
计算图,深度学习或者梯度,Numpy是有心无力,因为它的计算无法像Tensor一样在能用GPU加速。
Tensor是n维的数组,在概念上与numpy数组是一样的,但不同的是Tensor可以跟踪计算图和计算梯
度[参考链接1]。
Tensor与Numpy没区别,就是多维数组而已,不是物理概念的张量。不用numpy是因为pytorch需要
支持额外的功能:① GPU等其它硬件的支持;② 自动的梯度计算和back propagation[参考链接2]。
不精确地说, 若numpy数据结构也支持GPU,现在深度学习里大概就都叫ndarray而不是Tensor了。
在PyTorch中,张量Tensor是最基础的运算单位,与NumPy中的NDArray类似,张量表示的是一个多维
矩阵。不同的是,PyTorch中的Tensor可以运行在GPU上,而NumPy的NDArray只能运行在CPU上。由
于Tensor能在GPU上运行,故大大加快了运算速度,tensor再怎么高级也只是一个数据结构而已。
一句话总结:一个可以运行在gpu上的多维数据而已[参考链接3]。

2.2.1 为什么要用tensor

深度学习中的tensor指的是张量,相比于numpy中的数组,它有如下优点,

  • 支持 GPU 加速:深度学习中,需要对大量数据进行计算,并且这些计算通常是高度并行化的。使用 tensor 可以方便地将计算放到 GPU 上进行加速,而 numpy 则通常只能在 CPU 上进行计算。
  • 支持自动求导:深度学习中,需要对模型参数进行优化,而优化通常需要求解参数的梯度。使用 tensor 可以方便地实现自动求导,而 numpy 则需要手动实现求导过程。
  • 支持动态计算图:深度学习中,通常需要构建复杂的计算图来描述模型结构和计算过程。使用 tensor 可以方便地构建动态计算图,而 numpy 则只能使用静态计算图。

因此,使用 tensor 可以提高深度学习的计算效率和开发效率,并且方便实现自动求导和构建动态计算图。虽然 tensor 相对于numpy复杂一些,但是在深度学习中,使用 tensor 是非常普遍和必要的。

2.2.2 Tensor属性

Ⅰ Tensor属性概述

在 PyTorch 0.4.0 之前,torch.autograd 包中存在 Variable 这种数据类型,主要是用于封装 Tensor,进行自动求导。Variable 主要包含下面几种属性。 在 PyTorch 0.4.0之后,Variable 并入了 Tensor。在之后版本的 Tensor 中,除了具有上面 Variable 的 5 个属性,还有另外 3 个属性。

image.png

  • data:被包装的Tensor;
  • grad:data的梯度;
  • grad_fn:创建Tensor所使用的Function,是自动求导的关键,因为根据所记录的函数才能计算出导数。
  • requires_grad:指示是否需要梯度,并不是所有的张量都需要计算梯度。
  • is_leaf:指示是否为叶子节点(张量),叶子节点的概念在计算图中会用到,后面详细介绍。
  • dtype:张量的数据类型,如 torch.FloatTensor,torch.cuda.FloatTensor
  • shape:张量的形状。如 (64, 3, 224, 224)
  • device:张量所在设备 (CPU/GPU),GPU 是加速计算的关键
Ⅱ Tensor数据类型

torch.dtype属性标识了torch.Tensor的数据类型。PyTorch有八种不同的数据类型:

8a85fcc7257c4b34890002090a7f007e.png

Ⅲ Tensor所在设备

64d489edcf384fbc81c0e9942725fecf.png

如图所示,我们可以看到每种类型的数据都有一个CPU和一个GPU版本,因此我们对张量进行处理的时候需要指定一个设备,它要么是CPU要么是GPU,这是数据被分配的位置,这决定了给定张量的张量计算位置。

Pytorch支持多种设备的使用,我们可以用torch.device来创建一个设备,并指定索引,例如:

1
device=torch.device('cuda:0')

输出结果为:device(type=‘cuda’,index=0),可看到类型为cuda,即GPU,索引0表示为第一个GPU。

[参考链接]、[参考链接]。

2.2.2 Tensor创建

Ⅰ 直接创建Tensor

直接创建Tensor的语法如下。

1
2
3
4
5
6
7
8
9
10
11
12
"""
data:数据,可以是list, numpy
dtype:数据类型,默认与data对应
device:张量所在的设备(cuda或cpu)
requires_grad:是否需要梯度
pin_memory:是否存于锁存内存
"""
torch.tensor(data, dtype=None, device=None, requires_grad=False, pin_memory=False)

# 注意:pin_memory就是锁页内存,创建DataLoader时,设置pin_memory=True,
# 则意味着生成的Tensor数据最开始是属于内存中的锁页内存,这样将内存的Tensor
# 转义到GPU的显存就会更快一些。

示例1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import numpy as np
import torch

arr = np.ones((3, 3))
print(arr)
# [[1. 1. 1.]
# [1. 1. 1.]
# [1. 1. 1.]]

# ndarray的数据类型: float64
print("ndarray的数据类型:", arr.dtype)

t= torch.tensor(arr)
print(t)
'''
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]], dtype=torch.float64)
'''

如需创建一个放在GPU的数据,则可做如下修改,运行结果同上。

1
2
3
4
5
6
7
8
import numpy as np
import torch

device= torch.device("cuda" if torch.cuda.is_available() else 'cpu')
arr = np.ones((3, 3))
print("ndarray的数据类型:", arr.dtype)
t = torch.tensor(arr, device=device)
print(t)
Ⅱ 从Numpy中创建
  • torch.from_numpy()是PyTorch提供的一个便捷函数,用于将NumPy数组转换为PyTorch张量。该函数在内部使用了NumPy的C接口,所以它保留了NumPy数组的形状和数据类型[参考链接-CSDN博客]。
    • 数据类型torch.from_numpy()会保留NumPy数组的数据类型。如果NumPy数组是浮点数类型,转换后的张量也将是浮点数类型。
    • 不可变数据类型:通过torch.from_numpy()创建的张量默认是不可变的,这意味着你不能直接修改其内容。如果你需要修改张量,可以通过.clone()方法创建一个副本。
    • 内存共享torch.from_numpy()创建的张量和原始NumPy数组共享相同的内存。这意味着对张量的修改将影响原始数组,反之亦然。如果你不希望共享内存,可以使用.clone()方法。
  • torch.Tensor是默认的tensor类型(torch.FloatTensor)的简称,它是一个类,也就是说使用它只能创建torch.FloatTensot类型
  • torch.tensor是一个函数/方法,它根据参数data创建Tensor,Tensor类型根据数据进行推断,也就是说当我们没有指定dtype使用它创建的类型和data一致,
1
2
3
4
5
6
7
import torch
import numpy as np

numpy_array= np.array([1,2,3])
torch_tensor1 = torch.from_numpy(numpy_array)
torch_tensor2 = torch.Tensor(numpy_array) # 实例化一个Tensor类的对象
torch_tensor3 = torch.tensor(numpy_array) # 函数返回一个Tensor数据

在PyTorch中,torch.Tensor是主要的tensor类,所有的tensor都是torch.Tensor的实例。torch.Tensortorch.FloatTensor的别名。 而torch.tensor是一个函数,返回的是一个tensor。

区别1:

  • torch.Tensor(data):将输入的data转化torch.FloatTensor;
  • torch.tensor(data):(当你未指定dype的类型时)将data转化为torch.FloatTensor、torch.LongTensor、torch.DoubleTensor等类型,转化类型依据于data的类型或者dtype的值;

区别2:

  • 使用如下语句:tensor_without_data = torch.Tensor() 可以创建一个空的FloatTensor;
  • 而当你使用tensor_without_data = torch.tensor()时候则会报错。

[参考链接]

Ⅲ 从Python内置类型中创建
1
2
3
4
5
6
import torch

a = [1,2,3]
torch_tensor1 = torch.tensor(a)
a1 = (1,2,3)
torch_tensor2 = torch.tensor(a1)
Ⅳ 其他方式
1
2
3
4
5
6
7
8
9
10
11
# 创建相同元素的Tensor
torch_tensor1 = torch.full([2,3],2)
# 创建全为1的Tensor
torch_tensor2 = torch.ones([2,3])
# 创建全为0的Tensor
torch_tensor3 = torch.zeors([2,3])
# 创建对角阵的Tensor
torch_tensor4 = torch.eye(3)
# 在区间[1,10]中随机创建Tensor
torch_tensor5 = torch.randint(1,10,[2,2])
# 等等...

创建Tensor时候也可指定数据类型和所存储的设备

1
2
3
4
torch_tensor= torch.zeros([2,3],dtype=torch.float64,device=torch.device('cuda:0'))
torch_tensor.dtype #torch.float64
torch_tensor.device #cuda:0
torch_tensor.is_cuda #True

2.2.3 Tensor加速

我们可以使用以下两种方式使得Tensor在GPU上加速。

第一种方式是定义cuda数据类型。

1
2
dtype = torch.cuda.FloatTensor
gpu_tensor = torch.randn(1,2).type(dtype) #把Tensor转换为cuda数据类型

第二种方式是直接将Tensor放到GPU上(推荐)。

1
2
gpu_tensor = torch.randn(1,2).cuda(0)#把Tensor直接放在第一个GPU上
gpu_tensor = torch.randn(1,2).cuda(1)#把Tensor直接放在第二个GPU上

而将Tensor放在CPU上也很简单。

1
cpu_tensor = gpu_tensor.cpu()

2.2.4 查看Tensor属性

Ⅰ 查看Tensor类型属性
1
2
tensor1 = torch.ones([2,3])
tensor1.dtype # torch.float32
Ⅱ 查看Tensor尺寸属性
1
2
tensor1.shape # 尺寸
tenosr1.ndim #维度

关于Tensor的形状对应

在python中,会有标量,向量,矩阵等的区分。但在PyTorch中,这些统称为张量tensor,只是维度不同而已。

  • 标量就是0维张量,只有一个数字,没有维度。

image.png

  • 向量就是1维张量,是有顺序的数字,但没有“行”或“列”的区分。

image.png

  • 矩阵就是2维张量,有形状,行和列。

image.png

  • 3维张量

image.png

注:3维张量的大小输出为torch.Size(C=3, H=28, W=28)可以理解为:

1张C=3(RGB)通道的28*28的图片。

  • 4维张量

image.png

注:4维张量的大小输出为torch.Size(N=2, C=3, H=28, W=28)可以理解为:

N=2张C=3(RGB)通道的28*28的图片。

Ⅲ 查看Tensor是否存储在GPU上
1
tensor1.is_cuda #False
Ⅳ 查看Tensor存储设备
1
2
3
tensor1.device # cpu
tensor1.cuda(0)
tensor1.device # cuda:0
Ⅴ 查看Tensor梯度计算
1
tensor1.grad

2.2.5 Tensor操作

Ⅰ 形状重置

Tensor的shape可通过torch.reshape接口来改变。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import torch
Tensor =torch.tensor([[[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]]])
print("the shape of Tensor:", Tensor.shape)
# the shape of Tensor: torch.Size([3, 2, 5])

# 利用reshape改变形状
reshape_Tensor = torch.reshape(Tensor, [2, 5, 3])
print("After reshape:\n", reshape_Tensor)
# tensor([[[ 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]]])

在指定新的shape时存在一些技巧:

  • -1 表示这个维度的值是从Tensor的元素总数和剩余维度自动推断出来的。因此,有且只有一个维度可以被设置为-1。
  • 0 表示该维度的元素数量与原值相同,因此shape中0的索引值必须小于Tensor的维度(索引值从 0 开始计,如第 1 维的索引值是 0,第二维的索引值是 1)。
Ⅱ 索引和切片

通过索引或切片方式可访问或修改Tensor。

(1) 访问Tensor

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 torch

ndim_2_Tensor = torch.tensor([[0, 1, 2, 3],
[4, 5, 6, 7],
[8, 9, 10, 11]])
print("Origin Tensor:\n", ndim_2_Tensor.numpy())
# Origin Tensor:
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]

#索引或切片的第一个值对应第 0 维,第二个值对应第 1 维,
#依次类推,如果某个维度上未指定索引,则默认为 :
#所以下面两种操作结果一样
print("First row:", ndim_2_Tensor[0].numpy())
# First row: [0 1 2 3]

print("First row:", ndim_2_Tensor[0, :].numpy())
# First row: [0 1 2 3]

print("First column:", ndim_2_Tensor[:, 0].numpy())
# First column: [0 4 8]

print("Last column:", ndim_2_Tensor[:, -1].numpy())
# Last column: [ 3 7 11]

print("All element:\n", ndim_2_Tensor[:].numpy())
# All element:
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]

print("First row and second column:", ndim_2_Tensor[0, 1].numpy())
# First row and second column: 1

(2) 修改Tensor

与访问张量类似,可以在单个或多个轴上通过索引或切片操作来修改张量。

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 torch

ndim_2_Tensor = torch.ones([2, 3])
ndim_2_Tensor = ndim_2_Tensor.to(torch.float32)
print('Origin Tensor:\n ', ndim_2_Tensor)
# Origin Tensor:
# tensor([[1., 1., 1.],
# [1., 1., 1.]])

# 修改第1维为0
ndim_2_Tensor[0] = 0
print('change Tensor1:\n ', ndim_2_Tensor)
# change Tensor1:
# tensor([[0., 0., 0.],
# [1., 1., 1.]])

# 修改第1维为2.1
ndim_2_Tensor[0:1] = 2.1
print('change Tensor2:\n ', ndim_2_Tensor)
# change Tensor2:
# tensor([[2.1000, 2.1000, 2.1000],
# [1.0000, 1.0000, 1.0000]])

# 修改全部Tensor
ndim_2_Tensor[...] = 3
print('change Tensor3:\n ', ndim_2_Tensor)
#change Tensor3:
# tensor([[3., 3., 3.],
# [3., 3., 3.]])
Ⅲ Tensor运算

张量支持包括基础数学运算、逻辑运算、矩阵运算等100余种运算操作。

(1) 数学运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
x.abs()                       # 逐元素取绝对值
x.ceil() # 逐元素向上取整
x.floor() # 逐元素向下取整
x.round() # 逐元素四舍五入
x.exp() # 逐元素计算自然常数为底的指数
x.log() # 逐元素计算x的自然对数
x.reciprocal() # 逐元素求倒数
x.square() # 逐元素计算平方
x.sqrt() # 逐元素计算平方根
x.sin() # 逐元素计算正弦
x.cos() # 逐元素计算余弦
x.add(y) # 逐元素加
x.subtract(y) # 逐元素减
x.multiply(y) # 逐元素乘(积)
x.divide(y) # 逐元素除
x.mod(y) # 逐元素除并取余
x.pow(y) # 逐元素幂
x.max() # 指定维度上元素最大值,默认为全部维度
x.min() # 指定维度上元素最小值,默认为全部维度
x.prod() # 指定维度上元素累乘,默认为全部维度
x.sum() # 指定维度上元素的和,默认为全部维度

(2) 逻辑运算

1
2
3
4
5
6
7
8
9
x.isfinite()                  # 判断Tensor中元素是否是有限的数字,即不包括inf与nan
x.equal_all(y) # 判断两个Tensor的全部元素是否相等,并返回形状为[1]的布尔类Tensor
x.equal(y) # 判断两个Tensor的每个元素是否相等,并返回形状相同的布尔类Tensor
x.not_equal(y) # 判断两个Tensor的每个元素是否不相等
x.less_than(y) # 判断Tensor x的元素是否小于Tensor y的对应元素
x.less_equal(y) # 判断Tensor x的元素是否小于或等于Tensor y的对应元素
x.greater_than(y) # 判断Tensor x的元素是否大于Tensor y的对应元素
x.greater_equal(y) # 判断Tensor x的元素是否大于或等于Tensor y的对应元素
x.allclose(y) # 判断两个Tensor的全部元素是否接近

(3) 矩阵运算

1
2
3
4
5
x.t()                         # 矩阵转置
x.transpose([1, 0]) # 交换第 0 维与第 1 维的顺序
x.norm('fro') # 矩阵的弗罗贝尼乌斯范数
x.dist(y, p=2) # 矩阵(x-y)的2范数
x.matmul(y) # 矩阵乘法

2.2.6 Tensor的广播机制[参考]

229f2794d5af4d97ab0a37412bdc5531.png

2.3 Pandas学习

2.3.0 为什么要学习Pandas

Numpy已经能够帮助我们处理数据,能够结合matplotlib解决我们数据分析的问题,那么Pandas学习的目的在什么地方呢?Numpy能够帮我们处理处理数值型数据,但是这还不够, 很多时候,我们的数据除了数值之外,还有字符串、时间序列等。比如:我们通过爬虫获取到了存储在数据库中的数据。所以,Pandas出现了。

简单来说,Pandas是编程界的Excel

我个人理解,Pandas相当于Python中Excel、SQL这类表格工具,只是在此基础上它提供了更加丰富而高效的功能。

熟悉Excel和SQL的同学应该理解,Excel和SQL经常用于一维和二维数据的处理,效率非常高。

以数据分析为例,假如我们利用Python从零开始开发一个程序,需要先后实现数据的读取、数据结构的定义、数据遍历、数据处理、数据计算 …. 这是一个繁琐而多余的过程。

通过SQL或者Excel,我们可以借助countsumjoingroup by等函数,仅有几行代码就可以实现Python几十甚至上百行代码的工作,极大的提升了开发效率。

而Python相对于SQL、Excel则提供了更加灵活、更加强大的数据分析/处理功能。

它的功能包括但不限于下述内容:

  • 数据清理
  • 数据填充
  • 数据规范化
  • 合并和连接
  • 数据可视化
  • 统计分析

参考链接1:如何最简单、通俗地理解Python的pandas库? - 知乎

参考链接2:Python数据处理003:为什么要学习 Numpy & Pandas? - 知乎

参考链接2:为什么用 Pandas - 莫凡Python

2.3.1 对象的创建

导入Pandas时,通常给其一个别名“pd”,即 import pandas as pd

作为标签库,Pandas对象在NumPy数组基础上给予其行列标签。可以说:

image.png

Pandas中,所有数组特性仍在,Pandas的数据以NumPy数组的方式存储。

Ⅰ 一维对象的创建

(1) 字典创建法

NumPy中,可以通过np.array()函数,将Python列表转化为NumPy数组;同样,Pandas中,可以通过 pd.Series()函数,将Python字典转化为 Series 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
import pandas as pd

# 创建字典
dict_v = { 'a':0, 'b':0.25, 'c':0.5, 'd':0.75, 'e':1 }
# 用字典创建Pandas对象
sr= pd.Series( dict_v)
print(sr)
# a 0.00
# b 0.25
# c 0.50
# d 0.75
# e 1.00
# dtype: float64

(2) 数组创建法
最直接的创建方法即直接给pd.Series()函数参数,其需要两个参数:

  • 第一个参数是值 values(列表、数组、张量均可),
  • 第二个参数是键index(索引)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import pandas as pd

# 用列表创建对象
v = [0, 0.25,0.5,0.75,1]
k = ['a', 'b', 'c', 'd', 'e']
# 键值创建法
sr = pd.Series(v , index=k)
print(sr)
# a 0.00
# b 0.25
# c 0.50
# d 0.75
# e 1.00
# dtype: float64

其中,参数index可以省略,省略后索引即从0开始的顺序数字。

Ⅱ 一维对象的属性

Series 对象有两个属性:values与index。

1
2
3
4
5
6
7
8
9
10
11
import numpy as np
import pandas as pd

# 用数组创建 sr
v = np.array([53,64,72,82])
k = ['1号', '2号', '3号','4号']
sr = pd.Series(v, index=k)
print(sr.values)
# [53 64 72 82]
print(sr.index)
# Index(['1号', '2号', '3号', '4号'], dtype='object')

事实上,无论是用列表、数组还是张量来创建对象,最终values均为数组。

可见,虽然Pandas对象的第一个参数values可以传入列表、数组与张量,但传进去后默认的存储方式是 NumPy数组。这一点更加提醒我们,Pandas是建立在 NumPy基础上的库,没有NumPy数组库就没有Pandas数据处理库。当想要 Pandas 退化为 NumPy 时,查看其 values 属性即可

Ⅲ 二维对象的创建

二维对象将面向矩阵,其不仅有行标签index,还有列标签columns。

(1) 字典创建法

用字典法创建二维对象时,必须基于多个Series对象,每一个Series 就是一列数据,相当于对一列一列的数据作拼接。

  • 创建 Series对象时,字典的键是index,其延展方向是竖直方向;
  • 创建 DataFrame 对象时,字典的键是columns,其延展方向是水平方向。
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
import pandas as pd

# 创建sr1:各个病人的年龄
v1 = [53, 64, 72, 82 ]
i = ['1号', '2号', '3号', '4号']
sr1 = pd.Series( v1,index=i )
print(sr1)
# 1号 53
# 2号 64
# 3号 72
# 4号 82
# dtype: int64

# 创建sr2:各个病人的性别
v2 =['女', '男', '男', '女']
i = ['1号', '2号', '3号', '6号']
sr2 = pd.Series(v2, index=i)
print(sr2)
# 1号 女
# 2号 男
# 3号 男
# 4号 女
# dtype: object

#创建df对象
df = pd.DataFrame({'年龄':sr1,'性别':sr2})
print(df)
# 年龄 性别
# 1号 53 女
# 2号 64 男
# 3号 72 男
# 4号 82 女

在做大数据处理的时候,大数据表格一般都长成上述代码中的df那样:列代表不同的特征,行代表不同的个体(采样)

如果 sr1 和 sr2 的 index 不完全一致,那么二维对象的 index 会取 sr1 与 sr2 的index的交集,相应的,该对象就会产生一定数量的缺失值(NaN)。

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
import pandas as pd

# 创建sr1:各个病人的年龄
v1 = [53, 64, 72, 82 ]
i = ['1号', '2号', '3号', '4号']
sr1 = pd.Series( v1,index=i )
print(sr1)
# 1号 53
# 2号 64
# 3号 72
# 4号 82
# dtype: int64

# 创建sr2:各个病人的性别
v2 =['女', '男', '男', '女']
i = ['1号', '2号', '3号', '6号']
sr2 = pd.Series(v2, index=i)
print(sr2)
# 1号 女
# 2号 男
# 3号 男
# 4号 女
# dtype: object

#创建df对象
df = pd.DataFrame({'年龄':sr1,'性别':sr2})
print(df)
# 年龄 性别
# 1号 53.0 女
# 2号 64.0 男
# 3号 72.0 男
# 4号 82.0 NaN
# 6号 NaN 女

(2) 数组创建法

最直接的创建方法即直接给pd.DataFrame函数参数,其需要三个参数。第一个参数是值 values(数组),第二个参数是行标签index,第三个参数是列标签columns。其中,index和columns参数可以省略,省略后即从0开始的顺序数字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import numpy as np
import pandas as pd

# 设定键值
v = np.array([[53,'女'],[64,'男'],[72,'男'],[82,'女']])
i = ['1号', '2号', '3号', '4号']
c = ['年龄', '性别']
# 键值创建法
df = pd.DataFrame(v, index=i, columns=c )
print(df)
# 年龄 性别
# 1号 53 女
# 2号 64 男
# 3号 72 男
# 4号 82 女

细心的同学可能会发现端倪,第二行的NumPy数组居然又含数字又含字符串,上次课中明明讲过数组只能容纳一种变量类型。这里的原理是,数组默默把数字转为了字符串,于是v就是一个字符串型数组。

Ⅳ 二维对象的属性

DataFrame对象有三个属性:values、index与columns。

承接上述代码:

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
# 查看values属性
print(df.values)
# [['53' '女']
# ['64' '男']
# ['72' '男']
# ['82' '女']]
# 这里可以看到,年龄从数字转变为了字符;
# 此外,想要Pandas退化为Numpy时,查看其values属性

# 査看index属性
print(df.index)
# Index(['1号', '2号', '3号', '4号'], dtype='object')
# 查看columns属性
print(df.columns)
# Index(['年龄', '性别'], dtype='object')

# 提取完整的数组
arr = df.values
print(arr)
# [['53' '女']
# ['64' '男']
# ['72' '男']
# ['82' '女']]

# 提取第[0]行,并转化为一个整数型数组
arr = arr[:,0].astype(int)
print(arr)
# [53 64 72 82]

由于数组只能容纳一种变量类型,因此需要.astype(int)的操作。但对象不用,对象每一列的存储方式是单独的,这就很好的兼容了大数据的特性。

2.3.2 对象的索引

在学习 Pandas的索引之前,需要知道:

  • Pandas的索引分为显式索引隐式索引。显式索引是使用Pandas对象提供的索引,而隐式索引是使用数组本身自带的从0开始的索引。
  • 现假设某演示代码中的索引是整数,这个时候显式索引和隐式索引可能会出乱子。于是,Pandas作者发明了索引器loc(显式)iloc(隐式)手动告诉程序自己这句话是显式索引还是隐式索引。
Ⅰ 一维对象的索引

(1) 访问元素

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
import pandas as pd
#创建 sr
v = [53, 64, 72, 82]
k = ['1号', '2号', '3号','4号']
sr = pd.Series(v, index=k )

# 访问元素(显式)
age = sr.loc['3号'] # age = sr['3号']
print(age)
# 72

# 花式索引(显式)
sr2 = sr.loc[['1号', '3号']] # sr2 = sr[['1号', '3号']]
print(sr2)
# 1号 53
# 3号 72
# dtype: int64

# 修改元素(显式)
sr.loc['3号'] = 100 # sr['3号'] = 100
print(sr)
# 1号 53
# 2号 64
# 3号 100
# 4号 82
# dtype: int64


#创建 sr
v = [53, 64, 72, 82]
k = ['1号', '2号', '3号','4号']
sr = pd.Series(v, index=k )
# 访问元素(隐式)
age = sr.iloc[2] # age = sr[2]
print(age)
# 72

# 花式索引(隐式)
sr2 = sr.iloc[[0, 2]] # sr2 = sr[[0, 2]]
print(sr2)
# 1号 53
# 3号 72
# dtype: int64

# 修改元素(隐式)
sr.iloc[2] = 100 # sr[2] = 100
print(sr)
# 1号 53
# 2号 64
# 3号 100
# 4号 82
# dtype: int64

(2) 访问切片

使用显式索引时,’1号’:’3号’ 可以涵盖最后一个’3号’,但隐式与之前一样。

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
import pandas as pd

#创建 sr
v = [53, 64, 72, 82]
k = ['1号', '2号', '3号','4号']
sr = pd.Series(v, index=k )

# 访问切片(显式)
sr1 = sr.loc['1号':'3号'] # sr1 = sr['1号':'3号']
print(sr1)
# 1号 53
# 2号 64
# 3号 72
# dtype: int64

# 切片仅仅是视图(显式)
cut = sr.loc['1号':'3号'] # cut = sr['1号':'3号']
cut.loc['1号'] = 120 # cut['1号'] = 120
print(sr)
# 1号 120
# 2号 64
# 3号 72
# 4号 82
# dtype: int64

# 对象赋值仅是绑定(显式)
sr2 = sr
sr2.loc['4号'] = 400 # sr2['4号'] = 400
print(sr)
# 1号 120
# 2号 64
# 3号 72
# 4号 400
# dtype: int64

#创建 sr
v = [53, 64, 72, 82]
k = ['1号', '2号', '3号','4号']
sr = pd.Series(v, index=k )
# 访问切片(隐式)
sr1 = sr.iloc[0:3] # sr1 = sr[0:3]
print(sr1)
# 1号 53
# 2号 64
# 3号 72
# dtype: int64

# 切片仅仅是视图(隐式)
cut = sr.iloc[0:3]
cut.iloc[0] = 120 # cut[0] = 120
print(sr)
# 1号 120
# 2号 64
# 3号 72
# 4号 82
# dtype: int64

# 对象赋值仅是绑定(隐式)
sr2 = sr
sr2.iloc[3] = 400 # sr2[3] = 400
print(sr)
# 1号 120
# 2号 64
# 3号 72
# 4号 400
# dtype: int64

若想创建新变量,与 NumPy 一样,使用.copy()方法即可。

如果去掉.loc.iloc ,此时与NumPy中的索引语法完全一致。

Ⅱ 二维对象的索引

在二维对象中,索引器不能去掉,否则会报错,因此必须适应索引器的存在。

(1) 访问元素

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
import pandas as pd
#字典创建法
i = ['1号','2号','3号','4号']
v1 = [53,64,72,82]
v2 = ['女', '男', '男', '女']
sr1 = pd.Series( v1, index=i)
sr2 = pd.Series( v2, index=i)
df = pd.DataFrame({'年龄':sr1,'性别':sr2})

# 访问元素(显式)
age = df.loc['1号', '年龄']
print(age)
# 53

# 花式索引(显式)
sr3 = df.loc[['1号', '3号'], ['性别', '年龄']]
print(sr3)
# 性别 年龄
# 1号 女 53
# 3号 男 72

# 修改元素(显式)
df.loc['3号', '年龄'] = 300
print(df)
# 年龄 性别
# 1号 53 女
# 2号 64 男
# 3号 300 男
# 4号 82 女

#字典创建法
i = ['1号','2号','3号','4号']
v1 = [53,64,72,82]
v2 = ['女', '男', '男', '女']
sr1 = pd.Series( v1, index=i)
sr2 = pd.Series( v2, index=i)
df = pd.DataFrame({'年龄':sr1,'性别':sr2})

# 访问元素(隐式)
age = df.iloc[0, 0]
print(age)
# 53

# 花式索引(隐式)
sr3 = df.iloc[[0, 2], [1, 0]]
print(sr3)
# 性别 年龄
# 1号 女 53
# 3号 男 72

# 修改元素(隐式)
df.iloc[2, 0] = 300
print(df)
# 年龄 性别
# 1号 53 女
# 2号 64 男
# 3号 300 男
# 4号 82 女

在 NumPy数组中,花式索引输出的是一个向量。但在 Pandas 对象中,考虑到其行列标签的信息不能丢失,所以输出一个向量就不行了,所以这里才输出一个二维对象。

(2) 访问切片

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
import pandas as pd
#字典创建法
i = ['1号','2号','3号','4号']
v1 = [53,64,72,82]
v2 = ['女', '男', '男', '女']
sr1 = pd.Series( v1, index=i)
sr2 = pd.Series( v2, index=i)
df = pd.DataFrame({'年龄':sr1,'性别':sr2})

# 切片(显式)
sr3 = df.loc['1号':'3号', '年龄']
print(sr3)
# 1号 53
# 2号 64
# 3号 72
# Name: 年龄, dtype: int64

# 提取二维对象的行(显式)
sr4 = df.loc['3号', :]
print(sr4)
# 年龄 72
# 性别 男
# Name: 3号, dtype: object

# 提取二维对象的列(显式)
sr5 = df.loc[:, '年龄']
print(sr5)
# 1号 53
# 2号 64
# 3号 72
# 4号 82
# Name: 年龄, dtype: int64

#字典创建法
i = ['1号','2号','3号','4号']
v1 = [53,64,72,82]
v2 = ['女', '男', '男', '女']
sr1 = pd.Series( v1, index=i)
sr2 = pd.Series( v2, index=i)
df = pd.DataFrame({'年龄':sr1,'性别':sr2})

# 切片(隐式)
sr3 = df.iloc[0:3, 0]
print(sr3)
# 1号 53
# 2号 64
# 3号 72
# Name: 年龄, dtype: int64

# 提取二维对象的行(隐式)
sr4 = df.iloc[2, :]
print(sr4)
# 年龄 72
# 性别 男
# Name: 3号, dtype: object

# 提取二维对象的列(隐式)
sr5 = df.iloc[:, 0]
print(sr5)
# 1号 53
# 2号 64
# 3号 72
# 4号 82
# Name: 年龄, dtype: int64

在显示索引中,提取矩阵的行或列还有一种简便写法,即

  • 提取二维对象的行:df.loc['3号'](原理是省略后面的冒号,隐式也可以)
  • 提取二维对象的列:df['年龄'](原理是列标签本身就是二维对象的键)

2.3.3 对象的变形

Ⅰ对象的转置

有时候提供的大数据很畸形,行是特征,列是个体,这必须要先进行转置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import pandas as pd
# 创建畸形 df
v = [[53, 64, 72, 82], ['女', '男', '男', '女']]
i = ['年龄', '性别']
c = ['1号', '2号', '3号', '4号']
df= pd.DataFrame( v,index=i, columns=c)
print(df)
# 1号 2号 3号 4号
# 年龄 53 64 72 82
# 性别 女 男 男 女

# 转置
df = df.T
print(df)
# 年龄 性别
# 1号 53 女
# 2号 64 男
# 3号 72 男
# 4号 82 女
Ⅱ 对象的翻转

紧接上面的例子,对Pandas对象进行左右翻转与上下翻转。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 左右翻转
df = df.iloc[:, : : -1]
print(df)
# 性别 年龄
# 1号 女 53
# 2号 男 64
# 3号 男 72
# 4号 女 82

# 上下翻转
df = df.iloc[: : -1, :]
print(df)
# 性别 年龄
# 4号 女 82
# 3号 男 72
# 2号 男 64
# 1号 女 53
Ⅲ 对象的重塑

考虑到对象是含有行列标签的,.reshape()已不再适用,因此对象的重塑没有那么灵活。但可以做到将sr并入 df,也可以将df割出sr。

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
import pandas as pd
#数组法创建 sr
i = ['1号', '2号', '3号', '4号']
v1 = [10,20,30,40]
v2 = ['女', '男', '男', '女']
v3 = [1,2,3,4]
sr1 = pd.Series( v1, index=i)
sr2 = pd.Series( v2, index=i)
sr3 = pd.Series( v3, index=i)
print(sr1)
print(sr2)
print(sr3)
# 1号 10
# 2号 20
# 3号 30
# 4号 40
# dtype: int64
# 1号 女
# 2号 男
# 3号 男
# 4号 女
# dtype: object
# 1号 1
# 2号 2
# 3号 3
# 4号 4
# dtype: int64

df = pd.DataFrame({'年龄':sr1, '性别':sr2})
print(df)
# 年龄 性别
# 1号 10 女
# 2号 20 男
# 3号 30 男
# 4号 40 女

# 将sr并入 df
df['牌照'] = sr3
print(df)
# 年龄 性别 牌照
# 1号 10 女 1
# 2号 20 男 2
# 3号 30 男 3
# 4号 40 女 4

# 把df['年龄']分离成sr4
sr4 = df['年龄']
print(sr4)
# 1号 10
# 2号 20
# 3号 30
# 4号 40
# Name: 年龄, dtype: int64
Ⅳ 对象的拼接

Pandas 中有一个pd.concat()函数,与np.concatenate()函数语法相似。

(1) 一维对象的合并

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
import pandas as pd
# 创建 sr1 和 sr2
v1 = [10, 20,30, 40]
v2 = [40, 50, 60]
k1 = ['1号', '2号', '3号', '4号']
k2 = ['4号', '5号', '6号']
sr1 = pd.Series( v1,index=k1 )
sr2 = pd.Series( v2,index=k2 )
print(sr1)
print(sr2)
# 1号 10
# 2号 20
# 3号 30
# 4号 40
# dtype: int64
# 4号 40
# 5号 50
# 6号 60
# dtype: int64

sr3 = pd.concat([sr1, sr2])
print(sr3)
# 1号 10
# 2号 20
# 3号 30
# 4号 40
# 4号 40
# 5号 50
# 6号 60
# dtype: int64

值得注意的是,输出结果的键中出现了两个“4号”,这是因为Pandas 对象的属性,放弃了集合与字典索引中“不可重复”的特性,实际中,这可以拓展大数据分析与处理的应用场景。那么,如何保证索引是不重复的呢?对对象的属性.index.columns 使用.is unigue即可检査,返回True表示行或列不重复,False 表示有重复。

(2) 一维对象与二维对象的合并
一维对象与二维对象的合并,即可理解为:给二维对象加上一列或者一行。因此,不必使用pd.concat()函数,只需要借助“二维对象的索引”语法。

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
import pandas as pd
# 创建 sr1与 sr2
v1=[10,20,30]
v2=['女', '男', '男']
sr1 = pd.Series(v1,index=['1号','2号','3号'])
sr2 = pd.Series(v2,index=['1号','2号','3号'])
# 创建df
df = pd.DataFrame({'年龄':sr1, '性别':sr2})
print(df)
# 年龄 性别
# 1号 10 女
# 2号 20 男
# 3号 30 男

# 为二维对象加上一列特征,可以是列表、数组、张量或一维对象
df['牌照'] = [1, 2, 3]
print(df)
# 年龄 性别 牌照
# 1号 10 女 1
# 2号 20 男 2
# 3号 30 男 3

# 为二维对象加上一行个体。可以是列表、数组、张量或一维对象。
# 注意使用显示索引
df.loc['4号'] = [40, '女', 4]
print(df)
# 年龄 性别 牌照
# 1号 10 女 1
# 2号 20 男 2
# 3号 30 男 3
# 4号 40 女 4

(3) 二维对象的合并

二维对象合并仍然用 pd.concat() 函数,不过其多了一个 axis 参数。

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
# 设定 df1、df2、df3
v1 = [ [10,'女'], [20,'男'], [30,'男'], [40,'女']]
v2 = [[1,'是'], [2,'是'],[3,'是'],[4,'否']]
v3 = [[50,'男', 5,'是'], [60,'女', 6,'是']]
i1 = ['1号', '2号', '3号', '4号']
i2 = ['1号', '2号', '3号', '4号']
i3 = ['5号', '6号']
c1 = ['年龄', '性别']
c2 = ['牌照', 'ikun']
c3 = ['年龄', '性别', '牌照', 'ikun']
df1 = pd.DataFrame( v1, index=i1, columns=c1 )
df2 = pd.DataFrame( v2, index=i2, columns=c2 )
df3 = pd.DataFrame( v3, index=i3, columns=c3 )

# 按照列的方向来合并
df = pd.concat([df1, df2], axis=1)
print(df)
# 年龄 性别 牌照 ikun
# 1号 10 女 1 是
# 2号 20 男 2 是
# 3号 30 男 3 是
# 4号 40 女 4 否

# 按照行的方向来合并
df = pd.concat([df, df3], axis=0)
print(df)
# 年龄 性别 牌照 ikun
# 1号 10 女 1 是
# 2号 20 男 2 是
# 3号 30 男 3 是
# 4号 40 女 4 否
# 5号 50 男 5 是
6606

2.3.4 对象的运算

Ⅰ 对象与系数之间的运算

下列演示代码中,左侧为一维对象,右侧为二维对象。

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
import pandas as pd
# 创建一维对象 sr
sr = pd.Series([53,64,72],index=['1号','2号','3号'])
print(sr)
# 1号 53
# 2号 64
# 3号 72
# dtype: int64

sr = sr+10
print(sr)
# 1号 63
# 2号 74
# 3号 82
# dtype: int64

sr = sr*2
print(sr)
# 1号 126
# 2号 148
# 3号 164
# dtype: int64

# 创建二维对象 df
v = [[53,'女'], [64,'男'], [72,'男']]
df = pd.DataFrame(v,index=['1号', '2号', '3号'], columns=['年龄','性别'])
print(df)
# 年龄 性别
# 1号 53 女
# 2号 64 男
# 3号 72 男

# 年龄+10
df['年龄'] = df['年龄']+10
print(df)
# 年龄 性别
# 1号 63 女
# 2号 74 男
# 3号 82 男

# 年龄*2
df['年龄'] = df['年龄']*2
print(df)
# 年龄 性别
# 1号 126 女
# 2号 148 男
# 3号 164 男

df 这里面明明是一个混合型的数组,提取的年龄一列它不应该是一个字符串或者是一个 obiect 的类型的混合变量吗?

df.values 是一个NumPy数组,其整体只能以 obiect 类型的混合形式存在,因此之前需要对单独的数字列进行.astype(int)的操作。df['年龄']是Pandas对象的数字列,Pandas 为数据处理而生,尽管整体表现为object,但每一列数据单独存储,因此数字列和字符列完美共存。

Ⅱ 对象与对象之间的运算

对象做运算,必须保证其都是数字型对象,两个对象之间的维度可以不同。

(1) 一维对象之间的运算

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
import pandas as pd
# 创建 sr1
v1 =[10,20,30,40]
k1 = ['1号','2号','3号','4号']
sr1 = pd.Series(v1,index= k1 )

#创建sr2
v2 = [1, 2, 3]
k2 = ['1号', '2号','3号']
sr2 = pd.Series(v2,index= k2)

# 对象加法
s1 = sr1+sr2
print(s1)
# 1号 11.0
# 2号 22.0
# 3号 33.0
# 4号 NaN
# dtype: float64

# 对象乘法
s2 = sr1*sr2
print(s2)
# 1号 10.0
# 2号 40.0
# 3号 90.0
# 4号 NaN
# dtype: float64

(2) 二维对象之间的运算

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
import pandas as pd

# 创建df1和df2格
v1 = [[10,'女'],[28,'男'],[30,'男'],[48,'女']]
v2 = [1, 2, 3, 6]
i1 = ['1号', '2号', '3号', '4号']
c1 = ['年龄', '性别']
i2 = ['1号', '2号', '3号', '6号']
c2 = ['牌照']
df1 = pd.DataFrame( v1,index=i1,columns=c1)
df2 = pd.DataFrame( v2,index=i2,columns=c2)

# 加法
df1['加法'] = df1['年龄'] + df2['牌照']
print(df1)
# 年龄 性别 加法
# 1号 10 女 11.0
# 2号 28 男 30.0
# 3号 30 男 33.0
# 4号 48 女 NaN

# 减法、乘法、除法、幂方
df1['减法'] = df1['年龄'] - df2['牌照']
df1['乘法'] = df1['年龄'] * df2['牌照']
df1['除法'] = df1['年龄'] / df2['牌照']

本章的最后,补充两点内容,读者可自行尝试。

  1. Numpy中的数学函数可以直接对Pandas对象使用(这也证明Pandas就是建立在Numpy库之上的)
    1. 使用 np.abs()np.cos()np.exp()np.log()等数学函数时,会保留索引;
  2. Pandas中仍然存在布尔型对象,用法与NumPy无异,会保留索引。

2.3.5 对象的缺失值

Ⅰ 发现缺失值

发现缺失值使用 .isnull() 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import pandas as pd
#创建sr
v =[ 53, None, 72, 82 ]
k = ['1号', '2号', '3号', '4号']
sr = pd.Series(v, index=k)
print(sr)
# 1号 53.0
# 2号 NaN
# 3号 72.0
# 4号 82.0
# dtype: float64

print(sr.isnull())
# 1号 False
# 2号 True
# 3号 False
# 4号 False
# dtype: bool
Ⅱ 剔除缺失值

剔除缺失值使用 .dropna() 方法,返回一个Pandas对象,一维对象很好剔除;二维对象比较复杂,要么单独剔除df中含有缺失值的行,要么剔除df中含有缺失值的列。

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
# 一维对象剔除
import pandas as pd
# 创建sr
v = [53, None, 72, 82]
k = ['1号', '2号', '3号', '4号']
sr = pd.Series( v,index=k)
# 剔除sr的缺失值
dsr = sr.dropna()
print(dsr)
# 1号 53.0
# 3号 72.0
# 4号 82.0
# dtype: float64

# 二维对象剔除
import pandas as pd
#创建df
v=[[None,None],[64,None],[72,3],[82,4]]
i = ['1号', '2号', '3号', '4号']
c = ['年龄', '牌照']
df = pd.DataFrame(v,index=i,columns=c)
# 剔除df的缺失值的行
dff = df.dropna(axis=0)
print(dff)
# 年龄 牌照
# 3号 72.0 3.0
# 4号 82.0 4.0

把含有 NaN 的行剔除掉了,你也可以通过df.dropna(axis='columns')的方式剔除列。但请警惕,一般都是别除行,只因大数据中行是个体,列是特征。

此外有些同学认为,只要某行含有一个NaN 就剔除该个体太过残忍,我们可以设定一个参数,只有当该行全部是NaN,才剔除该列特征。

1
2
3
4
5
6
# 剔除df全是None的行
dff = df.dropna(how='all')
# 年龄 牌照
# 2号 64.0 NaN
# 3号 72.0 3.0
# 4号 82.0 4.0
Ⅲ 填补缺失值

填充缺失值使用 .filna() 方法,实际的数据填充没有统一的方法,很灵活。

(1) 一维对象填充

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
import pandas as pd
import numpy as np
#创建sr
v = [53, None, 72, 82]
sr = pd.Series(v, index=['1号', '2号', '3号', '4号'])
print(sr)
# 1号 53.0
# 2号 NaN
# 3号 72.0
# 4号 82.0
# dtype: float64

# 用常数0填充
sr1 = sr.fillna(0)
print(sr1)
# 1号 53.0
# 2号 0.0
# 3号 72.0
# 4号 82.0
# dtype: float64

# 用均值填充
sr2 = sr.fillna(np.mean(sr))
print(sr2)
# 1号 53.0
# 2号 69.0
# 3号 72.0
# 4号 82.0
# dtype: float64

# 用前值填充
sr3 = sr.fillna(method='ffill')
print(sr3)
# 1号 53.0
# 2号 53.0
# 3号 72.0
# 4号 82.0
# dtype: float64

# 用后值填充
sr4 = sr.fillna(method='bfill')
print(sr4)
# 1号 53.0
# 2号 72.0
# 3号 72.0
# 4号 82.0
# dtype: float64

(2) 二维对象填充

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 pandas as pd
import numpy as np

v=[[None,None],[64,None],[72,3],[82,4]]
i = ['1号', '2号', '3号', '4号']
c = ['年龄', '牌照']
df = pd.DataFrame(v,index=i,columns=c)
print(df)
# 用常数0填充
df1 = df.fillna(0)
print(df1)

# 用均值填充
df2 = df.fillna(np.mean(df))
print(df2)

# 用前值填充
df3 = df.fillna(method='ffill')
print(df3)
# 年龄 牌照
# 1号 NaN NaN
# 2号 64.0 NaN
# 3号 72.0 3.0
# 4号 82.0 4.0
# 注意,前值没有,故仍用NaN

# 用后值填充
df4 = df.fillna(method='bfill')
print(df4)

2.3.6 导入 Excel 文件

Ⅰ 创建 Excel 文件

首先,创建 Excel 文件,录入信息,第一列为index,第一行为columns。

image.png

如果你的数据没有index和columns,也即你只是想导入一个数组,那么也请先补上行列标签,后续用.values属性就能将二维对象转换为数组。

image.png

接着,将其另存为为CSV文件。

Ⅱ 放入项目文件夹

将刚刚另存为的 CSV 文件放置工程项目文件夹。

Ⅲ 导入Excel信息
    • pd.read_csv(): 从 CSV 文件中读取数据。
    • pd.read_excel(): 从 Excel 文件中读取数据。
    • pd.to_csv(): 将 DataFrame 保存到 CSV 文件。
    • pd.to_excel(): 将 DataFrame 保存到 Excel 文件。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import pandas as pd
    # 导入 Pandas 对象
    df = pd.read_csv('data.csv', index_col=0)
    print(df)
    # age gender num ikun
    # 1号 10 女 1 是
    # 2号 20 男 2 是
    # 3号 30 男 3 是
    # 4号 40 女 4 否
    # 5号 50 男 5 是
    # 6号 60 女 6 是

2.3.7 数据分析

Ⅰ 导入信息

首先,准备好Excel数据表格。

image.png

发现没有行标签,因此需要在最左侧快速填充一列顺序数字:

image.png

导入表格到python中。

1
2
3
import pandas as pd
#导入Pandas对象
df = pd.read_csv('行星数据.csv', index_col=0)
Ⅱ 聚合方法

可在输出 df时,对其使用.head()方法,使其仅输出前五行。

1
2
3
4
5
6
7
8
9
10
import pandas as pd
#导入Pandas对象
df = pd.read_csv('行星数据.csv', index_col=0)
print(df.head())
# 发现时间 发现数量 观测方法 行星质量 距地距离 轨道周期
# 0 1982 1 径向速度 11.680 40.57 29.726245
# 1 1992 3 脉冲行星法 2.000 NaN 12.797459
# 2 1992 3 脉冲行星法 3.000 NaN 93.576595
# 3 1994 3 脉冲行星法 4.000 NaN 83.094802
# 4 1995 1 径向速度 0.472 15.36 79.473673

NumPy中所有的聚合函数对Pandas对象均适用。此外,Pandas将这些函数变为对象的方法,这样不导入NumPy 也可使用

image.png

在这些方法中,像 NumPy中一样,有默认值为0的参数axis。一般不要将其数值手动设定为1,因为这种情况在数据分析中毫无意义。

此外,这些方法都忽略了缺失值,属于NumPy中聚合函数的安全版本。

Ⅲ 描述方法

在数据分析中用以上方法挨个查看未免太过麻烦,可以使用 .describe() 方法直接查看所有聚合函数的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import pandas as pd
#导入Pandas对象
df = pd.read_csv('行星数据.csv', index_col=0)

# 描述方法
info = df.describe()
print(type(info))
# <class 'pandas.core.frame.DataFrame'>
print(info)
# 发现时间 发现数量 行星质量 距地距离 轨道周期
# count 19.000000 19.000000 17.000000 16.000000 19.000000
# mean 1995.526316 2.368421 2.921071 20.436250 52.228279
# std 3.820535 1.300022 2.943290 11.624346 30.467731
# min 1982.000000 1.000000 0.472000 4.700000 2.082275
# 25% 1995.500000 1.000000 1.040000 13.470000 33.045239
# 50% 1996.000000 2.000000 2.000000 16.515000 52.687083
# 75% 1998.000000 3.000000 3.900000 21.297500 81.284238
# max 1999.000000 5.000000 11.680000 47.920000 93.576595

count:有效数据个数,以上为例,若count<19,则有缺失值。

  • 第1行count是计数项,统计每个特征的有效数量(即排除缺失值),从count可以看出,距地距离的缺失值比较多,需要考虑一定的办法填充或舍弃。
  • 第2行至第3行的mean与std 统计每列特征的均值与标准差。
  • 第4行至第8行的 min、25%、50%、75%、max的意思是五个分位点,即把数组从小到大排序后,0%、25%、50%、75%、100%五个位置上的数值的取值。显然,50%分位点即中位数。
Ⅳ 数据透视

(1) 两个特征内的数据透视

数据透视,对数据分析来讲十分重要。

现以泰坦尼克号的生还数据为例,以“是否生还”特征为考察的核心(或者说是神经网络的输出),研究其它特征(输入)与之的关系,如示例所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import pandas as pd
#导入Pandas对象
df = pd.read_csv('泰坦尼克.csv', index_col=0)

# 数据透视:一个特征
ts1 = df.pivot_table('是否生还', index='性别')
print(ts1)
# 是否生还
# 性别
# 女 0.440 (全体女性中生还的概率为44.0%)
# 男 0.375 (全体男性中生还的概率为37.5%)

# 数据透视:两个特征
ts2 = df.pivot_table('是否生还', index='性别', columns='船舱等级')
print(ts2)
# 船舱等级 一等 三等 二等
# 性别
# 女 0.25 0.571429 0.666667
# 男 0.50 0.400000 0.000000

在上述示例中,数据透视表中的数值默认是输出特征“是否生还”的均值(mean),行标签和列标签变成了其它的输入特征。

print(ts1)可以看出,女性整体的生还概率是44.0%,男性整体的生还概率为37.5%。

print(ts2)则区分的更细致。

值得注意的是,pivot_table()方法有一个很重要的参数:aggfunc,其默认值是mean,除此以外,所有的聚合函数maxminsumcount均可使用。显然,对于这里的“是否生还”来说,mean就是最好的选择,其刚好为概率。

(2) 多个特征的数据透视

有时需要考察更多特征与输出特征的关系。前面的示例只涉及到两个特征,这里,将年龄和费用都加进去。但是,这两个特征的数值很分散,之前的性别和船舱等级都可以按照类别分,现在已经不能再按类别分了。因此,需要涉及到数据透视表配套的两个重要函数:pd.cut()pd.qcut()

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
import pandas as pd
#导入Pandas对象
df = pd.read_csv('泰坦尼克.csv', index_col=0)

# 数据透视
ts1 = df.pivot_table('是否生还', index='性别')
#print(ts1)

ts2 = df.pivot_table('是否生还', index='性别', columns='船舱等级')
#print(ts2)

# 数据透视:3个特征
# 重置年龄列
age = pd.cut(df['年龄'], [0, 25, 100]) # 手动 - 以25岁为分水岭:把年龄分成2组[0, 25] [25, 100]
ts3 = df.pivot_table('是否生还', index=['性别', age], columns='船舱等级')
print(ts3)
# 船舱等级 一等 三等 二等
# 性别 年龄
# 女 (0, 25] 0.142857 0.000000 1.0
# (25, 100] 0.400000 0.666667 0.6
# 男 (0, 25] 0.500000 0.500000 NaN
# (25, 100] 0.500000 0.375000 0.0

# 数据透视:4个特征
# 重置费用列
fare = pd.qcut(df['费用'], 2) # 自动 - 将费用分为2部分
ts4 = df.pivot_table('是否生还', columns=['性别', age], index=['船舱等级', fare])
print(ts4)
# 性别 女 男
# 年龄 (0, 25] (25, 100] (0, 25] (25, 100]
# 船舱等级 费用
# 一等 (10.206000000000001, 26.88] 0.5 0.333333 0.5 0.50
# (26.88, 49.654] 0.0 0.500000 NaN NaN
# 三等 (10.206000000000001, 26.88] 0.0 0.500000 0.0 0.75
# (26.88, 49.654] NaN 0.750000 1.0 0.00
# 二等 (10.206000000000001, 26.88] 1.0 0.333333 NaN NaN
# (26.88, 49.654] NaN 1.000000 NaN 0.00

pd.cut()函数需要手动设置分割点,也可以设置为[0,18,60,100]pd.qcut()函数可自动分割,如果需要分割成3部分,可以设置为 3。当然,这里只演示了4个特征,你可以继续在pivot_table()indexcolumns的列表参数里添加其他特征,但也不要添加太多,会很混乱。

2.4 matplotlib学习

2.4.1 绘图基础

Matplotlib库太大,画图通常仅仅使用其中的核心模块 matplotlib.pyplot ,并给其一个别名 plt,即import matplotlib.pyplot as plt

Ⅰ 绘制图像

展示一个很简单的图形绘制示例,这个示例的代码就像在 Matlab 里一样。

1
2
3
4
5
6
7
8
9
import matplotlib.pyplot as plt
# matplotlib inline

# 绘制图像
Fig1 = plt.figure() # 创建新的绘图窗口
x = [1, 2, 3, 4, 5 ] # 数据的x值
y = [1, 8, 27, 64, 125 ] # 数据的y值
plt.plot(x,y) # plot函数:先描点,再连线
plt.show() # 显示图像
Ⅱ 保存图像

.savefig()方法,其需要一个r字符串:r'绝对路径\图形名.后缀

  • 绝对路径:如果要保存到桌面,绝对路径即:C:Users\用户名\Desklop
  • 后缀:可保存图形的格式很多,包括:eps、jpg、pdf、png、ps、svg等。为了保存清晰的图,推荐保存至svg 矢量格式,即
1
Fig2.savefig(r'c:\Users\zjj\Desktop\我的图.svg')

保存为svg 格式后,可直接拖至 Word 或 Visio 中,即可显示高清矢量图。

Ⅲ 2种绘图方式

Matplollib中有两种画图方式:① Matlab方式② 面向对象方式。这两种方式都可以完成同一个目的,也可以相互转化(3.3小节的颜色条只有Mallab方式具备,4.1与4.2二者转化时形式出现区别)。

两种画图方式

Ⅳ 图窗与坐标轴

图形窗口(figure)在Matlab中会单独弹出,该窗口中可容纳元素,也可以是空的窗口。在Jupyter中,由于我们将图形嵌入到了Out[]中,所以不会看到有 figure 弹出。虽然看不到窗口,但在画图之前,仍然要手动 Fig1=plt.figure()创建图窗,毕竟保存图形的.savefg()方法是需要图形名,且后面几章会更加强调。坐标轴(axes)是一个矩形,其下方是x的数值与刻度,左侧是y轴的数值与刻度。因此,将1.4示例的Ou[4]中的蓝色曲线副除,剩余部分全是axcs。

2.4.2 多图形的绘制

在Jupyter的某个代码块中使用 Fig1=plt.figure()创建图窗后,其范围仅仅在此代码块内,跳出此代码块外的其它面图命令将与Fig1无关。

因此,画一幅图,请在一个代码块内完成,不得分块。

Ⅰ 绘制多线条

在同一个图窗内绘制多线条,按Matlab画图方式来演示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import matplotlib.pyplot as plt

# 准备敬据
x = [1, 2, 3, 4, 5]
y1 = [1, 2, 3, 4, 5]
y2 = [0, 0, 0, 0, 0]
y3 = [-1, -2, -3, -4, -5]

# Matlab方式
fig = plt.figure()
plt.plot(x,y1)
plt.plot(x,y2)
plt.plot(x,y3)
plt.show()

fig.savefig('pie_chart.svg', format='svg') # 保存为矢量图svg,高清不失真
Ⅱ 绘制多个子图

类似Matlab,使用plt.subplot(3, 1, 1)语句。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import matplotlib.pyplot as plt

# 准备敬据
x = [1, 2, 3, 4, 5]
y1 = [1, 2, 3, 4, 5]
y2 = [0, 0, 0, 0, 0]
y3 = [-1, -2, -3, -4, -5]

# Matlab方式
fig = plt.figure()
plt.subplot(3, 1, 1)
plt.plot(x,y1)
plt.subplot(3, 1, 2)
plt.plot(x,y2)
plt.subplot(3, 1, 3)
plt.plot(x,y3)
plt.show()

2.4.3 图表类型

Ⅰ 基本的图表类型

plt提供5类基本图表,分别是二维图、网格图、统计图、轮廓图、三维图。以下罗列深度学习中可能用的。

https://matplotlib.org/stable/plot_types/index

(1) 二维图

二维图,只需要两个向量即可绘图,其中线型图可以替代其它所有二维图。

不同类型的二维图

(2) 网格图

网格图,只需要一个矩阵即可绘图,以下网格图都有一定的实用价值。

常见类型网格图

(3) 统计图

统计图一般做数据分析时使用。

常见统计图

以上图形只会挑选其中最关键、使用最频繁的函数进行讲解。其它情况可百度或者去官网查看使用方法。

除了上述链接中的这五类基本图表外,还有更多作者提前画好的花哨的靓图详见 https://matplotlib.org/stable/gallery/index.html,看到自己想画的图点进去即可学习示例代码。

最后,作者还温馨地向小白的我们提供了从0开始到大神的完整教程,详见:https://matplotlib.org/stable/tutorials/index.html。

Ⅱ 二维图绘制

二维图,仅演示plot线型图函数,只因其可以替代其它所有二维图。

(1) 设置颜色

plot()函数含color关键字,可以设置线条的颜色,如示例所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import matplotlib.pyplot as plt

# 准备敬据
x = [1, 2, 3, 4, 5]
y1 = [1, 2, 3, 4, 5]
y2 = [0, 0, 0, 0, 0]
y3 = [-1, -2, -3, -4, -5]

# Matlab方式
fig = plt.figure()
plt.plot(x,y1, color='red') # 红色全拼
plt.plot(x,y2, color='b') # 蓝色缩写
plt.plot(x,y3, color='#47ADC7') # 16进制颜色代码
plt.show()

颜色设置

(2) 设置不同风格

plot()函数含linestyle 关键字,可以设置线条的风格,如示例所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import matplotlib.pyplot as plt

# 准备敬据
x = [1, 2, 3, 4, 5]
y1 = [1, 2, 3, 4, 5]
y2 = [0, 0, 0, 0, 0]
y3 = [-1, -2, -3, -4, -5]

# Matlab方式
fig = plt.figure()
plt.plot(x,y1, color='red', linestyle='-')
plt.plot(x,y2, color='b', linestyle='-.')
plt.plot(x,y3, color='#47ADC7', linestyle=':')
plt.show()

(3) 设置粗细

plot()函数含linewidth 关键字,可以设置线条的粗细,如示例所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import matplotlib.pyplot as plt

# 准备敬据
x = [1, 2, 3, 4, 5]
y1 = [1, 2, 3, 4, 5]
y2 = [0, 0, 0, 0, 0]
y3 = [-1, -2, -3, -4, -5]

# Matlab方式
fig = plt.figure()
plt.plot(x,y1, color='red', linestyle='-', linewidth=0.5)
plt.plot(x,y2, color='b', linestyle='-.', linewidth=3.5)
plt.plot(x,y3, color='#47ADC7', linestyle=':', linewidth=6.5)
plt.show()

粗细设置

(4) 设置标记

plot()函数含 marker 关键字,可以设置线条的标记,如示例所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import matplotlib.pyplot as plt

# 准备敬据
x = [1, 2, 3, 4, 5]
y1 = [1, 2, 3, 4, 5]
y2 = [0, 0, 0, 0, 0]
y3 = [-1, -2, -3, -4, -5]

# Matlab方式
fig = plt.figure()
plt.plot(x,y1, color='red', linestyle='-', marker='o')
plt.plot(x,y2, color='b', linestyle='-.', marker='^')
plt.plot(x,y3, color='#47ADC7', linestyle=':', marker='s')
plt.show()

标记设置

标记的尺寸可以由 markersize 关键字调整,其值以3至9为宜

Ⅲ 网格图

网格图,仅演示imshow()函数,只因另外两个在深度学习中几乎用不到。

1
2
3
4
5
6
7
8
9
10
11
import matplotlib.pyplot as plt
# 准备数据
import numpy as np

x = np.linspace(0,10,1000)
l = np.sin(x)*np.cos(x).reshape(-1,1)
fig = plt.figure()
plt.imshow(l, cmap='jet')
# 显示颜色柱
plt.colorbar()
plt.show()

网格图

Ⅳ 统计图

统计图,仅演示hist函数,只因其它函数主要出现在数据分析领域。为避免将直方图hist与条形图bar混,现说明:条形图bar可用plot 替代;hist则是统计学的函数,是为了看清某分布的均值与标准差。

1
2
3
4
5
6
7
8
9
10
import matplotlib.pyplot as plt
# 准备数据
import numpy as np

# 创建 10000 个标准正态分布的样本
data=np.random.randn(10000)
# Matlab 方式
fig = plt.figure()
plt.hist( data )
plt.show()

默认直方图

(1) 参数1:区间个数

bins 关键字参数即区间划分的数量,默认为10,现将其改为为30。

1
2
3
fig = plt.figure()
plt.hist( data, bins=30 )
plt.show()

直方图区间参数

(2) 参数2:透明度

Alpha 关键字表示透明度,默认为1,现将其改为为0.5。

1
2
3
fig = plt.figure()
plt.hist( data, alpha=0.5 )
plt.show()

透明度设置

(3) 参数3:图表类型

histtype 表示类型,默认为’bar’,现将其改为为’stepfilled’,图形浑然一体。

1
2
3
fig = plt.figure()
plt.hist( data, histtype='stepfilled' )
plt.show()

图表类型

(4) 参数4:直方图颜色

使用color表示直方图的颜色。

1
2
3
fig = plt.figure()
plt.hist( data, color='green' ) # 颜色设为绿色
plt.show()

颜色参数

(5) 参数5:边缘颜色

使用edgecolor表示直方图边缘的颜色。

1
2
3
fig = plt.figure()
plt.hist( data, edgecolor="k" ) # 边缘设置为黑色
plt.show()

2.4.4 图窗属性

Ⅰ 坐标轴上下限

尽管 Matplotlib 会自动调整图窗为最佳的坐标轴上下限,但叛逆的我们知道很多时候仍需手动设置,才能适应当时的情况。

设置其坐标轴上下限,有两种方法:lim法与axis法。

(1) lim法

1
2
3
4
5
6
7
8
9
10
11
12
import matplotlib.pyplot as plt

# 准备数据
x = [1, 2, 3, 4, 5]
y = [1, 8, 27, 64, 125]

# Matlab方式(lim法)
fig = plt.figure()
plt.plot(x,y)
plt.xlim(1, 10) # 设置x轴为1~10
plt.ylim(1, 135) # 设置y轴为1~135
plt.show()

lim法

(2) axis法

1
2
3
4
5
fig = plt.figure()
plt.plot(x,y)
plt.axis([1,6, 1, 150]) # 设置x轴为1~16, 设置y轴为1~150
plt.grid() # 添加网格
plt.show()

axis法

Ⅱ 图标题与轴名称

主要使用xlabelylabeltitle函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
import matplotlib.pyplot as plt  # python中画图所用到的第三方库

# 1956-1920年对应的GDP值,单位亿元
y = [1470.1, 1447.5, 1312.3, 1071.4, 1030.7, 911.6, 859.8, 824.4, 679.1]
# 1952-1960年
x = [1960, 1959, 1958, 1957, 1956, 1955, 1954, 1953, 1952]

plt.scatter(x, y) # 画点,将列表中的值分别画在plot中
plt.plot(x, y, color='r') # 连线,按顺序将
plt.xlabel("year") # 横轴的标签
plt.ylabel("GDP/Billion") # 纵轴的标签
plt.title("Changes in China's GDP since the founding of the PRC") # 图像标头
plt.show() # 相当于将图像显示出,必须要加上才能显示出图像

显示结果

Ⅲ 图例

一般图例会出现在二维图与统计图中,网格图则用的是颜色条。

主要使用labellegend两个参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import matplotlib.pyplot as plt
# 准备数据
import numpy as np

# 准备数据
x = np.array([1, 2, 3, 4, 5])
y1 = np.array([1, 8, 27, 64, 125])
y2 = y1/2
y3 = y2 + 10

# Matlab方式(lim法)
fig = plt.figure()
plt.plot(x,y1, label='legend1')
plt.plot(x,y2, label='legend2')
plt.plot(x,y3, label='legend3')
plt.legend()
plt.grid()
plt.show()

如果你不想展示某些线条的图例,只需要去除该函数中的label关键字即可(除了 plot 外,其它画图函数也携带label关键字参数)。

当然,有些教程不使用label关键字参数,使用下个示例的操作来替代上面的label

1
2
3
4
5
Fig3 = plt.figure()
plt.plot(x,y1)
plt.plot(x,y2)
plt.plot(x,y3)
plt.legend(['y=x', 'y=0', 'y=-x’])

legend 还有三个常用的关键字参数:locframeonncol

  • loc用于表示图例位置,该关键字在upper、center、lower中选一个,在left、center、right 中选一个,用法如 loc='upper right',也可以 loc='best'
  • frameon用于表示图例边框,去边框是frameon=False
  • ncol用于表示图例的列数,默认是1列,也可以通过ncol=2调为2列。

解决python画图无法显示中文的问题 - CSDN博客

Python绘图设置新罗马字体和设置字体大小 - Bilibili

  • Copyrights © 2015-2024 wjh
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信