文章目录
1 Numpy介绍
内建数据结构:list --> Array模块 --> Numpy基础包
1.1 Numpy
Numpy(Numerical Python)是目前Python数值计算中最为重要的基础包。大多数计算包都提供了基于Numpy的科学函数功能,将Numpy的数组对象作为数据交换的通用语。
1.2 Numpy特点
- 提供高效多维数组
- 提供了基于数组的便捷算术操作以及广播机制
- 对数据进行快速的矩阵计算
- 对硬盘中数组数据进行读写操作
官网:https://numpy.org/devdocs/user/quickstart.html
2 Numpy基本操作
2.1 创建数组
创建N维数组对象:生成数组最简单的方式就是使用array函数。array函数接收任何的序列型(sequence)对象,生成一个新的包含传递数据的Numpy数组。
numpy.array(object)
import numpy
li = [1,2,3,4]
arr = numpy.array(li)
print(type(arr),arr)
numpy.arange([start,] stop[, step,], dtype=None)
import numpy
# 左开右闭,类似与range()函数
arr = numpy.arange(start=1,stop=5)
print(type(arr),arr) # <class 'numpy.ndarray'> [1 2 3 4]
注意:
- ndarray数组是一个通用的多维同类数据容器,意味着数组里面每一个元素均为相同类型。(同质性)
2.2 数组的数据类型
Numpy数据类型
类型 | 类型代码 | 描述 |
---|---|---|
int8,uint8 | i1,u1 | 有符号和无符号的8数位整数 |
int16,uint16 | i2,u2 | 有符号和无符号的16数位整数 |
int32,uint32 | i4,u4 | 有符号和无符号的32数位整数 |
int64,uint64 | i8,u8 | 有符号和无符号的64数位整数 |
float16 | f2 | 半精度浮点数 |
float32 | f4 | 标准单精度浮点数 |
float64 | f8 | 标准双精度浮点数 |
bool | ? | 布尔值,存储True或False |
string_ | S | ASCII字符串类型,eg:‘S10’ |
unicode_ | U | Unicode类型,eg:‘U10’ |
2.2.1 查看数组数据类型
- arr.dtype # 查看数组的数据类型
import numpy
arr = numpy.arrange(1,5)
print(arr.dtype) # 类型为int32
arr1 = numpy.array([1,2,3,4.5])
print(arr1.dtype) # float64
arr2 = numpy.array([True,1,2,'www',5.0])
print(arr2.dtype) # <U11
注意
- numpy.array()会自动推断生成数组的数据类型,即会自动转换数据类型
2.2.2 指定数组的数据类型
- numpy.array(object,dtype=None) # 创建数组时通过dtype直接指定
import numpy
arr = numpy.array([1,2,3],dtype='f8')
print(arr,arr.dtype) # [1. 2. 3.] float64
arr1 = numpy.array([1,2,3,0],dtype='?')
print(arr1,arr1.dtype) # [ True True True False] bool
- arr.astype(dtype) # 修改数组数据类型,但并没有改变数组本身的数据
import numpy
arr = numpy.array([1,2,3])
arr = arr.astype(numpy.float32)
print(arr.dtype) # float32
2.3 数组形状
2.3.1 查看数组维度和形状
-
arr.ndim:查看数组的维度,返回值为整数
-
arr.shape:查看数组的形状,返回值为行列组成的元组
import numpy
arr = numpy.array([1,2,3])
print(arr.ndim) # 输出结果:1
print(arr.shape) # 输出结果:(3,)
2.3.2 N-维数组对象
- 一维
axis 0
1 | 2 | 3 |
---|
- 二维
axis 0–> 0轴:行
axis 1–> 1轴:列
(0,1) --> (行,列)
1 | 2 | 3 |
---|---|---|
4 | 5 | 6 |
注意:如果向array的二维数组传递不对等元素,则子列表会被当成独立的元素,从而变成一维的。
- 三维
0轴:面或者块(页)
1轴:行
2轴:列
import numpy as np
arr = np.array([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]])
print(arr.shape) # 结果:(2, 2, 3)
print(arr)
'''
结果:
[[[ 1 2 3]
[ 4 5 6]]
[[ 7 8 9]
[10 11 12]]]
'''
注意:如果向array的三维数组传递不对等元素,则子列表会被当成独立的元素,从而变成二维的。
图片是三维的:宽、高、颜色。 声音是四维的。
拓展:
from matplotlib import pyplot as plt
# 读取图片数据
img = plt.imread("hsq.jpg")
print(img)
'''
array([[[118, 91, 48],
[120, 93, 50],
[122, 95, 52],
...,
[161, 153, 150],
[162, 154, 151],
[161, 153, 150]],
[[118, 91, 48],
[119, 92, 49],
[121, 94, 51],
...,
[155, 147, 144],
[161, 153, 150],
[171, 163, 160]],
[[119, 92, 49],
[120, 93, 50],
[121, 94, 51],
...,
[161, 153, 151],
[160, 152, 150],
[166, 158, 156]],
...,
[[175, 157, 145],
[171, 153, 141],
[165, 147, 135],
...,
[208, 205, 200],
[175, 172, 167],
[130, 127, 122]],
[[172, 155, 145],
[169, 152, 142],
[164, 146, 136],
...,
[160, 157, 152],
[109, 106, 101],
[ 69, 66, 61]],
[[163, 146, 136],
[163, 146, 136],
[161, 143, 133],
...,
[105, 102, 97],
[ 65, 62, 57],
[ 57, 54, 49]]], dtype=uint8)
'''
print(img.ndim)
print(img.shape)
'''
3
(220, 288, 3)
'''
plt.imshow(img)
2.4 重塑数组
对应方法不会改变原数组本身
2.4.1 一维转多维
- arr.reshape(shape,order=‘C’)
参数:shape为数组重塑形状;order表示重塑方向,其中C顺序(行方向)重塑、F顺序(列方向)重塑。
import numpy as np
arr1 = np.arange(6)
print(arr1,arr1.shape) # [0 1 2 3 4 5] (6,)
# 注意重塑后的行列数乘积必须等于原来的行列数乘积,这里 2*3 = 6*1
arr2 = arr1.reshape(2,3)
print(arr2,'\n',arr2.shape)
'''
结果:
[[0 1 2]
[3 4 5]]
(2, 3)
'''
# print(arr1.reshape(-1,3)) # 形状维度可以有一个值为-1,表示维度通过数据进行判断
arr3 = arange(12).reshape(2,2,3)
print(arr3)
2.4.2 多维转一维
- arr.flatten()
- arr.ravel()
import numpy
arr = numpy.arange(16).reshape(4,4)
'''
结果:
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]
[12 13 14 15]]
'''
arr1 = arr.flatten() # 扁平化
print(arr1)
'''
结果:
[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
'''
arr2 = arr.ravel() # 分散化
print(arr2)
'''
结果:
[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
'''
2.4.3 数组转置与换轴
- arr.transpose():转置
- arr.T:转置
- arr.swapaxes():换轴
import numpy as np
arr = np.arange(20).reshape(4,5)
print(arr)
'''
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]
[15 16 17 18 19]]
'''
# 转置:可以用arr.transpose() 或者 arr.T
arr1 = arr.transpose()
print(arr1)
'''
[[ 0 5 10 15]
[ 1 6 11 16]
[ 2 7 12 17]
[ 3 8 13 18]
[ 4 9 14 19]]
'''
# 换轴:arr.swapaxes(axis1,axis2)
arr2 = arr.swapaxes(1,0) # 把0轴(行)和1轴(列)互换
print(arr2)
'''
[[ 0 5 10 15]
[ 1 6 11 16]
[ 2 7 12 17]
[ 3 8 13 18]
[ 4 9 14 19]]
'''
2.5 创建数组的其他函数
2.5.1 常用函数汇总
函数名 | 描述 |
---|---|
array | 将输入数据转换为ndarray数组,自动推断数据类型,默认复制所有输入数据 |
asarray | 将输入数据转换为ndarray数组,但输入数据已经是ndarray则不再复制 |
arange | python内建函数range的数组版,返回一个数组 |
zeros | 根据给定形状和数据类型生成全0数组。注意:需要传入序列或者整数 |
ones | 根据给定形状和数据类型生成全1数组。注意:需要传入序列或者整数 |
empty | 根据给定形状和数据类型生成一个没有初始化数值的空数组 |
full | 根据给定形状和数据类型生成指定数值的数组 |
eye | 构造单位矩阵,可以创建矩形和方形单位矩阵 |
identity | 只能创建方形单位矩阵 |
one_like | 构造相同形状的全为1的数组 |
2.5.2 array函数和asarray函数的区别
import numpy as np
# 将列表转为array类型
data = [1,2,3,4] # list类型
arr1 = np.array(data)
arr2 = np.asarray(data)
print(arr1) # [1,2,3,4]
print(arr2) # [1,2,3,4]
# 修改data列表里面的2改为5,arr1和arr2的值都没有受影响
data[1] = 5
print(data) # [1, 5, 3, 4]
print(arr1) # [1 2 3 4]
print(arr2) # [1 2 3 4]
data_2 = np.arange(4) # ndarray类型
print(data_2) # [0 1 2 3]
arr3 = np.array(data_2)
arr4 = np.asarray(data_2)
data_2[1] = 5
print(data_2) # 结果为[0 5 2 3] 数据有改变
print(arr3) # 结果为[0 1 2 3],不变,说明array函数拷贝了副本,不会受原来的值变动影响
print(arr4) # 结果为[0 5 2 3] 数据有改变,说明asarray函数不拷贝,会随原来的值变动
2.5.3 其他方法实例
import numpy
arr0 = numpy.zeros(1) # 生成全为0的一维数组
arr1 = numpy.zeros((2,3)) # 生成全为0的二维
arr2 = numpy.empty(1).dtype() # 结果为dtype('float')
arr3 = numpy.full(3,4) # 生成元素全为4的一维数组,元素个数为3
arr4 = numpy.full(3,[1,2,3]) # 结果为array([1,2,3])
arr5 = numpy.full((2,3),2) # 生成一个元素全为2的二维数组,两行三列
arr6 = numpy.eye(3) # 生成一个3行3列的单位矩阵
arr7 = numpy.ones_like(arr) # 生成一个形状与arr一致但值全为1的数组
2.6 数组的拷贝
栈区 --> 内存中的内存
堆区 --> 内存中的硬盘
2.6.1 不拷贝
只是简单的赋值,比如 arr2 = arr,也就是说,arr2与arr指向堆区的内存空间一致。
import numpy as np
arr = np.arange(12)
print(arr) # [ 0 1 2 3 4 5 6 7 8 9 10 11]
arr2 = arr
# 判断内存地址是不是一致,用is判断(也可以用" .id "的方法来判断)
print(arr is arr2) # True
arr2[1] = 20
print(arr) # [ 0 20 2 3 4 5 6 7 8 9 10 11]
2.6.2 浅拷贝
在栈区重新开辟一块内存空间指向同一个堆区 arr3 = arr.view()。注意:当我通过arr3去修改堆区某元素的值,那arr访问的就是改变后的值
import numpy as np
arr = np.arange(12)
print(arr) # [ 0 1 2 3 4 5 6 7 8 9 10 11]
# 视图层的拷贝,浅拷贝
arr3 = arr.view()
print(arr3) # [ 0 1 2 3 4 5 6 7 8 9 10 11]
print(arr3 is arr) # False
arr3[0] = 10
print(arr) # [10 1 2 3 4 5 6 7 8 9 10 11]
2.6.3 深拷贝
在栈区重新开辟一块内存空间指向堆区重新开辟的一块内存空间 arr4 = arr.copy()。注意:当我通过arr3去修改堆区某元素的值,arr的值不会受影响。
import numpy as np
arr = np.arange(12)
print(arr) # [ 0 1 2 3 4 5 6 7 8 9 10 11]
arr4 = arr.copy()
print(arr4) # [ 0 1 2 3 4 5 6 7 8 9 10 11]
print(arr4 is arr) # False
arr4[0] = 10
print(arr) # [ 0 1 2 3 4 5 6 7 8 9 10 11]
2.7 Numpy数组算术
数组之所以重要是因为它允许我们进行批量操作数据并且没有任何for循环,也被称为向量化。
2.7.1 数组与标量的算术操作
数组与标量的算术操作(和Python的运算符类似)会把计算参数传递给数组的每一个元素。(两个特殊值:nan --> not a number ;inf --> infinity 无穷数,分母为0时得到。)
实例
import numpy
arr = numpy.arange(6).reshape(2,3)
print(arr+1)
'''
[[1 2 3]
[4 5 6]]
'''
拓展
# [0,1,2,3,4,5] 中每个元素加1
# 方法一:列表推导式
lt1 = [i+1 for i in range(6)]
# 方法二:用map()函数
# map(func,*iterables) 映射,将可迭代对象里面的每一个元素传到func函数里面去进行处理并返回结果
def add_one(x):
return x+1
list(map(add_one,range(6))
2.7.2 两个等尺寸数组的算术操作
两个等尺寸数组的算术操作实际上是逐元素操作。
import numpy
arr = numpy.arange(1,7).reshape(2,3)
print(arr)
'''
[[1 2 3]
[4 5 6]]
'''
print(arr + arr)
'''
[[ 2 4 6]
[ 8 10 12]]
'''
print(arr / arr)
'''
[[1. 1. 1.]
[1. 1. 1.]]
'''
print(arr - arr)
'''
[[0 0 0]
[0 0 0]]
'''
print(arr > arr) # 使用比较运算符,对应位置进行比较,返回布尔值
2.7.3 广播机制
广播机制规则:如果两个数组的后缘维度(trailing dimension,即从末尾开始算起的维度)的轴长度相符或其中一方的长度为1,则认为它们是广播兼容的。广播会在缺失和(或)长度为1的维度上进行。
(1) 与列数相同并且只有1行的数组之间进行运算
在其丢失的轴上进行,也就是0轴(行)广播
import numpy
arr1 = numpy.arange(12).reshape(4,3)
print(arr1)
'''
[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]
[ 9 10 11]]
'''
arr2 = numpy.arange(3).reshape(1,3)
print(arr2) # [0 1 2]
# (4,3) - (1,3) = (4,3)
arr = arr1 - arr2
print(arr.shape,'\n',arr)
'''
(4, 3)
[[0 0 0]
[3 3 3]
[6 6 6]
[9 9 9]]
'''
(2)与行数相同并且只有1列的数组之间进行运算
在其长度为1轴上进行,此处也就是1轴广播
import numpy
arr1 = numpy.arange(12).reshape(4,3)
print(arr1)
'''
[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]
[ 9 10 11]]
'''
arr2 = numpy.arange(4).reshape(4,1)
print(arr2)
'''
[[0]
[1]
[2]
[3]]
'''
# (4,3) - (4,1) = (4,3)
arr = arr1 - arr2
print(arr.shape,'\n',arr)
'''
(4, 3)
[[0 1 2]
[2 3 4]
[4 5 6]
[6 7 8]]
'''
注意:维度完全不一致,则无法广播
2.8 数组拼接
numpy.vstack()
:垂直(vertical)拼接,以行方向进行堆叠numpy.hstack()
:水平(horizontal)拼接,以列方向进行堆叠
import numpy as np
t1 = np.arange(12).reshape(2,6)
t2 = np.arange(12,24).reshape(2,6)
# 垂直拼接
t3 = np.vstack((t1,t2))
# 水平拼接
h3 = np.hstack((t1,t2)
注意:水平拼接需要对应行数一致,垂直拼接需要对应列数一致。
2.9 切片
2.9.1 一维索引与切片
同Python中内建的数据类型list一值
- 索引默认从0开始
- 切片左闭右开
- 步长为整数
注意:截取子数组,然后改变子数组的值,原数组的值也会改变,这是因为数组的截取是浅拷贝,要区别于列表的切片。
2.9.2 二维索引与切片
- 方式一:二维数组递归方式获取
- 方式二:二维数组逗号分隔获取(行,列):
(1)取单行 ---- arr[x1,:]
(2)取连续行 ---- arr[x1:x2,:]
(3)取分开行 ---- arr[[x1,x2],:]
(4)取子矩阵 ---- arr[x1:x2,y1:y2]
(5)取点 ---- arr[[x1,x2],[y1,y2]]
import numpy as np
arr = np.arange(24).reshape(4,6)
print(arr)
'''
[[ 0 1 2 3 4 5]
[ 6 7 8 9 10 11]
[12 13 14 15 16 17]
[18 19 20 21 22 23]]
'''
# 取第2行
arr[1,:]
# 取2到3连续行
arr[1:3,:]
# 取第2行与第4行
arr[1:4:2,:] # 或者arr[1::2,:]
# 用神奇索引取第2行与第4行
arr[[1,3],:]
# 取子数组[[8 9],[14,15]]
arr[1:3,2:4]
# 用神奇索引(也叫花式索引)取8和15 (注意:使用神奇索引并不是取子数组,而是分开执行)
arr[[1,2],[2,3]]
注意:
- 当有需要修改数组当中的值时,我们可以直接取值进行赋值。
- 当数组与标量进行比较时,数组当中每个元素与标量进行比较,返回bool值,与此同时,该比较也可以作为索引,被称为布尔索引。比如arr[arr>20],取出arr数组中元素大于20的值,然后组成新的一维数组。
import numpy as np
arr = np.arange(24).reshape(4,6)
print(arr)
'''
[[ 0 1 2 3 4 5]
[ 6 7 8 9 10 11]
[12 13 14 15 16 17]
[18 19 20 21 22 23]]
'''
# 将第二列的值全部修改为0
arr[:,1] = 0 # 先取值再赋值
print(arr > 10) # 每个元素返回bool值,满足条件为True,反之为False
# 取值,即把为True的值取出来
arr[arr > 10]
# 筛选出 >10 并且 <20 的值
arr[(arr>10) & (arr<20)] # 注意有多个运算符时,优先级会紊乱,所以要括号进行限定
2.10 数组的条件逻辑操作
numpy.where()
函数是三元表达式 x if condition else y 的向量化版本。
numpy.where(condition,[x,y])
# 当满足条件,执行x,否则执行y
import numpy
arr = numpy.arange(16).reshape(4,4)
print(arr)
'''
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]
[12 13 14 15]]
'''
# np.where(arr<10) # 返回元组(满足条件的行索引,满足条件的列索引)
# 将<10的变为0,>=10变为10
arr1 = numpy.where(arr<10,0,10)
print(arr1)
'''
[[ 0 0 0 0]
[ 0 0 0 0]
[ 0 0 10 10]
[10 10 10 10]]
'''
# 将arr<8的变为0,其它的保留不变
arr2 = np.where(arr<8,0,arr) # 注意:x和y也可以传数组
print(arr2)
'''
[[ 0 0 0 0]
[ 0 0 0 0]
[ 8 9 10 11]
[12 13 14 15]]
'''
np.clip(a, a_min, a_max, out=None,\*\*kwargs)
:用于裁剪(限制)数组中的值,等价于但效率要高于 np.minimum(a_max, np.maximum(a, a_min)) ,要求a_min < a_max
参数:(1)a —> 数组;(2)a_min —> 整数,下边界;(3)a_max —> 整数,上边界;(4)out —> 裁剪后输出的结果,要求必须和原数组的形状一致
import numpy as np
arr = numpy.arange(16).reshape(4,4)
print(arr)
'''
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]
[12 13 14 15]]
'''
# 将arr <= 5 替换为5 arr >= 10 的替换为10
arr1 = arr.clip(5,10) # np.clip(x,y)的一般用法:小于x的替换为x,大于y的替换为y
print(arr1)
'''
[[ 5 5 5 5]
[ 5 5 6 7]
[ 8 9 10 10]
[10 10 10 10]]
'''
2.11 Numpy操作本地数据
2.11.1 写入本地数据
numpy.savetxt(fname, X, fmt=’%.18e’, delimiter=’ ‘, newline=’\n’, header=’’,footer=’’, comments=’# ', encoding=None) 该函数不止可以操作txt文件,还可以操作csv文件
参数:
- fname ----> 文件名称,默认保存在本地,可修改路径,比如fname=‘D:\test\test.csv’
- X ----> 保存到text文件的1维或2维数组数据
- fmt=’%.18e’ ----> 写入文件的格式,例如:%d,%.2f,%.18e
- delimiter ----> 分隔符
- newline ----> 换行符
- header ----> 指定表头
- footer ----> 指定结束符
- comments ----> 在“ header”和“ footer”字符串之前添加的字符串,默认标记为 “#”
- encoding ----> 指定编码
实例
import numpy as np
# 随机生成40位同学期中与期末的成绩(40行2列的数据)
scores = np.random.randint(0,100,size=(40,2))
print(scores)
# 将scores数据保存到csv文件中(以逗号分隔、数据类型为整数型,表头为期中 期末,注释comments为空)
np.savetxt(fname=r'D:\scores.csv',X=scores,delimiter=',',fmt='%d',header='期中,期末',comments='')
2.11.2 读取本地数据
np.loadtxt(fname, dtype=float, comments=’#’, delimiter=None,converters=None, skiprows=0, usecols=None, unpack=False,ndmin=0, encoding=‘bytes’, max_rows=None)
参数:
- fname ----> 文件路径及文件名称
- dtype ----> 数据类型,默认为浮点型
- comments ----> 如果行的开头为#,就会跳过该行
- delimiter ----> 分隔符
- converters ----> 对列数据进行预处理,需要传递字典,比如{0:func}表示第0列进行func函数预处理。
- skipprows ----> 跳过行,整数,默认为0
- usecols ----> 整数或序列,表示读取哪一列或哪几列,比如usecols=(1,4,5)表示读取第二列、第五列、第六列。
- unpack ----> 如果为True,则对返回的数组进行转置,以便可以使用``x,y,z = loadtxt(…)’'解压缩参数。 与结构化数据类型一起使用时,将为每个字段返回数组。 默认值为False。
- ndmin ----> 返回的数组将至少具有“ ndmin”维。 否则,将挤压一维轴。
- encoding ----> 指定编码
- max_rows ----> 设置显示的最大行数
实例1
import numpy as np
'''
注意:
(1)如果不使用分隔符,Python会认为数据是一个整体的字符串,如"11,12",一旦指定分隔符之后,数据会变为 11 12;
(2)表头为字符串,不能转化为浮点型,需要跳过第一行,另外skiprows=1表示跳过第一行,而不是索引为1的那一行
'''
scores = np.loadtxt(fname=r'D:\scores.csv',delimiter=',',skiprows=1)
print(scores)
# 上面跳过第一行的原因为:表头是字符串,表数据是整数型,而array数组要求元素必须是同一种数据类型。如果不进行跳过,也可以将表数据全部转化为字符串
scores_s = np.loadtxt(fname=r'D:\python报班学习\Python数据分析-Amy\scores.csv',delimiter=',',dtype='object')
实例2: 合并云南白药.csv与五浪液.csv的数据
import numpy as np
# 使用代码将两个表的数据(表头一致)进行拼接的步骤:1、读取数据,只保留第一个表的表头,另一个表的表头跳过;2、垂直合并表格。
# 读取云南白药数据
ynby = np.loadtxt(fname=r'G:\云南白药.csv',delimiter=',',dtype='object')
# 读取五粮液数据
wly = np.loadtxt(fname=r'G:\五粮液.csv',delimiter=',',dtype='object',skiprows=1)
# 查看两个数据表的形状
print(ynby.shape,'\n',wly.shape) # 都为(156,11)
# 进行垂直拼接
concat = np.vstack((ynby,wly))
print(concat.shape)
# 保存数据
np.savetxt(fname=r'D:\concat.csv',X=concat,delimiter=',',fmt='%s')
2.12 NaN与inf
2.12.1 nan与inf介绍
- nan:not a number 表示不是一个数字,属于浮点型
- inf:np.inf表示正无穷,-np.inf表示负无穷,属于浮点型
2.12.2 NaN和inf的特点
- nan与inf都是float类型
import numpy as np
print(type(np.nan)) # <class 'float'>
print(type(np.inf)) # <class 'float'>
- 两个nan是不相等的:该特性可以用来判断nan的个数,其中(1)np.count_nonzero()函数用于计算非零的个数;(2)np.isnan()函数用于判断数组元素是否是NaN,返回为bool值。
import numpy as np
print(np.nan == np.nan) # False
# 使用np.nan的特点判断nan的个数
arr = np.arange(6).reshape(2,3)
arr[1,2] = np.nan
# 方式一:通过np.nan != np.nan 以及布尔索引,再通过长度获取
print(len(arr[arr != arr]))
# 方式二:True == 1 False == 0 ,所以可以计算非0的个数
np.count_nonzero(arr != arr)
# 方式三:用np.isnan()函数
np.count_nonzero(np.isnan(arr))
- np.nan与任何数值计算都是nan。
import numpy as np
print(np.arange([2,np.nan,3]) + 100) # arr([102,nan,103])
由于np.nan与任何值计算都是nan,所以在运算时会带来较大的误差。一般我们都会将其处理掉。
2.12.3 如何处理nan
直接删除缺失值所在行,但是当数据量较小时,这样处理会有影响整体的数据。更优雅的做法(nan数量不多的情况下),是当求和时,将np.nan处理为0;当求均值时,将np.nan处理为非nan的均值。
实例
import numpy as np
b = np.arange(9).reshape(3,3)
# 将1和5替换为np.nan
b[(0,1),(1,2)] = np.nan
# 获取含有nan的索引
# print(np.where(np.isnan(b))) # 返回的是元组(行和列)
row_index = np.where(np.isnan(b))[0]) # 获取含有nan的行索引
# 将含有nan的行删除
np.delete(b,row_index,axis=0)
练习:将 exam.csv 文件的数据读取,并且将成绩为空的值替换为非空数据的均值。
import numpy as np
# 读取数据,注意:1、ndarray为同质性数据;2、由于有缺失(有空格),所以需指定类型为字符串
exam_data = np.loadtxt(fname=r'G:\exam.csv',skiprows=1,delimiter=',',dtype=np.str)
# 取出空字符串的数据,赋值为np.nan
# 注意:这里不能直接将空字符串赋值为np.nan,这是因为字符串类型赋值为np.nan会转为na,而na无法转为浮点型,所以一般先赋值为0,然后再转化为需要的类型
exam_data[data_data == ' '] = 0
# 将数据转为float类型
exam_data = exam_data.astype(dtype=np.float)
# 取出为0的数据,赋值为np.nan
exam_data[exam_data == 0] = np.nan
# 将np.nan赋值为对应的列的均值
for exam_cols in range(exam_data.shape[1]):
# 取出每一列的数据
exam_col = exam_data[:,exam_cols]
# 取出非nan的值
non_nan_data = exam_col[exam_col == exam_col]
# 求非nan的均值,然后赋值给nan
exam_col[np.isnan(exam_col)] = non_nan_data.mean()
# 进行四舍五入
exam_col = np.round(exam_col)
2.13 numpy random模块
np.random为我们提供了许多获取随机数的函数,其实是Python内置random模块进行的封装。
参考网址:https://numpy.org/doc/stable/reference/random/legacy.html#numpy.random.RandomState
- np.random.seed:用于指定随机数生成时所用算法开始的整数值,如果使用相同的seed()值,则每次生成的随机数都相同,如果不设置这个值,则系统根据时间来自己选择这个值,此时每次生成的随机数因时间差异而不同。一般没有特殊要求不用设置。以下代码:
import numpy as np
# 随机种子,加上seed之后,每次执行的结果都是一样的
np.random.seed(1)
print(np.random.rand())
- np.random.rand:生成一个值为[0,1)之间的数组,形状由参数指定,如果没有参数,那么将返回一个随机值。示例代码如下:
print(np.random.rand()) # 生成一个0~1之间的随机数
print(np.random.rand(2,3)) # 生成2行3列在0~1的随机数组
- np.random.randn:生成均值(μ)为0,标准差(σ)为1的标准正态分布的值。示例代码如下:
import numpy as np
data = np.random.randn(2,3) # 生成一个2行3列的数组,数组中的值都满足标准正太分布
- np.random.randint:生成指定范围内的随机整数,并且可以通过size参数指定维度。示例代码如下:
import numpy as np
data = np.random.randint(low=0,high=10,size=(3,5)) #生成值在0-10之间,3行5列的数组
- np.random.choice:从一个列表或者数组中,随机进行采样。或者是从指定的区间中进行采样,采样个数可以通过参数指定。注意:np.random模块没有sample()函数,该函数是内置的random模块的函数。
import numpy as np
data = np.arange(100)
res = np.random.choice(data,size=(2,3)) #从data中随机采样,生成2行3列的数组
- np.random.shuffle:把原来数组的元素的位置打乱。注意:np.random.shuffle没有返回值,改变的是原数组。
import numpy as np
a = np.arange(10)
np.random.shuffle(a) # 将a的元素的位置都会进行随机更换
print(a)
- np.random.normal(loc=0.0,scale=1.0,size=None):默认生成均值loc为0,标准差scale为1的服从正态分布的数组,均值和标准差都可以重新设置
import numpy as np
# 生成一个均值为5,标准差为10的3行4列数组
np.random.normal(loc=5,scale=10,size=(3,4)
2.14 Numpy常用函数
2.14.1 聚合函数
方法 | 描述 |
---|---|
sum | 沿着轴向计算所有元素的累和,0长度的数组,累和为0 |
mean | 数学平均,0长度的数组平均值为NaN |
max,min | 最大值和最小值 |
argmax,argmin | 最大值和最小值的位置 |
std,var | 标准差和方差 |
ptp | 极值(最大值减去最小值) |
cumsum | 从0开始元素累积和,返回一个数组 |
median | 中值 |
import numpy as np
a1 = np.arange(12).reshape(3,4)
np.sum(a1) # 全部相加
np.sum(a1,axis=0) # 计算0轴(每一列)的累和,返回一个整型数组
np.sum(a1,axis=1) # 计算1轴(每一行)的累和,返回一个数组
np.mean(a1) # 求整个数组的均值
np.mean(a1,axis=0) # 计算0轴(每一列)的均值,默认返回一个浮点型数组
# 其他函数类似
# 方差:样本数据与均值之间的偏离程度,方差越小,说明偏离均值越小,数据越稳定
np.var(a1)
# 标准差:方差的算术平方根(有正负之分),体现数据的波动性,标准差越大,波动越大
np.std()
print(np.cumsum(a1)) # 返回[ 0 1 3 6 10 15 21 28 36 45 55 66]
a2 = a1.astype('float') # 由于nan是float类型,所以先转换类型
a2[2,2] = np.nan # 将数组的其中一个值变为nan
print(np.sum(a2)) # 结果为nan,这是因为nan与任何值计算都是nan
# 需求:跳过nan进行求和,可以用np.nansum()函数
np.nansum(a2)
计算数组时,numpy的聚合函数要比Python内置的聚合函数效率高
import numpy as np
import time
a1 = np.arange(10000000)
start1 = time.time()
sum(a1)
end1 = time.time()
print(end1-start1) # 1.8280293941497803
start2 = time.time()
np.sum(a1)
end2 = time.time()
print(end2-start2) # 0.010016202926635742
2.14.2 一元函数
函数 | 描述 |
---|---|
np.abs | 绝对值 |
np.sqrt | 开根 |
np.square | 平方 |
np.exp | 计算指数(e^x) |
np.log,np.log10,np.log2,np.log1p | 求以e为底,以10为低,以2为低,以(1+x)为底的对数 |
np.sign | 标签化 |
np.sort | 排序,默认axis=1,即默认按1轴从小到大进行排序 |
np.linspace | 平分,比如np.linspace(0,10,15)表示将0~10的数平均分成15份,返回一个数组 |
np.ceil | 向上取整,比如5.1会变成6,-6.9会变成-6 |
np.floor | 向下取整,比如5.9会变成5,-6.1会变成-7 |
np.rint,np.round | 四舍六入五成偶 |
np.modf | 将整数和小数分隔开来形成两个数组 |
np.isnan | 判断是否是nan |
np.isinf | 判断是否是inf |
np.cos,np.cosh,np.sin,np.sinh,np.tan,np.tanh | 三角函数 |
import numpy as np
# 生成-10到10之间服从均匀分布的3行5列的随机数组
arr = np.random.uniform(-10,10,size=(3,5))
print(arr)
# 求绝对值
arr1 = np.abs(arr)
# 计算指数e^x
np.exp(0) # e^0=1
np.exp(1) # e^1=e
# 以e为底的对数,经常用来做数据的收敛
np.log(np.exp(1)) # 计算结果为1
np.log(0) # 结果为0.0
# 标签化,大于0的为1,小于0的为-1
print(np.sign(arr))
'''
[[ 1. -1. -1. 1. -1.]
[-1. -1. 1. 1. 1.]
[ 1. 1. -1. -1. -1.]]
'''
np.rint(4.4) # 四舍,结果为4
np.rint(4.5) # 五舍,结果为4
np.rint(4.6) # 六入,结果为5
np.rint(5.5) # 五成双,结果为6
# 三角函数
np.sin(np.pi/2) # 结果为1
np.sin(np.pi) # 结果为1.2246467991473532e-16,按道理来说,应该为0,但由于是float64,精度不够,所以无线接近于0,而不是0
2.14.3 二元函数
函数有两个参数
函数 | 描述 |
---|---|
np.add | 加法运算(即1+1=2),相当于+ |
np.subtract | 减法运算(即3-2=1),相当于- |
np.negative | 负数运算(即-2),相当于加个负号 |
np.multiply | 乘法运算(即23=6),相当于 |
np.divide | 除法运算(即3/2=1.5),相当于/ |
np.floor_divide | 取整运算,相当于// |
np.mod | 取余运算,相当于% |
greater,greater_equal,less,less_equal,equal,not_equal | >,>=,<,<=,=,!=的函数表达式 |
logical_and | &的函数表达式 |
logical_or | \的函数表达式 |
import numpy as np
arr = np.random.uniform(-10,10,size=(3,5))
np.add(arr,arr) # 加法运算
# 取出arr中 >0 并且 <5 的数
# 方式一:
arr[(arr<5) & (arr>0)]
# 方式二:
arr[np.logical_and(arr>0,arr<5)]
2.14.4 布尔函数
- np.all():只要该布尔数组里面有一个False,那么该函数就返回False
- np.any():只要该布尔数组里面有一个True,那么该函数就返回True
import numpy as np
arr = np.random.uniform(-10,10,size=(3,5))
np.all((arr>0)&(arr<5)) # 结果为False
np.any((arr>0)&(arr<5)) # 结果为True
2.15 数组的线性代数运算
线性代数(例如矩阵乘法、矩阵分解、行列式以及其他数学函数)是任何数据分析库的重要组成部分。Numpy也提供这样的能力。
- np.dot(arr,arr.T):矩阵的乘法运算
import numpy as np
arr = np.arange(4)
# 矩阵乘法
np.dot(arr,arr.T)
numpy.linalg封装了一组标准的矩阵分解运算以及诸如逆运算、行列式等功能。
函数 | 描述 |
---|---|
det | 计算矩阵行列式 |
eig | 计算方阵的特征值和特征向量 |
inv | 计算方阵的逆 |
qr | 计算QR分解 |
svd | 计算奇异值分解(SVD) |
solve | 解线性方程 |
lstsq | 计算Ax=b的最小二乘解 |
需求1:利用inv函数,求解矩阵的逆矩阵(注意:矩阵可变,首先必须是方阵)
from numpy.linalg import inv
arr_lg = np.array([0,1,2],[1,0,3],[4,-3,8])
# 求方阵的逆
print(inv(arr_lg))
# 检验:矩阵*矩阵的逆 = 单位矩阵
print(np.dot(arr_lg,inv(arr_lg))
需求2:
# 求解如下方程组的解:
# x - 2y + z = 0
# 2y - 8z = 8
# -4x + 5y + 9z = -9
from numpy.linalg import solve
# 创建系数矩阵
A = np.array([0,-2,1],[0,2,-8],[-4,5,9])
B = np.array([0,8,-9])
# 解方程组
print(solve(A,B) # 结果为array([29,16,3])
2.16 综合练习
GB_video_data_numbers.csv
US_video_data_numbers.csv
以上为英国与美国Youtube数据,其每列对应的是:点击,喜欢,不喜欢,评论
练习1:结合Matplotlib绘制各自的评论数量的图形,体现其评论数主要分布在哪个区间。
练习2:绘制图形,分析英国的Youtube中视频的评论数与喜欢数的关系。
练习3:当希望将两个国家的数据拼接一起来研究分析:
(1)拼接全为0的数组标识为英国
(2)拼接全为1的数组标识为美国
(3)将两个国家的数据拼接
实现方式一:
import numpy as np
import matplotlib
from matplotlib import pyplot
font = {
'family':'SimHei',
'weight':'bold',
'size':12
}
matplotlib.rc("font", **font)
# 读取数据
gb_data = np.loadtxt(fname=r'D:\GB_video_data_numbers.csv',delimiter=',',dtype=np.int)
us_data = np.loadtxt(fname=r'D:\US_video_data_numbers.csv',delimiter=',',dtype=np.int)
print('英国Youtube数据',gb_data.shape,'\n',gb_data,'\n','美国Youtube数据',us_data.shape,'\n',us_data)
'''实现练习1和练习2的要求'''
# 截取需要的数据,喜欢数量在第二列、评论数量在第四列
gb_like = gb_data[:,1]
gb_comment = gb_data[:,3]
us_comment = us_data[:,3]
# 查看评论数的最大值、最小值、数值个数,以及小于均值的数值个数
print('英国评论数量最大值:{a};英国评论数量最小值:{b};英国评论数量值个数:{c}'.format(a=np.max(gb_comment),b=np.min(gb_comment),c=len(gb_comment)))
print('美国评论数量最大值:{a};美国评论数量最小值:{b};美国评论数量值个数:{c}'.format(a=np.max(us_comment),b=np.min(us_comment),c=len(us_comment)))
print('英国评论数小于均值的数值个数:{}'.format(len(gb_comment[gb_comment < np.mean(gb_comment)])))
print('美国评论数小于均值的数值个数:{}'.format(len(us_comment[us_comment < np.mean(us_comment)])))
'''
1、从上面的输出结果初步判断可能存在离群值,因此考虑使用箱型图体现其评论数主要分布在哪个区间;
2、用散点图体现评论数与喜欢数的相关关系。
'''
# 创建画布
fig = pyplot.figure(figsize=(8,6))
# 多图布局
gs = fig.add_gridspec(2,2)
ax1 = fig.add_subplot(gs[0,0])
ax2 = fig.add_subplot(gs[0,1])
ax3 = fig.add_subplot(gs[1,:])
# 自动调整子图的间距
fig.tight_layout()
# 绘制图形
ax1.boxplot(x=gb_comment,notch=True,sym='^',widths=0.1,meanline=True,showmeans=True,autorange=True)
ax2.boxplot(x=us_comment,notch=True,sym='*',widths=0.1,meanline=True,showmeans=True,autorange=True)
ax3.scatter(x=gb_comment,y=gb_like)
# 设置散点图 x轴 y轴的标签
ax3.set_xlabel('英国评论数')
ax3.set_ylabel('英国喜欢数')
# 设置标题
ax1.set_title(label='英国评论数箱型图')
ax2.set_title(label='美国评论数箱型图')
ax3.set_title(label='英国评论数与喜欢数的散点图')
pyplot.show()
'''实现练习3的要求'''
# 创建元素全为0、长度与gb_data行数相同的数组
arr_0 = np.zeros(shape=(gb_data.shape[0],1),dtype=np.int)
# 将创建好的全0数组与gb_data进行水平拼接
gb_data_new = np.hstack((gb_data,arr_0))
# 创建元素全为1、长度与us_data行数相同的数组
arr_1 = np.ones(shape=(us_data.shape[0],1),dtype=np.int)
# 将创建好的全1数组与us_data进行水平拼接
us_data_new = np.hstack((us_data,arr_1))
# 垂直拼接两个国家的数据
concat_data = np.vstack((gb_data_new,us_data_new))
# 检查数据
print(concat_data.shape,'\n',concat_data)