当前位置:   article > 正文

二、Numpy库

numpy库

一、Numpy库介绍

Numpy 是一个功能强大的 Python 库,主要由于对多维数组执行计算。Numpy 这个词来源于两个单词 – Numerical 和 Python。Numpy 提过了大量的库函数和操作,可以帮助程序员轻松地进行数值计算。在数据分析和机器学习领域被广泛使用。他有以下几个特点:

  1. Numpy内置了并行运算功能,当系统有多个核心时,作某种计算时,numpy会自动作并行计算。
  2. Numpy底层使用C语言编写,内部解除了GIL(全局解释器锁),其对数组的操作速度不受Python解释器的限制,效率远高于纯Python代码
  3. 有一个强大的N维数组对象Array(一种类似于列表的东西)
  4. 实用的线性代数、傅里叶变换和随机数生成函数

1.1 安装

pip install numpy
  • 1

1.2 Numpy数组和Python列表性能对比

比如我们想要多一个Numpy数组和Python列表中的每一个元素进行求平方。那么代码如下:

# Python列表的方式
import time
t_start = time.time()
a = [x**2 for x in range(100000)]
t_end = time.time()
print(f'list 花费{t_end-t_start}s')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
list 花费0.02654242515563965s
  • 1
# Numpy
import numpy as np
t3 = time.time()
b = np.arange(10000)**2
t4 = time.time()
print(f'Numpy 花费{t4-t3}s')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
Numpy 花费0.0008003711700439453s
  • 1

二、Numpy数组基本操作

2.1 Numpy中的数组

Numpy 中的数组的使用跟 Python 中的列表非常类似。他们之间的区别如下:

  1. 一个列表中可以存储多种数据类型。比如 a = [1,‘a’] 是允许的,而数组只能存储同种数据类型
  2. 数组可以是多维的,当多维数组中所有的数据都是数值类型的时候,相当于线性代数中的矩阵,是可以进行相互间的运算的

2.2 创建数组(np.ndarray对象)

Numpy 经常和数组打交道,因此首先第一步是要学会创建数组。在 Numpy 中的数组的数据类型叫 ndarray。以下是 4 种创建的方式:

  1. 根据Python中的列表生成
import numpy as np
a1 = np.array([1, 2, 3, 4])
print(a1)
print(type(a1))
  • 1
  • 2
  • 3
  • 4
[1 2 3 4]
<class 'numpy.ndarray'>
  • 1
  • 2
  1. 使用 np.arange 生成,np.arange 的用法类似于 Python 中的 range:
import numpy as np
a2 = np.arange(2, 21, 2)
print(a2)
  • 1
  • 2
  • 3
[ 2  4  6  8 10 12 14 16 18 20]
  • 1
  1. 使用 np.random 生成随机数的数组
a1 = np.random.random((2, 2)) # 生成2行2列的随机数的数组,其中元素的值是0-1之间的随机数
a2 = np.random.randint(0, 10, size=(3, 3)) # 元素从0~10之间随机的3行3列的数组
print(a1)
print(a2)
  • 1
  • 2
  • 3
  • 4
[[0.49986588 0.1486504 ]
 [0.08750787 0.02891811]]
[[0 1 9]
 [4 9 3]
 [1 5 6]]
  • 1
  • 2
  • 3
  • 4
  • 5
  1. 使用函数生成特殊的数组
import numpy as np
a1 = np.zeros((2, 2)) # 生成一个所有元素都是0的2行2列的数据
a2 = np.ones((3, 2)) # 生成一个所有元素都是1的3行2列的数组
a3 = np.full((2, 2), 8) # 生成一个所有元素都是8的2行2列的数组
a4 = np.eye(3) # 生成一个斜方形上元素为1,其他元素都为0的3x3的矩阵

print(a1)
print(a2)
print(a3)
print(a4)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
[[0. 0.]
 [0. 0.]]
[[1. 1.]
 [1. 1.]
 [1. 1.]]
[[8 8]
 [8 8]]
[[1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

2.3 ndarray常用属性

  • ndarray.dtype:因为数组中只能存储同一种数据类型,因此可以通过 dtype 获取数组中的元素的数据类型。以下是 ndarray.dtype 的常用的数据类型
数据类型描述唯一标识符
bool用一个字节存储的布尔类型(True或False)‘b’
int8一个字节大小,-128至127‘i’
int16整数,16位整数(-32768~32767)‘i2’
int32整数,32位整数(-2147483648~2147483647)‘i4’
int64整数,64位整数(-9223372036854775808~9223372036854775807)‘i8’
uint8无符号整数,0至255‘u’
uint16无符号整数,0至65535‘u2’
uint32无符号整数,0至2*32-1‘u4’
uint64无符号整数,0至2*64-1‘u8’
float16半精度浮点数:16位,正负号1位,指数5位,精度10位‘f2’
float32半精度浮点数:32位,正负号1位,指数8位,精度23位‘f4’
float64半精度浮点数:64位,正负号1位,指数11位,精度52位‘f8’
complex64复数,分别用两个32位浮点数表示实部与虚部‘c8’
complex128复数,分别用两个64位浮点数表示实部与虚部‘c16’
object_python对象‘o’
string_字符串‘S’
unicode_unicode类型‘U’
# 可以在创建ndarray对象时指定元素数据类型
import numpy as np
a1 = np.array([1,2,3,4,5],dtype=np.int8)
print(a1.dtype)

# 也可以中途修改 dtype
a2 = np.array([1, 2, 3, 4])
print(f'默认元素类型是:{a2.dtype}')
a2 = a2.astype(np.float16) # astype不会修改数组本身,而是将修改后的结果返回
print(a2.dtype)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
int8
默认元素类型是:int64
float16
  • 1
  • 2
  • 3
  • ndarray.size:获取数组中总的元素的个数
import numpy as np
a1 = np.array([[1,2,3],[4,5,6]])
print(a1.size) # 打印的是6,因为总共有6个元素
  • 1
  • 2
  • 3
6
  • 1
  • ndarray.ndim:数组的维度
import numpy as np
a1 = np.array([1,2,3])
print(a1.ndim) # 维度为1
a2 = np.array([[1,2,3],[4,5,6]])
print(a2.ndim) # 维度为2
a3 = np.array([[[1,2,3],[4,5,6],[7,8,9]]])
print(a3.ndim) # 维度为3
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
1
2
3
  • 1
  • 2
  • 3
  • ndarray.shape:数组的维度的元组
import numpy as np
a1 = np.array([1,2,3])
print(a1.shape) # 输出(3,) 意思是一维数组,有3个数据
a2 = np.array([[1,2,3],[4,5,6]])
print(a2.shape) # 输出 (2,3) 意思是二维数组,2行3列 
  • 1
  • 2
  • 3
  • 4
  • 5
(3,)
(2, 3)
  • 1
  • 2

另外,还可以通过 ndarray.reshape 方法来重新修改数组的维度。但注意,reshape并不会修改原来数组本身,而是会将修改后的结果返回。 如果想要直接修改数组本身,那么可以使用 resize 代替 reshape

import numpy as np
a1 = np.arange(12) # 生成一个有12个数据的一维数组
print(a1)

a2 = a1.reshape((3,4)) # 变成了一个2维数组,是3行4列
print(a2)

# 特殊的将多维数组转换为一维数组的方法
# 方法1:a3 = a2.reshape((12,))
# 方法2
a3 = a2.flatten()
print(a3)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
[ 0  1  2  3  4  5  6  7  8  9 10 11]
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]
[ 0  1  2  3  4  5  6  7  8  9 10 11]
  • 1
  • 2
  • 3
  • 4
  • 5
  • ndarray.itemsize:数组中每个元素占的大小,单位是字节
a1 = np.array([1,2,3],dtype=np.int32)
print(a1.itemsize) # 打印4,因为每个字节是8位,32位/8=4个字节
  • 1
  • 2
4
  • 1

2.4 总结

  • 数组的创建与元素类型

    1. 数组中的数据类型都是一致的,要么都是整型,要么都是浮点类型,要么都是字符串类型,不能同时出现多种数据类型。
    2. 创建数组的四种方式:
    • 使用 np.array 来创建
    • 使用 np.arange 来创建一个区间的数组
    • 使用 np.random 模块来创建
    • 使用 np 上面的一些特殊函数来创建
    1. 为什么Numpy的数组中有这么多的数据类型呢?
    • Numpy本身是基于C语言编写的,C语言中本身就有很对数据类型,所以直接引用过来了
    • Numpy为了考虑到处理海量数据的性能,针对不同的数据给不同的数据类型,来节省内存空间,所以有不同的数据类型
    1. Numpy的数组元素有哪些数据类型(将2.3节)
    2. 使用 ndarray.astype 可以修改数组元素的数据类型
    3. 数组一般达到3维就已经很复杂了,不太方便计算了,所以我们一般都会把3维以上的数组转换成2维数组来计算
  • 数组对象的属性

    1. 通过 ndarray.ndim 可以看到数组的维度
    2. 通过 ndarray.shape 可以看到数组的形状,shape是一个元组,里面有几个元素代表是几维数组
    3. 通过 ndarray.reshape 可以修改数组的形状。条件只有一个,就是修改后形状的元素个数必须和原来的个数一致。比如原来是(2,6),那么修改完成后可以变成(3,4),但是不能变成(1,4)。reshape不会修改原来数组的形状,它只会将修改后的结果返回
    4. 通过 ndarray.size 可以看到数组的总元素个数
    5. 通过 ndarray.itemsize 可以看到数组中每个元素所占内存的大小,单位是字节。(1个字节=8位)

三、Numpy索引和切片

3.1 Numpy索引与切片方法

切片结果属于对原数据的部分数据的浅拷贝

  • 获取某行的数据:
import numpy as np
# 1. 如果一维数组
a1 = np.arange(0,29)
print(a1[1]) # 获取下标为1的元素

# 2. 如果二维数组
a1 = np.arange(0,24).reshape((4,6))
print(a1[1]) # 获取下标为1的行数据
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
1
[ 6  7  8  9 10 11]
  • 1
  • 2
  • 连续获取某几行的数据
# 1. 获取连续的几行的数据
a1 = np.arange(0,24).reshape((4,6))
print(a1[0:2]) # 获取0行到1行的数据

# 2. 获取不连续的几行的数据
print(a1[[0,2,3]])

# 3. 使用步长
print(a1[::2])

# 4. 也可以使用负数进行索引
print(a1[[-1,-2]])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
[[ 0  1  2  3  4  5]
 [ 6  7  8  9 10 11]]
[[ 0  1  2  3  4  5]
 [12 13 14 15 16 17]
 [18 19 20 21 22 23]]
[[ 0  1  2  3  4  5]
 [12 13 14 15 16 17]]
[[18 19 20 21 22 23]
 [12 13 14 15 16 17]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 获取某行某列的数据
# 也是通过中括号来索引和切片,在中括号中使用逗号进行分割,逗号前面的是行,
# 逗号后面的是列,如果多维数组中只有一个值,那么这个值就是行
a2 = np.random.randint(0,10,size=(4,6))

# 1.获取连续行、连续列的数据
print(a2[0:2,2:4])
# 等价于 print(a2[[0,1],[2,3]])

# 2.获取不连续行、不连续列的数据
print(a2[[0,1],[2,3]])

# 3.获取某行(某列)
print(a2[1,:])
print(a2[:,2]) # 结果为1维度的数组
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
[[2 3]
 [6 9]]
[2 9]
[9 1 6 9 4 7]
[2 6 9 3]
  • 1
  • 2
  • 3
  • 4
  • 5

3.2 布尔索引

布尔运算也是矢量

a1 = np.arange(0,24).reshape((4, 6))
print(a1<10) # 会返回一个新的数组,这个数组中的值全部都是bool类型

a1[(5<a1)&(a1<10)] # 会把大小小于10的元素提取出来
  • 1
  • 2
  • 3
  • 4
[[ True  True  True  True  True  True]
 [ True  True  True  True False False]
 [False False False False False False]
 [False False False False False False]]





array([6, 7, 8, 9])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

3.3 值的替换

  • 利用索引,也可以做一些值得替换。把满足条件的位置的值替换成其他的值
a1 = np.arange(0, 24).reshape((4,6))
a1[3] = 0 # 将第四行的所有值都替换成0
print(a1)
  • 1
  • 2
  • 3
[[ 0  1  2  3  4  5]
 [ 6  7  8  9 10 11]
 [12 13 14 15 16 17]
 [ 0  0  0  0  0  0]]
  • 1
  • 2
  • 3
  • 4
  • 也可以使用条件索引来实现
a1 = np.arange(0,24).reshape((4,6))
a1[a1<5] = 0 # 将小于5的所有值都替换成0
print(a1)
  • 1
  • 2
  • 3
[[ 0  0  0  0  0  5]
 [ 6  7  8  9 10 11]
 [12 13 14 15 16 17]
 [18 19 20 21 22 23]]
  • 1
  • 2
  • 3
  • 4
  • 还可以使用函数来实现
# where函数
a1 = np.arange(0,24).reshape((4,6))

# 获取满足条件元素的行索引与列索引数组
a2 = np.where(a1<10)
print(a2)

# 把a1中所有小于10的数全部变成1,其余的变成0
a2 = np.where(a1<10, 1, 0)
print(a2)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
(array([0, 0, 0, 0, 0, 0, 1, 1, 1, 1], dtype=int64), array([0, 1, 2, 3, 4, 5, 0, 1, 2, 3], dtype=int64))
[[1 1 1 1 1 1]
 [1 1 1 1 0 0]
 [0 0 0 0 0 0]
 [0 0 0 0 0 0]]
  • 1
  • 2
  • 3
  • 4
  • 5

3.4 总结

  • 数组的索引与切片操作

    1. 如果数组时一维的,那么索引和切片就是跟python的列表是一样的
    2. 如果多维的(这里以二维为例),那么在中括号中,给两个值,两个值是通过逗号分割的,逗号前面的是行,后面的是列。如果中括号中只有一个值,那么就是代表的是行
    3. 如果多维数组(这里以二维为例),那么行的部分和列的部分,都是遵循一维数组的方式,可以使用整型切片,还可以使用中括号的形式,来代表不连续的。比如a[[1,2],[3,4]],那么放返回的是第二、三行,第四、五列的数组
  • 布尔索引与条件过滤

    1. 布尔索引是通过相同数组上的True还是False来进行提取的。提取的条件可以有多个,那么如果有多个,可以使用 & 来代表且,用 | 来代表或,如果有多个条件,那么每个条件要使用圆括号括起来
  • 值的替换

    1. 可以使用索引或者切片来替换
    2. 使用条件索引来替换
    3. 使用where函数来实现替换

四、Numpy数组操作

4.1 数组广播机制

如果两个数组的后缘维度(trailing dimension,即从末尾开始算起的维度)的轴长度相符或其中一方的长度为1,则认为他们是广播兼容的。广播会在缺失和(或)长度为1的维度上进行

  1. shape为(3,8,2)的数组能和(8,3)的数组进行运算吗?
    分析:不能,因为按照广播原则,从后面往前面数,(3,8,2)和(8,3)中的 2 和 3 不相等,所以不能进行运算

  2. shape为(3,8,2)的数组能和(8,1)的数组进行运算吗?
    分析:能,因为按照广播原则,从后面往前面数,(3,8,2)和(8,1)中的 2 和 1 虽然不相等,但是因为有一方的长度为 1,所以能参与运算,所以能参与运算

  3. shape为(3,1,8)的数组能和(8,1)的数组进行运算吗?
    分析:能,因为按照广播原则,从谋面往前面数,(3,1,8)和(8,1)中的 8 和 1 虽然不相等且 1 和 8 不相等,但是因为这两项中有一方的长度为 1 ,所以能参与运算

4.2 数组与数的计算

在Python列表中,想要对列表中所有的元素都加一个数,要么采用 map 函数,要么循环遍历整个列表进行操作。但是 numpy 中的数组可以直接在数组上进行操作

import numpy as np
a1 = np.random.random((3,4))
print(a1)

# 如果想要在a1数组上所有元素都乘以10,那么可以通过以下来的实现
a2 = a1*10
print(a2)
# 也可以使用round让所有元素只保留2位小数
a3 = a2.round(2)
print(a3)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
[[0.45837412 0.77030571 0.92012895 0.65235262]
 [0.58623037 0.36705373 0.99718742 0.26862229]
 [0.73230645 0.20494406 0.95031449 0.60970811]]
[[4.58374119 7.70305709 9.2012895  6.52352618]
 [5.86230374 3.67053725 9.97187419 2.68622292]
 [7.32306454 2.04944061 9.50314489 6.09708111]]
[[4.58 7.7  9.2  6.52]
 [5.86 3.67 9.97 2.69]
 [7.32 2.05 9.5  6.1 ]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

4.2 数组与数组的计算

  • 结构相同的数组之间的运算
a1 = np.arange(0,24).reshape((3,8))
a2 = np.random.randint(1,10,size=(3,8))
print(a1)
print(a2)
# 相加
a3 = a1 + a2
print(a3)

# 相减
a3 = a1 - a2
print(a3)

# 相乘(对应位置元素相乘)
a3 = a1 * a2
print(a3)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
[[ 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 5 4 7 5 9 7 2]
 [3 3 1 8 2 6 5 8]
 [6 3 9 7 3 2 4 2]]
[[ 2  6  6 10  9 14 13  9]
 [11 12 11 19 14 19 19 23]
 [22 20 27 26 23 23 26 25]]
[[-2 -4 -2 -4 -1 -4 -1  5]
 [ 5  6  9  3 10  7  9  7]
 [10 14  9 12 17 19 18 21]]
[[  0   5   8  21  20  45  42  14]
 [ 24  27  10  88  24  78  70 120]
 [ 96  51 162 133  60  42  88  46]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 与行数相同并且只有1列的数组之间的运算(广播机制)
a1 = np.random.randint(10,20,size=(3,8)) # 3行8列
a2 = np.random.randint(1,10,size=(3,1)) # 3行1列
a3 = a1 - a2 # 行数相同,且a2只有1列,能互相运算
print(a1)
print(a2)
print(a3)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
[[11 13 18 19 17 17 16 16]
 [11 15 11 14 13 18 14 17]
 [12 12 18 13 19 16 11 16]]
[[1]
 [6]
 [6]]
[[10 12 17 18 16 16 15 15]
 [ 5  9  5  8  7 12  8 11]
 [ 6  6 12  7 13 10  5 10]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 与列数相同并且只有1行的数组之间的运算(广播机制)
a1 = np.random.randint(10,20,size=(3,8)) # 3行8列
a2 = np.random.randint(1,10,size=(1,8)) # 1行8列
a3 = a1 - a2 # 行数相同,且a2只有1列,能互相运算
print(a1)
print(a2)
print(a3)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
[[11 10 19 10 11 16 19 12]
 [16 18 13 19 17 10 19 18]
 [12 10 15 11 11 12 11 15]]
[[2 3 4 3 5 7 7 9]]
[[ 9  7 15  7  6  9 12  3]
 [14 15  9 16 12  3 12  9]
 [10  7 11  8  6  5  4  6]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

4.3 数组形状操作

可以通过一些函数,非常方便的操作数组的形状

  • reshape 和 resize方法
    两个方法都是用来修改数组形状的,但是有一些不同
  1. reshape 是将数组转换成指定的形状,然后返回转换或的结果,对于原数组的形状是不会发生改变的。调用方式
a1 = np.random.randint(0,10,size=(3,4))
print(a1)
a2 = a1.reshape((2,6)) # 将修改后的结果返回,不会影响原数组本身
print(a1)
print(a2)
  • 1
  • 2
  • 3
  • 4
  • 5
[[5 6 9 5]
 [0 5 2 8]
 [5 4 1 0]]
[[5 6 9 5]
 [0 5 2 8]
 [5 4 1 0]]
[[5 6 9 5 0 5]
 [2 8 5 4 1 0]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  1. resize 是将数组转换成指定的形状,会直接修改数组本身。并不会放回任何值。调用方式:
a1 = np.random.randint(0,10,size=(3,4))
print(a1)
a1.resize((2,6)) # a1 本身发生了改变
print(a1)
  • 1
  • 2
  • 3
  • 4
[[1 0 8 9]
 [4 4 9 9]
 [1 9 9 1]]
[[1 0 8 9 4 4]
 [9 9 1 9 9 1]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • flatten 和 ravel方法
    两个方法都是将多维数组转换成为一维数组,但是有以下不同
  1. flatten 是将数组转换为一维数组后,然后将这个拷贝返回回去,所以后续对这个返回值进行修改,不会影响之前的数组
  2. ravel 是将数组转换为一维数组后,将这个视图(可以理解为引用)返回回去,所以后续对这个返回值进行修改会影响之前的数组
x = np.array([[1,2],[3,4]])
print('初始的x:',x)
x.flatten()[1] = 100 # 此时的x[0]的位置元素还是1
print('使用flatten后修改元素值的x:',x)
y = x.ravel() # y并不是x,此时x的结构未变
y[1] = 100 # 此时的x[0]的位置元素还是1
print('使用ravel后修改元素值的x:',x)
print('使用ravel后修改元素值的y:',y)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
初始的x: [[1 2]
 [3 4]]
使用flatten后修改元素值的x: [[1 2]
 [3 4]]
使用ravel后修改元素值的x: [[  1 100]
 [  3   4]]
使用ravel后修改元素值的y: [  1 100   3   4]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

4.4 不同数组的组合

  • vstack:将数组按垂直方向进行叠加。数组的列数必须相同才能叠加
a1 = np.random.randint(0,10,size=(3,5))
a2 = np.random.randint(0,10,size=(1,5))
a3 = np.vstack((a1,a2))
print(a1,'\n')
print(a2,'\n')
print(a3)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
[[1 5 7 5 0]
 [1 3 5 1 7]
 [4 8 5 5 2]] 

[[2 0 0 2 3]] 

[[1 5 7 5 0]
 [1 3 5 1 7]
 [4 8 5 5 2]
 [2 0 0 2 3]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • hstack:将数组按水平方向进行叠加。数组的行必须相同才能叠加
a1 = np.random.randint(0,10,size=(3,2))
a2 = np.random.randint(0,10,size=(3,1))
a3 = np.hstack((a1,a2))
print(a1,'\n')
print(a2,'\n')
print(a3)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
[[0 0]
 [0 9]
 [4 2]] 

[[4]
 [9]
 [3]] 

[[0 0 4]
 [0 9 9]
 [4 2 3]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • concatenate(a,b,axis):将两个数组进行叠加,但是具体是按水平方向还是垂直方向。则要看 axis 的参数,如果 axis=0 ,那么代表的事往垂直方向叠加;如果 axis=1,那么代表的是往水平方向上叠加,如果 axis=None,那么会将两个数组组合成一个一维数组。需要注意的是,如果往水平方向上叠加,那么行必须相同,如果是往垂直方向叠加,那么列必须相同
a = np.array([[1,2],[3,4]])
b = np.array([[5,6]])
np.concatenate((a,b),axis=0)
  • 1
  • 2
  • 3
array([[1, 2],
       [3, 4],
       [5, 6]])
  • 1
  • 2
  • 3

4.5 数组的切割

通过 hsplit 和 vsplit 以及 array_split 可以将一个数组进行切割

  • hsplit:按照水平方向进行切割。用于指定分割成几列,可以使用数字来代表分成几部分,也可以使用数组来代表分割的地方
a1 = np.arange(16.0).reshape((4, 4))
print(a1)
print(np.hsplit(a1, 2)) # 分割成两部分
print(np.hsplit(a1,[1, 2])) # 代表在下标为1的地方切一刀,下标为2的地方切一刀,分成三部分
  • 1
  • 2
  • 3
  • 4
[[ 0.  1.  2.  3.]
 [ 4.  5.  6.  7.]
 [ 8.  9. 10. 11.]
 [12. 13. 14. 15.]]
[array([[ 0.,  1.],
       [ 4.,  5.],
       [ 8.,  9.],
       [12., 13.]]), array([[ 2.,  3.],
       [ 6.,  7.],
       [10., 11.],
       [14., 15.]])]
[array([[ 0.],
       [ 4.],
       [ 8.],
       [12.]]), array([[ 1.],
       [ 5.],
       [ 9.],
       [13.]]), array([[ 2.,  3.],
       [ 6.,  7.],
       [10., 11.],
       [14., 15.]])]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • vsplit:按照垂直方向切割。用于指定分割成几行,可以使用数字来代表分成几部分,也可以使用数组来代表分割的地方
a1 = np.arange(16.0).reshape((4, 4))
print(a1)
print(np.vsplit(a1, 2)) # 代表按照行总共分成2个数组
print(np.vsplit(a1,[1,2])) # 代表按照行进行划分,在下标为1的地方和下标为2的地方分割

  • 1
  • 2
  • 3
  • 4
  • 5
[[ 0.  1.  2.  3.]
 [ 4.  5.  6.  7.]
 [ 8.  9. 10. 11.]
 [12. 13. 14. 15.]]
[array([[0., 1., 2., 3.],
       [4., 5., 6., 7.]]), array([[ 8.,  9., 10., 11.],
       [12., 13., 14., 15.]])]
[array([[0., 1., 2., 3.]]), array([[4., 5., 6., 7.]]), array([[ 8.,  9., 10., 11.],
       [12., 13., 14., 15.]])]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • split/array_split(arry,indicate_or_seciont,axis):用于指定切割方式,在切割的时候需要指定是按照行还是按照列,axis=1 代表按照列,axis=0代表按照行
a1 = np.arange(16.0).reshape((4, 4))
np.split(a1,2,axis=0) # 按照垂直方向切割成2部分
# 等价于 np.array_split(a1,2,axis=0)
  • 1
  • 2
  • 3
[array([[0., 1., 2., 3.],
        [4., 5., 6., 7.]]),
 array([[ 8.,  9., 10., 11.],
        [12., 13., 14., 15.]])]
  • 1
  • 2
  • 3
  • 4

4.6 数组(矩阵)转置和轴对换

numpy 中的数组其实就是线性代数中的矩阵。矩阵是可以进行转置的。ndarray 有一个T属性,可以返回这个数组的转置的结果

a1 = np.arange(0,24).reshape((4, 6))
print(a1.shape)
print(a1.T.shape)
  • 1
  • 2
  • 3
(4, 6)
(6, 4)
  • 1
  • 2

另外还有一个方法叫做 transpose,这个方法返回的是一个View,也即修改返回值,会影响到原来数组

a1 = np.arange(0,24).reshape((4, 6))
print(a1.transpose())
  • 1
  • 2
[[ 0  6 12 18]
 [ 1  7 13 19]
 [ 2  8 14 20]
 [ 3  9 15 21]
 [ 4 10 16 22]
 [ 5 11 17 23]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

为什么要进行矩阵转置呢,有时候在做一些计算的时候需要用到。比如做矩阵的内积的时候。就必须将矩阵进行转置后再乘以之前的矩阵

a1 = np.arange(0,24).reshape((4, 6))
a2 = a1.T
print(a1.dot(a2))
  • 1
  • 2
  • 3
[[  55  145  235  325]
 [ 145  451  757 1063]
 [ 235  757 1279 1801]
 [ 325 1063 1801 2539]]
  • 1
  • 2
  • 3
  • 4

4.7 总结

  • 数组的广播机制运算

    1. 数组和数字直接进行运算是没有问题的
    2. 两个shape相同的数组是可以进行运算的
    3. 如果两个shape不同的数组,想要进行运算,那么需要看它们是否满足广播原则
  • 数组的形状改变

    1. reshaperesize 都是重新定义形状的。但是reshape不会修改数组本身,而是将修改后的结果返回回去,而resize是直接修改数组本身
    2. flattenravel 都是用来将数组变成一维数组的,并且他们都不会对原来的数组形状造成影响,凡是flatten返回的是一个拷贝,所以对flatten的返回值的修改不会影响到原来数组,而ravel返回的是一个 View,那么对返回值的修改会影响到原来数组的值
  • 数组的叠加

    1. hstack代表在水平方向叠加,如果想要叠加成果,那么他们的行必须一致
    2. vstack代表在垂直方向叠加,如果想要叠加成果,那么他们的列必须一致
    3. concatenate可以手动的指定具体在哪个方向上叠加,如果 axis=0 ,那么代表的事往垂直方向叠加;如果 axis=1,那么代表的是往水平方向上叠加,如果 axis=None,那么会将两个数组组合成一个一维数组
  • 数组的切割

    1. hsplit代表在水平方向切割,按列进行切割的。他的切割方式有两种,第一种就是直接指定切割成多少列,第二种就是指定切割的下标值
    2. vsplit代表在垂直方向切割,按行进行切割。它的切割方式和hsplit是一样的
    3. split/array_split是手动的指定axis参数,axis=0表示按行进行切割,axis=1按列进行切割
  • 矩阵的转置

    1. 可以通过 ndarray.T 的形式进行转置
    2. 也可以通过 ndarray.transpose() 进行转置,这个方法返回的是一个 View,所以对返回值上进行修改,会影响到原来的数组。

五、深拷贝和浅拷贝

在操作数组的时候,它们的数据有时候拷贝进一个新的数组,有时候又不是。这经常让初学者感到困惑。下面有三种情况:

5.1 不拷贝

如果只是简单的赋值,那么不会进行拷贝

a = np.arange(12)
b = a # 这个情况不会进行拷贝
print(b is a) # 返回True,说明b和a指向的是同一个物理空间
  • 1
  • 2
  • 3
True
  • 1

5.2 View或者浅拷贝

有些情况,会进行变量的拷贝,但是它们所指向的内存空间都是一样的,那么这种情况叫做浅拷贝,或者叫做 View(视图)

a = np.arange(12)
c = a.view()
print(c is a) # 返回False,说明c和a是两个不同的变量
print(a)
c[0] = 100 # 修改c的数据,a也被修改
print(a)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
False
[ 0  1  2  3  4  5  6  7  8  9 10 11]
[100   1   2   3   4   5   6   7   8   9  10  11]
  • 1
  • 2
  • 3
# 测试torch中的view方法也是浅拷贝
import torch
x = torch.randn(3,4,5)
print(x)
print(x.shape,'\n')
y = x.view(3,-1)
y[0][0] = 1
print(x)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
tensor([[[-1.4582e+00, -1.8481e+00,  2.9551e-01, -1.4263e+00,  6.9132e-01],
         [ 2.5786e+00, -7.9345e-01, -2.1037e+00, -6.2299e-01, -2.0349e+00],
         [-1.1204e+00,  7.8410e-01,  4.2736e-02,  1.1501e+00, -4.5683e-01],
         [ 2.9022e-01,  9.0955e-01, -9.7117e-01,  6.6526e-02, -1.6036e+00]],

        [[ 8.6045e-01, -1.2575e+00, -6.4802e-01, -9.2809e-02, -4.1277e-01],
         [-2.9113e-01,  8.5110e-01, -1.5484e-02, -1.2415e+00,  1.4440e+00],
         [-1.8091e+00, -1.9069e+00,  1.5823e+00, -4.3149e-01, -5.9490e-01],
         [ 1.5120e+00,  3.8810e-01,  5.0874e-01,  2.9571e-01, -3.4822e-01]],

        [[-1.3785e+00, -2.2480e-01, -1.4546e+00, -4.3500e-01,  7.4371e-01],
         [ 2.7077e-02,  6.3148e-01,  1.2705e+00,  2.2875e-01,  3.6703e-01],
         [-8.7489e-02, -4.4425e-01, -1.5119e+00, -6.5937e-02, -1.4610e+00],
         [ 7.8731e-01,  7.8602e-01,  2.1924e-04, -1.6953e+00, -1.1275e+00]]])
torch.Size([3, 4, 5]) 

tensor([[[ 1.0000e+00, -1.8481e+00,  2.9551e-01, -1.4263e+00,  6.9132e-01],
         [ 2.5786e+00, -7.9345e-01, -2.1037e+00, -6.2299e-01, -2.0349e+00],
         [-1.1204e+00,  7.8410e-01,  4.2736e-02,  1.1501e+00, -4.5683e-01],
         [ 2.9022e-01,  9.0955e-01, -9.7117e-01,  6.6526e-02, -1.6036e+00]],

        [[ 8.6045e-01, -1.2575e+00, -6.4802e-01, -9.2809e-02, -4.1277e-01],
         [-2.9113e-01,  8.5110e-01, -1.5484e-02, -1.2415e+00,  1.4440e+00],
         [-1.8091e+00, -1.9069e+00,  1.5823e+00, -4.3149e-01, -5.9490e-01],
         [ 1.5120e+00,  3.8810e-01,  5.0874e-01,  2.9571e-01, -3.4822e-01]],

        [[-1.3785e+00, -2.2480e-01, -1.4546e+00, -4.3500e-01,  7.4371e-01],
         [ 2.7077e-02,  6.3148e-01,  1.2705e+00,  2.2875e-01,  3.6703e-01],
         [-8.7489e-02, -4.4425e-01, -1.5119e+00, -6.5937e-02, -1.4610e+00],
         [ 7.8731e-01,  7.8602e-01,  2.1924e-04, -1.6953e+00, -1.1275e+00]]])
  • 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
# Python中的浅拷贝
import copy
a = ['a1', 1, 3]
b = a.copy()
b[0] = 'a2'
print(a)
# 此处由于元素是不可变数据类型,修改b[0]指向一个新的存储空间
print(b)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
['a1', 1, 3]
['a2', 1, 3]
  • 1
  • 2

5.3 深拷贝

将之前数据完完整整的拷贝一份放到另一块内存空间中,这样就是两个完全不同的值了

a = np.arange(12)
d = a.copy()
print(d is a) # 返回False,说明d和a是两个不同的变量
d[0] = 100
print(a[0]) # 打印0,说明 d 和 a 指向的内存空间完全不同
  • 1
  • 2
  • 3
  • 4
  • 5
False
0
  • 1
  • 2

5.4 总结

  • 在数组操作中分成三种情况:
  1. 不拷贝:直接赋值,那么栈区没有拷贝,只是用同一个栈区定义了不同的名称
  2. 浅拷贝:只拷贝了栈区,栈区指定的堆区并没有拷贝
  3. 深拷贝:栈区和堆区都拷贝了

六、文件操作

6.1 保存CSV文件

有时候我们有了一个数组,需要保存到文件中,那么可以使用 np.savetxt 来实现

"""
np.savetxt(frame, array, fmt='%.18e', delimiter=None)
* frame:文件、字符或生成器,可以是.gz或.bz2的压缩文件
* array:存入文件的数组
* fmt:写入文件的元素数值精度,例如:%d、%.2f、%.18e
* delimiter:分割字符串,默认是任何空格(csv的分割符是,)
"""
a = np.arange(100).reshape((5,20))
np.savetxt('a.csv',a,fmt='%d',delimiter=',')

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

6.2 读取文件

有时候我们的数据是需要从文件中读取出来的,那么可以使用 np.loadtxt 来实现

"""
np.loadtxt(frame, dtype=np.float, delimiter=None, unpack=False)
* frame:文件、字符串或产生器,可以是.gz或.bz2的压缩文件
* dtype:数据类型,可选
* delimiter:分割字符串,默认是任何空格
* skiprows:跳过前面x行
* usecols:读取指定的列,用元组组合
* unpack:如果True,读取出来的数组时转置后的
"""
a = np.loadtxt('a.csv',dtype=int,delimiter=',')
print(a)
print(a.shape)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
[[ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19]
 [20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39]
 [40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59]
 [60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79]
 [80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99]]
(5, 20)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

6.3 Numpy独有的存储解决方案

numpy 中还有一种独有的存储解决方案。文件名是以 .npy 或者 .npz 结尾的。该方法区别于CSV存储,它可以存储多维数组。以下是存储和加载的函数

  1. 存储:np.save(frame,array)np.savez(frame,array)。其中,前者函数的扩展名是 .npy,后者的扩展名是 .npz,后者是经过压缩的。
  2. 加载:np.load(frame)
c = np.random.randint(0,10,size=(2,3)) 
# frame不需要加后缀名(.npy)
np.save('c',c)
  • 1
  • 2
  • 3
c = np.load('c.npy')
print(c)
  • 1
  • 2
[[2 9 1]
 [9 0 3]]
  • 1
  • 2

6.4 总结

  1. np.savetxtnp.loadtxt一般用来操作csv文件,它可以设置header,但是不能存储3维以上的数组
  2. np.savenp.load一般用来存储非文本类型的文件,他不可以设置header,但是可以存储3维以上的数组
  3. 如果想专门的操作csv文件,其实还有另外一个模块叫做csv,这个模块时python内置的

七、NAN和INF值处理

首先我们要知道这两个英文单词代表的什么意思:

  1. NAN:Not A Number,不是一个数组的意思,但是它是属于浮点类型,所以想要进行数据操作的时候需要注意他的类型
  2. INF:Infinity,代表的是无穷大的意思,也是属于浮点类型np.inf 表示正无穷大,-np,inf 表示负无穷大,一般在出现除数为 0 的时候为无穷大。比如 2/0

7.1 NAN的一些特点

  1. NAN的NAN不相等。比如 np.NAN != np.NAN 这个条件时成立的
  2. NAN和任何值作运算,结果都是NAN
    有些时候,特别是从文件中读取数据的时候,经常会出现一些缺失值。缺失值的出现会影响数据的处理。因此我们在做数据分析之前,先要对缺失值进行一些处理。处理的方式有多种,需要根据实际情况来做。一般有两种处理方式:删除缺失值,用其他值进行填充

7.2 删除缺失值

有时候,我们想要将数组中的 NAN 删除,那么我们可以换一种思路,就是只提取不为 NAN 的值

# 1. 删除所有的NAN的值,因为删除了值后数组将不知道该怎么变化,所以会被变成一维数组
data = np.random.randint(0,10,size=(3,5)).astype(np.float16)
data[0,1] = np.nan
print(data)
# 此时的data会没有nan,并且返回一个1维数组
data = data[~np.isnan(data)]
print(data)

# 2. 删除NAN所在的行
data = np.random.randint(0,10,size=(3,5)).astype(np.float16)
# 将第(0,1)和(1,2)两个值设置为NAN
data[[0,1],[1,2]] = np.nan
print(data)
# 获取哪些行有NAN
lines = np.where(np.isnan(data))[0]
print(lines)
# 使用delete方法删除指定的行,axis=0表示删除行,lines表示删除的行号
data1 = np.delete(data,lines,axis=0)
print(data1)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
[[ 8. nan  6.  3.  6.]
 [ 1.  6.  6.  3.  3.]
 [ 2.  7.  6.  7.  0.]]
[8. 6. 3. 6. 1. 6. 6. 3. 3. 2. 7. 6. 7. 0.]
[[ 9. nan  1.  3.  5.]
 [ 6.  7. nan  3.  3.]
 [ 9.  5.  4.  5.  6.]]
[0 1]
[[9. 5. 4. 5. 6.]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

7.3 用其他值进行替代

有些时候我们不想直接删掉,比如有一个成绩单,分别是数学和英语,但是因为某个人在某个科目上没有成绩,那么此时就会出现NAN的情况,这时候就不能直接删掉了,就可以使用某些值进行替代

数学英语
5989
9032
7845
34NAN
NAN56
2356

如果想要求每门成绩的总分,以及每门成绩的平均分,那么就可以采用某些值替代。比如求总分,那么就可以把NAN替换成0,如果想要求平均分,那么就把NAN替换成其他值得平均值

scores = np.loadtxt('nan_scores.csv',skiprows=1,delimiter=',',dtype=str)
print(scores)
scores[scores == ''] = np.NAN
print(scores)
scores1 = scores.astype(np.float16)
print(scores1)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
[['59.0' '89.0']
 ['90.0' '32.0']
 ['78.0' '45.0']
 ['34.0' '']
 ['' '56.0']
 ['23.0' '56.0']]
[['59.0' '89.0']
 ['90.0' '32.0']
 ['78.0' '45.0']
 ['34.0' 'nan']
 ['nan' '56.0']
 ['23.0' '56.0']]
[[59. 89.]
 [90. 32.]
 [78. 45.]
 [34. nan]
 [nan 56.]
 [23. 56.]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
# 求总分
scores1[np.isnan(scores1)] = 0
scores1.sum(axis=1)
  • 1
  • 2
  • 3
array([148., 122., 123.,  34.,  56.,  79.], dtype=float16)
  • 1
# 求科目的平均分
scores2 = scores.copy()
scores2 = scores2.astype(np.float16)
for x in range(scores2.shape[1]):
    col = scores2[:,x]
    average = col[~np.isnan(col)].mean()
    col[np.isnan(col)] = average
print(scores2)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
[[59.  89. ]
 [90.  32. ]
 [78.  45. ]
 [34.  55.6]
 [56.8 56. ]
 [23.  56. ]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

7.4 总结

  1. NAN:Not A Number的简写,不是一个数字,但是它属于浮点型型
  2. INF:无穷大,除数为0的那个情况下会出现INF
  3. NAN和所有的值进行计算结果都是等于NAN
  4. NAN != NAN
  5. 可以通过np.isnan来判断某个值是不是NAN
  6. 处理值得时候,可以通过删除NAN的形式进行处理,也可以通过值的替换进行处理
  7. np.delete比较特殊,它通过axis=0来代替行,而其他大部分函数是通过axis=1来代替行

八、np.random模块

np.random 为我们提供了许多获取随机数的函数

8.1 np.random.seed

用于指定随机数生成时所用算法开始的整数值,如果使用相同的 seed() 值,则每次生成的随机数都相同,如果不设置这个值,则系统根据时间来自己选择这个值,此时每次生成的随机数因时间差异而不同。一般没有特殊要求不用设置

# 多次运行此处代码生成的随机数都一样
np.random.seed(1)
print(np.random.rand()) # 打印[0,1]的随机数
print(np.random.rand()) # 打印其他值,因为随机数种子只对下一次随机数的产生有影响
  • 1
  • 2
  • 3
  • 4
0.417022004702574
0.7203244934421581
  • 1
  • 2

8.2 np.random.rand

生成一个值为 [0,1) 之间的数组,形状有参数指定,如果没有参数,那么将返回一个随机值

data1 = np.random.rand(2,3,4) # 生成2块3行4列的数组,值从0~1之间
data2 = np.random.rand() # 生成一个0-1之间的随机数
print(data1,'\n')
print(data2)
  • 1
  • 2
  • 3
  • 4
[[[0.34776586 0.7508121  0.72599799 0.88330609]
  [0.62367221 0.75094243 0.34889834 0.26992789]
  [0.89588622 0.42809119 0.96484005 0.6634415 ]]

 [[0.62169572 0.11474597 0.94948926 0.44991213]
  [0.57838961 0.4081368  0.23702698 0.90337952]
  [0.57367949 0.00287033 0.61714491 0.3266449 ]]] 

0.5270581022576093
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

8.3 np.random.randn

生成均值( μ \mu μ)为0,标准差( σ \sigma σ)为1的标准正态分布的值

data = np.random.randn(2, 3) # 生成一个2行3列的数组,数组中的值都满足标准正态分布
print(data)
  • 1
  • 2
[[-0.30620401  0.82797464  0.23009474]
 [ 0.76201118 -0.22232814 -0.20075807]]
  • 1
  • 2

8.4 np.random.randint

生成指定范围内的随机数,并且可以通过 size 参数指定维度

data1 = np.random.randint(10,size=(3,5)) # 生成值在0-10之间,3行5列的数组
data2 = np.random.randint(1,20,size=(3,6)) # 生成值在1-20之间,3行6列的数组
print(data1,'\n')
print(data2)
  • 1
  • 2
  • 3
  • 4
[[5 1 2 4 3]
 [0 6 0 7 2]
 [8 3 0 8 4]] 

[[14 19 16 10  1 13]
 [18 11 13 16 15 12]
 [ 8  4 16 15  3  5]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

8.5 np.random.choice

从一个列表或者数组中,随机进行采样。或者是从指定的区间中进行采样,采样个数可以通过参数指定

data = [4, 65, 6, 3, 5, 73, 23, 5, 6]
result1 = np.random.choice(data, size=(2, 3)) # 从data中随机采样,生成2行3列的数组 
result2 = np.random.choice(data, 3) # 从data中随机采样3个数据形成一个一维数据
result3 = np.random.choice(10, 3) # 从0-10之间随机取3个值
print(result1,'\n')
print(result2,'\n')
print(result3)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
[[ 4  6  6]
 [ 6 65  5]] 

[ 4  5 65] 

[7 3 1]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

8.6 np.random.shuffle

把原来数组的元素的位置打乱

a = np.arange(10)
print(a,'\n')
np.random.shuffle(a) # 将a的元素的位置都会进行随机打乱
print(a,'\n')

a = np.arange(10).reshape((2,5))
print(a,'\n')
np.random.shuffle(a) # 将a的元素的位置都会进行随机打乱
print(a)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
[0 1 2 3 4 5 6 7 8 9] 

[1 2 8 0 3 9 5 6 7 4] 

[[0 1 2 3 4]
 [5 6 7 8 9]] 

[[5 6 7 8 9]
 [0 1 2 3 4]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

九、Axis理解

之前的课程中,为了方便大家理解,我们说 axis = 0代表的是行,axis = 1代表的是列。但其实不是这么简单理解的
简单来说,最外面的括号代表着 axis=0,依次往里的括号对应的axis 的技术就依次加1
在这里插入图片描述

最外面的括号就是 axis=0,里面两个子括号 axis=1
操作方式:如果指定轴进行相关的操作,那么他会使用轴下的每一直接子元素的第0个,第1个,第2个…分别进行相关的操作

x=np.array([[0,1],[2,3]])
# 1. 求 x 数组在 axis=0 和 axis=1两种情况下的和
print(x.sum(axis=0),'\n')
print(x.sum(axis=1))
  • 1
  • 2
  • 3
  • 4
[2 4] 

[1 5]
  • 1
  • 2
  • 3

总结

  1. 最外面的括号代表着 axis=0,依次往里的括号对应的axis 的技术就依次加1
  2. 操作方式:如果指定轴进行相关的操作,那么他会使用轴下的每一直接子元素的第0个,第1个,第2个…分别进行相关的操作
  3. np.delete,是直接删除指定轴下的第几个子元素

十、通用函数

10.1 一元函数

函数描述
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将数组中的值标签化,大于0的变成1,等于0的变成0,小于0的变成-1
np.ceil向上取整
np.floor向下取整
np.rint,np.round返回四舍五入后的值
np.modf将整数和小数分隔开来形成两个数组
np.isnan判断是否是nan
np.isinf判断是否是inf
np.cos,np.cosh,np.sin,np.sinh,np.tan,np.tanh三角函数
np.arccos,np.arcsin,np.arctan反三角函数

10.2 二元函数

函数描述
np.add加法运算(即1+1=2),相当于+
np.subtract减法运算(即3-2=1),相当于-
np.negative负数运算(即-2),相当于加个负号
np.multiply乘法运算(即2*3=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|的函数表达式

10.3 聚合函数

函数名称NAN安全版本描述
np.sumnp.nansum计算元素的和
np.prodnp.nanprod计算元素的积
np.meannp.nanmean计算元素的平均值
np.stdnp.nanstd计算元素的标准差
np.varnp.nancar计算元素的方差
np.minnp.nanmin计算元素的最小值
np.maxnp.nanmax计算元素的最大值
np.mediannp.nanmedian计算元素的中位数
np.argminnp.nanargmin找出最小值的索引
np.argmaxnp.nanargmax找出最大值的索引
a = np.arange(24).reshape((3,8))
print(a)
b = np.argmax(a,axis=1)
print(b)
  • 1
  • 2
  • 3
  • 4
[[ 0  1  2  3  4  5  6  7]
 [ 8  9 10 11 12 13 14 15]
 [16 17 18 19 20 21 22 23]]
[7 7 7]
  • 1
  • 2
  • 3
  • 4

10.4 布尔数组的函数

函数名称描述
np.any验证任何一个元素是否为真
np.all验证所有元素是否为真

比如想看下数组中是不是所有元素都是0,那么可以通过以下代码来实现

a = np.zeros((3,5))
np.all(a==0)
# 或者
(a==0).all()
  • 1
  • 2
  • 3
  • 4
True
  • 1

比如我们想看数组中是否有等于0的数,那么可以通过以下代码来实现

np.any(a==0)
# 或者
(a==0).any()
  • 1
  • 2
  • 3
True
  • 1

10.5 排序

  1. np.sort:指定轴进行排序。默认是使用数组的最后一个轴进行排序
a = np.random.randint(0,10,size=(3,5))
print(a,'\n')
b = np.sort(a) # 按照行进行排序,因为最后一个轴是1,那么就是将最里面的元素进行排序
c = np.sort(a,axis=0) # 按照列进行排序,因为指定了axis=0
print(a,'\n')
print(b,'\n')
print(c,'\n')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
[[4 8 2 1 6]
 [3 8 9 7 0]
 [5 2 2 8 5]] 

[[4 8 2 1 6]
 [3 8 9 7 0]
 [5 2 2 8 5]] 

[[1 2 4 6 8]
 [0 3 7 8 9]
 [2 2 5 5 8]] 

[[3 2 2 1 0]
 [4 8 2 7 5]
 [5 8 9 8 6]] 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

  1. np.argsort:返回排序后的下标值
a = np.random.randint(0,10,size=(3,5))
print(a)
np.argsort(a) # 默认使用最后的一个轴来进行排序
  • 1
  • 2
  • 3
[[0 5 9 8 6]
 [6 0 4 7 3]
 [0 1 6 0 6]]





array([[0, 1, 4, 3, 2],
       [1, 4, 2, 0, 3],
       [0, 3, 1, 2, 4]], dtype=int64)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  1. 降序排序:np.sort默认会采用升序排序。如果我们想采用降序排序。那么可以采用以下方案来实现
a = np.random.randint(0,10,size=(3,5))
print(a,'\n')

# 1. 使用负号
print(-np.sort(-a),'\n')

# 2. 使用sort和argsort以及take(结果有问题)
indexes = np.argsort(-a) # 排序后的结果就是降序
print(np.take(a,indexes),'\n') # 从a中根据下标提取相应的元素
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
[[4 6 6 2 2]
 [4 1 2 3 9]
 [3 6 7 0 3]] 

[[6 6 4 2 2]
 [9 4 3 2 1]
 [7 6 3 3 0]] 

[[6 6 4 2 2]
 [2 4 2 6 6]
 [6 6 4 2 2]] 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

10.6 其他函数补充

  1. np.apply_along_axis:沿着某个轴执行指定的函数
a = np.arange(24).reshape((3,8))
print(a)
# 求数组 a 按照行求均值,并且要去掉最大值和最小值
# x为原数组中的各个行向量
np.apply_along_axis(lambda x:x[(x!=x.max()) & (x != x.min())].mean(),axis=1,arr=a)
  • 1
  • 2
  • 3
  • 4
  • 5
[[ 0  1  2  3  4  5  6  7]
 [ 8  9 10 11 12 13 14 15]
 [16 17 18 19 20 21 22 23]]





array([ 3.5, 11.5, 19.5])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  1. np.linspace:用来将指定区间内的值平均分成多少份
# 将0-1分成12份,生成一个数组
np.linspace(0,1,12)
  • 1
  • 2
array([0.        , 0.09090909, 0.18181818, 0.27272727, 0.36363636,
       0.45454545, 0.54545455, 0.63636364, 0.72727273, 0.81818182,
       0.90909091, 1.        ])
  • 1
  • 2
  • 3
  1. np.unique:返回数组中唯一值(即去重后的数组)
a = np.random.randint(0,10,size=(3,8))
print(a)
# 返回数组a中的唯一值,并且会返回每个唯一值出现的次数
np.unique(a,return_counts=True)
  • 1
  • 2
  • 3
  • 4
[[3 6 8 6 5 1 3 2]
 [6 3 6 7 2 8 0 1]
 [8 6 0 0 1 2 7 7]]





(array([0, 1, 2, 3, 5, 6, 7, 8]), array([3, 3, 3, 3, 1, 5, 3, 3], dtype=int64))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/神奇cpp/article/detail/981268
推荐阅读
相关标签
  

闽ICP备14008679号