前言
在我学习机器学习的过程中,我频繁遇到了Python中处理数据的相关库,主要有Numpy,Matplotlib和Pandas库,它们分别对应着数据的表示,展示和统计分析,熟练掌握这些库的应用可以提高我们对数据的处理能力。所以我依据慕课上嵩天老师的《Python数据分析与展示》对相关内容进行总结。 课程链接
我们知道Python有列表(List)、字典(Set)、元组(Tuple)、集合(Dictionary)等基本数据类型,而Python本身是没有数组类型的。在使用数组的过程中,会发现它和列表很相似,一个列表可以存储一个一维的数组,列表的嵌套即可实现多维数组。当然它们也有不同,比如列表可以存储不同类型的数据,而数组只能存储相同类型的数据,所以在通用性上数组不及列表。那么我们为什么要引入数组呢?原因就在于数组在科学计算中可以发挥它的优势,减少循环运算的次数,提高运行效率。可以对比一下:
import numpy as np
# 计算a^2 + b^3
# 列表计算
def pysum():
a = [0, 1, 2, 3, 4]
b = [5, 6, 7, 8, 9]
c = []
for i in range(len(a)):
c.append(a[i]**2 + b[i]**3)
return c
# 数组计算
def npsum():
a = np.array([0, 1, 2, 3, 4])
b = np.array([5, 6, 7, 8, 9])
c = a**2 + b**3
return c
所以在列表之外引入数组主要有以下原因:
- 数组对象可以去掉元素间运算所需的循环,使一维向量更像单个数据
- 设置专门的数组对象,经过优化,可以提升这类应用的运算速度
- 科学计算中,一个维度所有数据的类型往往相同,而数组对象采用相同的数据类型,有助于节省运算和存储空间
Numpy的数组对象—ndarray
(一)创建一个ndarray类型数组
1.用列表、元组等类型创建
import numpy as np
a = np.array([0, 1, 2, 3, 4]) # 列表
b = np.array((0, 1, 2, 3, 4)) # 元组
c = np.array([[0, 1, 2], (3, 4, 5)]) # 列表和元组混合,最外层必须用[]括起来
print(a)
print(b)
print(c)
运行结果:
[0 1 2 3 4]
[0 1 2 3 4]
[[0 1 2]
[3 4 5]]
2.使用Numpy中的函数创建
import numpy as np
a = np.arange(5)
b = np.ones((2, 5))
c = np.zeros((2, 5))
d = np.full((2, 5), 3)
e = np.eye(3)
print(a)
print(b)
print(c)
print(d)
print(e)
运行结果:
[0 1 2 3 4]
[[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]]
[[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]]
[[3 3 3 3 3]
[3 3 3 3 3]]
[[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
import numpy as np
a = np.linspace(1, 10, 4) # 从1到10等间距生成4个数
print(a)
b = np.linspace(1, 10, 4)
c = np.concatenate((a, b))
print(c)
运行结果:
[ 1. 4. 7. 10.]
[ 1. 4. 7. 10. 1. 4. 7. 10.]
(二)ndarray对象属性
import numpy as np
a = np.array([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9]])
print(a.ndim) # 维度是二维的
print(a.shape) # 形状是两行5列
print(a.size) # 大小是2*5
print(a.dtype) # 数据类型是整型
print(a.itemsize) # 整型是4个字节
运行结果:
2
(2, 5)
10
int32
4
(三)ndarray数组变换
1.维度变换
注意reshape和resize的区别,一个不改变原数组,形成新数组,一个改变原数组
import numpy as np
a = np.ones((2, 3, 4))
b = a.reshape((3, 8))
print(b)
print(a) # a没有改变
a.resize((3, 8)) # a改变了
print(a)
a = np.ones((2, 3, 4))
c = a.swapaxes(0, 1) # 0维和1维进行交换
print(c)
d = a.flatten()
print(d)
运行结果:
[[1. 1. 1. 1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1. 1. 1. 1.]]
[[[1. 1. 1. 1.]
[1. 1. 1. 1.]
[1. 1. 1. 1.]]
[[1. 1. 1. 1.]
[1. 1. 1. 1.]
[1. 1. 1. 1.]]]
[[1. 1. 1. 1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1. 1. 1. 1.]]
[[[1. 1. 1. 1.]
[1. 1. 1. 1.]]
[[1. 1. 1. 1.]
[1. 1. 1. 1.]]
[[1. 1. 1. 1.]
[1. 1. 1. 1.]]]
[1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]
2.数组类型变换
astype会产生一个新数组,其类型为new_type
import numpy as np
a = np.ones((2, 3))
print(a)
b = a.astype(np.int) # 将数据变为整型
print(b)
运行结果:
[[1. 1. 1.]
[1. 1. 1.]]
[[1 1 1]
[1 1 1]]
3.数组转换为列表
import numpy as np
a = np.ones((2, 3))
print(a)
b = a.tolist()
print(b)
运行结果:
[[1. 1. 1.]
[1. 1. 1.]]
[[1.0, 1.0, 1.0], [1.0, 1.0, 1.0]]
(四)数组的索引和切片
- 和列表的切片和索引相同
import numpy as np
# 一维数组
a = np.array([0, 1, 2, 3, 4, 5, 6])
print(a[2]) # 索引
# 切片
print(a[ 1 : 6 : 2]) # 从1号位置到6号位置每隔2空取一位数,最后一位不包括
# 多维数组
a = np.arange(24).reshape(2, 3, 4)
print(a[1, 2, 3]) # 索引
# 切片
print(a[:, 1, -3]) # 0维全取,1维取1,2维取倒数第三个
print(a[:, 1:3, :]) # 0维全取,1为取1和2,2维全取
print(a[:, :, ::2]) # 0,1维全取,2维以步长为2跳跃取
运行结果:
2
[1 3 5]
23
[ 5 17]
[[[ 4 5 6 7]
[ 8 9 10 11]]
[[16 17 18 19]
[20 21 22 23]]]
[[[ 0 2]
[ 4 6]
[ 8 10]]
[[12 14]
[16 18]
[20 22]]]
(五)数组的运算
(六)数组和矩阵的区别
在Numpy中,数组和矩阵形式很像,在运算的时候却有较大的差异,因此我主要从它们运算的层面总结一下它们的区别。
1.创建数组和矩阵
import numpy as np
# 创建一个二维的数组和矩阵
a = np.array([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9]])
b = np.mat([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9]])
print(a)
print(type(a))
print(b)
print(type(b))
运行结果:
[[0 1 2 3 4]
[5 6 7 8 9]]
<class 'numpy.ndarray'>
[[0 1 2 3 4]
[5 6 7 8 9]]
<class 'numpy.matrix'>
可以看出两者创建方法类似,输出结果除了类型不同其余完全相同。另外,两者可以用np.array和np.mat进行类型互换。
2.相关运算
- 加减运算
import numpy as np
a = np.array([[0, 1, 2],
[3, 4, 5]])
b = np.array([[1, 2, 3],
[4, 5, 6]])
c = np.array([[0, 1, 2],
[3, 4, 5]])
d = np.array([[1, 2, 3],
[4, 5, 6]])
print(a+b)
print(c+d)
print(a+c)
运行结果:
[[ 1 3 5]
[ 7 9 11]]
[[ 1 3 5]
[ 7 9 11]]
[[ 0 2 4]
[ 6 8 10]]
可以看出,两者加减运算完全相同,甚至可以进行相互运算,若数组和矩阵进行相互运算,运算结果为数组类型
- 数乘运算
import numpy as np
a = np.array([[0, 1, 2],
[3, 4, 5]])
b = np.mat([[1, 2, 3],
[4, 5, 6]])
print(a*10)
print(b*10)
运行结果:
[[ 0 10 20]
[30 40 50]]
[[10 20 30]
[40 50 60]]
数乘运算完全相同
- 点乘运算
import numpy as np
a = np.array([[0, 1],
[3, 4]])
b = np.mat([[0, 1],
[3, 4]])
print(a*a)
print(b*b)
print(np.dot(a, a))
print(np.dot(b, b))
运行结果:
[[ 0 1]
[ 9 16]]
[[ 3 4]
[12 19]]
[[ 3 4]
[12 19]]
[[ 3 4]
[12 19]]
可以看出,当用乘号*分别对数组和矩阵相乘时,矩阵做的是点乘运算,而数组做的是对应元素相乘的运算;当用np.dot的时候,两者都是点乘运算
- 转置运算
import numpy as np
a = np.array([[0, 1],
[3, 4]])
b = np.mat([[0, 1],
[3, 4]])
print(a.T)
print(b.T)
运行结果:
[[0 3]
[1 4]]
[[0 3]
[1 4]]
两者转置运算完全相同
- 求逆运算
import numpy as np
a = np.array([[0, 1],
[3, 4]])
b = np.mat([[0, 1],
[3, 4]])
# print(a.I) # 报错:AttributeError: 'numpy.ndarray' object has no attribute 'I'
print(b.I)
print(np.linalg.inv(a))
print(np.linalg.inv(b))
运行结果:
[[-1.33333333 0.33333333]
[ 1. 0. ]]
[[-1.33333333 0.33333333]
[ 1. 0. ]]
[[-1.33333333 0.33333333]
[ 1. 0. ]]
可见,求逆运算中.I操作只适用于矩阵,inv()操作适合两者