【笔记】初识NumPy

标准python中,list可以保存任意对象。所以list里实际保存的是对象的指针。例如我们需要保存一个list [1,2,3],那么实际上,就需要有3个指针和3个整数对象。浪费了内存和计算时间。

使用NumPy让科学计算更高效

为什么要使用NumPy的数组结构而不是python标准库的list?

因为python的list的元素在系统内存中是分散存储的。就是上面所提到的指针,指针指向的才是具体元素存储的位置。
而NumPys数组存储在一个均匀连续的内存块中。这样在遍历所有元素的时候,就不需要像list一样再去内存地址中挨个进行查找,从而节省了计算资源。

另外在内存访问模式中,缓存会直接把字节块从 RAM 加载到 CPU 寄存器中。因为数据连续的存储在内存中,NumPy 直接利用现代 CPU 的矢量化指令计算,加载寄存器中的多个连续浮点数。另外 NumPy 中的矩阵计算可以采用多线程的方式,充分利用多核 CPU 计算资源,大大提升了计算效率。

提升python效率的技巧

  • 避免采用隐式拷贝,而是采用就地操作的方式。
    比如我要把一个数x变为原来的2倍,应该写成:x*=2, 而不是y=x*2。这样速度能快两倍甚至更多。

NumPy入门

ndarray对象(N-dimensional array object)

ndarray实际上是多维数组的含义。

  1. 在NumPy数组中,维数成为秩(rank),一维数组的秩为1,二维数组的秩为2,以此类推。
  2. 在NumPy中,每一个线性的数组称为一个轴(axes),其实秩就是描述轴的数量。

创建数组

代码:

1
2
3
4
5
6
7
8
9
10
11
import numpy as np
a = np.array([1,2,3])
b = np.array([[1,2,3], [4,5,6], [7,8,9]])
# 通过下标访问数组元素
b[1,1] = 10
# 通过shape属性获取数组的大小
print(a.shape)
print(b.shape)
# 通过dtype获取元素的属性
print(a.dtype)
print(b)

执行结果:

1
2
3
4
5
6
7
8
(3,) # 数组a的大小
(3, 3) # 数组b的大小
int64 # 数组元素的属性

# 数组b
[[ 1 2 3]
[ 4 10 6]
[ 7 8 9]]

结构数组

代码:

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 numpy as np
persontype = np.dtype(
{
'names':['name', 'age', 'chinese', 'math', 'english'],
'formats':['S32', 'i', 'i', 'i', 'f']
}
)

peoples = np.array(
[
('ZhangFei', 32, 75, 100, 90),
('GuanYu', 24, 85, 96, 88.5),
('ZhaoYun', 28, 85, 92, 96.5),
('HuangZhong', 29, 65, 85, 100)
],
dtype=persontype
)

age = peoples[:]['age']
chinese = peoples[:]['chinese']
math = peoples[:]['math']
english = peoples[:]['english']

print(age)
print(chinese)
print(math)
print(english)

print(np.mean(age))
print(np.mean(chinese))
print(np.mean(math))
print(np.mean(english))

执行结果:

1
2
3
4
5
6
7
8
[32 24 28 29] # age 
[75 85 85 65] # chinese
[100 96 92 85] # math
[ 90. 88.5 96.5 100. ] # english
28.25 # average of age
77.5 # average of chinese
93.25 # average of math
93.75 # average of english

ufunc(universal function object)

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import numpy as np
# arange() 类似内置函数 range(),通过指定初始值,终值,步长来创建等差数列d额一维数组,默认是不包括终值的。
x1 = np.arange(1, 10, 2)
print(x1)
# linspace 是 linear space 的缩写,代表线性等分向量的含义。linspace() 通过指定初始值、终值、元素个数来创建等差数列的一维数组,默认是包括终值的。
x2 = np.linspace(1, 9, 5)
print(x2)
print(np.add(x1, x2))
print(np.subtract(x1, x2))
print(np.multiply(x1, x2))
print(np.divide(x1, x2))
print(np.power(x1, x2))
print(np.remainder(x1, x2))
print(np.mod(x1, x2))

执行结果:

1
2
[1 3 5 7 9]
[1. 3. 5. 7. 9.]

算数运算

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import numpy as np
x1 = np.arange(1, 10, 2)
x2 = np.linspace(1, 9, 5)
# 数组元素相加
print(np.add(x1, x2))
# 数组元素相减
print(np.subtract(x1, x2))
# 数组元素相乘
print(np.multiply(x1, x2))
# 数组元素相除
print(np.divide(x1, x2))
# 数组元素求n次方
print(np.power(x1, x2))
# 数组元素求n取余数
print(np.remainder(x1, x2))
# 数组元素求n取余数
print(np.mod(x1, x2))

执行结果:

1
2
3
4
5
6
7
8
[ 2.  6. 10. 14. 18.]
[0. 0. 0. 0. 0.]
[ 1. 9. 25. 49. 81.]
[1. 1. 1. 1. 1.]
[1.00000000e+00 2.70000000e+01 3.12500000e+03 8.23543000e+05
3.87420489e+08]
[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]

统计函数

计算数组 / 矩阵中的最大值函数 amax(),最小值函数 amin()

代码:

1
2
3
4
5
6
7
8
import numpy as np
a = np.array([[1,2,3],[4,5,6],[7,8,9]])
print(np.amin(a))
print(np.amin(a,0))
print(np.amin(a,1))
print(np.amax(a))
print(np.amax(a,0))
print(np.amax(a,1))

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

amin() 用于计算数组中的元素沿指定轴的最小值。对于一个二维数组 a,amin(a) 指的是数组中全部元素的最小值,amin(a,0) 是延着 axis=0 轴的最小值,axis=0 轴是把元素看成了 [1,2,3], [4,5,6], [7,8,9] 三个元素,所以最小值为 [1,2,3],amin(a,1) 是延着 axis=1 轴的最小值,axis=1 轴是把元素看成了 [1,4,7], [2,5,8], [3,6,9] 三个元素,所以最小值为 [1,4,7]。同理 amax() 是计算数组中元素沿指定轴的最大值。

统计最大值和最小值之差ptp()

代码:

1
2
3
4
5
import numpy as np
a = np.array([[1,2,3],[4,5,6],[7,8,9]])
print(np.ptp(a))
print(np.ptp(a,0))
print(np.ptp(a,1))

执行结果:

1
2
3
8
[6 6 6]
[2 2 2]

对于相同的数组 a,np.ptp(a) 可以统计数组中最大值与最小值的差,即 9-1=8。同样 ptp(a,0) 统计的是沿着 axis=0 轴的最大值与最小值之差,即 7-1=6(当然 8-2=6,9-3=6,第三行减去第一行的 ptp 差均为 6),ptp(a,1) 统计的是沿着 axis=1 轴的最大值与最小值之差,即 3-1=2(当然 6-4=2, 9-7=2,即第三列与第一列的 ptp 差均为 2)。

统计数组的百分位数(percentile)

代码:

1
2
3
4
5
import numpy as np
a = np.array([[1,2,3], [4,5,6], [7,8,9]])
print(np.percentile(a, 50))
print(np.percentile(a, 50, axis=0))
print(np.percentile(a, 50, axis=1))

执行结果:

1
2
3
5.0
[4. 5. 6.]
[2. 5. 8.]

percentile() 代表着第 p 个百分位数,这里 p 的取值范围是 0-100,如果 p=0,那么就是求最小值,如果 p=50 就是求平均值,如果 p=100 就是求最大值。同样你也可以求得在 axis=0 和 axis=1 两个轴上的 p% 的百分位数。

统计数组中的中位数 median(), 平均数 mean()

1
2
3
4
5
6
7
8
9
10
import numpy as np
a = np.array([[1,2,3], [4,5,6], [7,8,9]])
# 求中位数
print(np.median(a))
print(np.median(a, axis=0))
print(np.median(a, axis=1))
# 求平均数
print(np.mean(a))
print(np.mean(a, axis=0))
print(np.mean(a, axis=1))

执行结果:

1
2
3
4
5
6
5.0
[4. 5. 6.]
[2. 5. 8.]
5.0
[4. 5. 6.]
[2. 5. 8.]

统计数组中的加权平均值

代码:

1
2
3
4
5
6
7
import numpy as np
a = np.array([1,2,3,4])
wts = np.array([1,2,3,4])
# np.average(a)=(1+2+3+4)/4=2.5
print(np.average(a))
# np.average(a,weights=wts)=(1*1+2*2+3*3+4*4)/(1+2+3+4)=3.0
print(np.average(a,weights=wts))

执行结果:

1
2
2.5
3.0

统计数组中的标准差 std()、方差 var()

代码:

1
2
3
4
import numpy as np
a = np.array([1,2,3,4])
print(np.std(a))
print(np.var(a))

执行结果:

1
2
1.118033988749895
1.25

方差的计算是指每个数值与平均值之差的平方求和的平均值,即 mean((x - x.mean())** 2)。标准差是方差的算术平方根。在数学意义上,代表的是一组数据离平均值的分散程度。所以 np.var(a)=1.25, np.std(a)=1.118033988749895。

NumPy排序

那么这些排序算法在 NumPy 中实现起来其实非常简单,一条语句就可以搞定。这里你可以使用 sort 函数,sort(a, axis=-1, kind=‘quicksort’, order=None),默认情况下使用的是快速排序;在 kind 里,可以指定 quicksort、mergesort、heapsort 分别表示快速排序、合并排序、堆排序。同样 axis 默认是 -1,即沿着数组的最后一个轴进行排序,也可以取不同的 axis 轴,或者 axis=None 代表采用扁平化的方式作为一个向量进行排序。另外 order 字段,对于结构化的数组可以指定按照某个字段进行排序。

1
2
3
4
5
a = np.array([[4,3,2],[2,4,1]])
print(np.sort(a))
print(np.sort(a, axis=None))
print(np.sort(a, axis=0))
print(np.sort(a, axis=1))
1
2
3
4
5
6
7
[[2 3 4]
[1 2 4]]
[1 2 2 3 4 4]
[[2 3 1]
[4 4 2]]
[[2 3 4]
[1 2 4]]
加载评论框需要科学上网