
Hi,大家好。我是茶桁。
上一节课中,我们学习了matplotlib. 实际上,我们已经进入了数据可视化阶段。
可是在上一节课中,所有的数据都是我们固定写好的,包括两个电影的数据展示的案例(柱状图和直方图),都是我们将数据手动写成了数据列表,然后直接使用。
在我们平时的工作中,不太有那么多的机会使用现成的数据,除非你跟我一样是一个数据产品经理,那倒是会有程序员们会将数据整理好递到你手上。可是即便如此,很多时候我还是需要自己亲手去处理数据,因为我不能为了一次数据验证或者一个什么想法就去惊动程序员为你服务。
更何况,我们现在的课程面向的是人工智能,那么处理数据就成了必须要有的手段,也是家常便饭常有的事。
那么今天,我们就来学习一下Python中科学计算的基础第三方库:「numpy」。
简单介绍一下NumPy

Numpy的全称是:Numerical Python, 是一个开源的Python科学计算库,用于快速处理任意维度的数组。
Numpy支持常见的数组和矩阵操作,对于同样的数值计算任务,使用Numpy比直接使用Python要简洁的多。其中的ndarray对象用于处理多维数组,该对象是一个快速而灵活的大数据容器。
相对于直接使用Python,NumPy具有一下优势:
对于同样的数值计算任务,使用NumPy要比直接编写Python代码便捷得多;
NumPy中的数组的存储效率和输入输出性能均远远优于Python中等价的基本数据结构,且其能够 提升的性能是与数组中的元素成比例的;
NumPy的大部分代码都是用C语言写的,其底层算法在设计时就有着优异的性能,这使得NumPy 比纯Python代码高效得多.
说那么多我们不如直接来一次对比显得更直接一点,让我们来计算100000000个数字的加法运算。
import random
import time
import numpy as np
a = []
for i in range(100000000):
a.append(random.random())
t1 = time.time()
# Python 处理
sum_py = sum(a)
t2 = time.time()
b = np.array(a)
t4 = time.time()
# NumPy 处理
sum_np = np.sum(b)
t5 = time.time()
print(f'Python:{t2-t1}, NumPy:{t5-t4}')
---
Python:1.782839059829712, NumPy:0.2144303321838379
在以上代码中,t2-t1为使用python自带的求和函数消耗的时间,t5-t4为使用numpy求和消耗的时间。我们看到了时间对比,是不是为NumPy效率的提升感到惊讶?实际上,在一些更早一些配置差一点的电脑上,这个差距还会更大。记得我以前曾经做过一样的事情,时间大概为Python: 5s, NumPy:0.5s。差了有10倍左右。那么我们也能看出来了,ndarray的计算速度快了很多,为我们节约了大量时间。
ndarray对象
N维数组对象ndarray可以说是NumPy中最重要的一个特点了,它是一个系列同类型数据的集合,以0下标为开始进行集合中元素的索引。ndarray对象是用于存放同类型元素的多维数组。
让我尝试创建一些一维数组,Numpy创建数组有三种不同的方式,1. 直接传入列表的方式;2. 传入range生成序列;3. 直接使用np.arange()生成数组。让我们一一来实现下:
# 创建数组的多种形式
# 1. 直接传入列表的方式
list1 = [1, 2, 3, 4]
oneArray = np.array(list1)
print()
print(f'oneArray: {oneArray, type(oneArray)}')
t1 = np.array([1, 2, 3, 4])
print(f't1: {t1, type(t1)}')
# 2. 传入range生成序列
t2 = np.array(range(10))
print(f't2: {t2, type(t2)}')
# 3. 使用numpy自带的np.arange()生成数组
t3 = np.arange(0, 10, 2)
print(f't3: {t3, type(t3)}')
---
oneArray: (array([1, 2, 3, 4]), <class 'numpy.ndarray'>)
t1: (array([1, 2, 3, 4]), <class 'numpy.ndarray'>)
t2: (array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]), <class 'numpy.ndarray'>)
t3: (array([0, 2, 4, 6, 8]), <class 'numpy.ndarray'>)
无论用哪种方式,我们可以看到最终打印的类型都是numpy.ndarray。
那以上是一维数组的创建,我们再来看看二维数组:
# 二维数组
list2 = [[1,2],[3,4],[5,6]]
twoArray = np.array(list2)
print(twoArray)
---
[[1 2]
[3 4]
[5 6]]
对于二位数组的理解,我们可以将其看成行跟列。如果是在Excel中,那么1,2就是一行,3,4是一行,5,6是一行。而[1,3,5]就是一列,[2,4,6]也是一列。
那么,我们在看到数据之前怎么知道这组数据的维度呢?可以使用方法ndim, 顺便,我们来学习一下一些常用属性。
# 获取数组的维度
print(twoArray.ndim)
# 获取数据的形状(行、列)
print(twoArray.shape)
# 获取数组的元素个数
print(twoArray.size)
---
2
(3, 2)
6
这样,我们就可以看到数组的一些相关属性,拿来在处理数据前做参考。其中,获取到的shape是一个元组数据,而我们获取到的size可以看成是元组内的数据相乘。
现在我们来看,我们能否在numpy中调整数组的形状呢?来,尝试一下,当前我们有一组二维数组:
[[1,2,3],[4,5,6]]
将其转变为ndarray并赋值给一个变量arr_1, 既然我们前面知道获取数据的形状是用shape, 那我们尝试直接更改它的shape看看是否可行:
arr_1 = np.array([[1,2,3],[4,5,6]])
arr_1.shape = (3, 2)
print(arr_1)
---
[[1 2]
[3 4]
[5 6]]
居然可以。
不过虽然这样能够对数组形状进行修改,不过在NumPy中正确的修改方式应该是使用reshape:
# 返回一个新的数组
arr_1 = arr_1.reshape(arr_1.shape)
print(f'\narr_1:\n{arr_1}')
---
arr_1:
[[1 2]
[3 4]
[5 6]]
这次我们使用了数组第一次修改后的形状,所以整个和之前没有差别,让我们再试试其他的:
# 将多维变成一维数组
arr_2 = arr_1.reshape((arr_1.size), order='F')
print(f'\narr_2:\n{arr_2}')
arr_3 = arr_1.flatten(order='F')
print(f'\narr_3:\n{arr_3}')
---
arr_2:
[1 3 5 2 4 6]
arr_3:
[1 3 5 2 4 6]
后方那个形参order是一个可选参数,是读取元素的索引顺序,有C, F, A三个固定值。C为行有限,F为列有限, A为按数据存储顺序。
如果只是转为一维数组,使用reshape还需要知道数组的元素个数,不如flatten来的方便。这是一个专门用于将数组折叠成一维数组的方法。
让我们来看看reshape转换数组形状的其他几个例子:
# 数组的形状
t = np.arange(24)
print(f't:\n{t}')
print(t.shape)
# 转换成二维
t1 = t.reshape((4,6))
print(f'\nt1:\n{t1}')
print(t1.shape)
# 转换成三维
t2 = t1.reshape((2, 3, 4))
print(f'\nt2:\n{t2}')
print(t2.shape)
---
t:
[ 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,)
t1:
[[ 0 1 2 3 4 5]
[ 6 7 8 9 10 11]
[12 13 14 15 16 17]
[18 19 20 21 22 23]]
(4, 6)
t2:
[[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
[[12 13 14 15]
[16 17 18 19][9, 12, 88, 14, 25] <class 'list'>
[20 21 22 23]]]
(2, 3, 4)
在数据转为了ndarray之后,方便了我们在NumPy中进行计算等操作,不过很多时候,最终我们还是要转回Python内的List。如果需要进行转换,直接使用tolist()就可以了。
# 将数组转为list
a = np.array([9, 12, 88, 14, 25])
items = a.tolist()
print(items, type(items))
---
[9, 12, 88, 14, 25] <class 'list'>
NumPy的数据类型
在之前的Python基础课程中,我用几节课给大家讲了一遍Python中的数据类型。同样的,在NumPy中也有一些不同的数据类型,大多数时候,他们都可以进行转换。
来直接上代码看几个例子:
arr = np.array([1, 2, 3, 4, 5], dtype=np.int16)
这样,我们就在Numpy中生成了一个int16类型的数组。我们来看看这组数据的元素长度和类型:
# 返回数组中每个元素的直接单位长度
print(arr.itemsize)
# 获取数据类型
print(arr.dtype)
---
2
int16
当然,就如我们之前说的,数据的类型之间是可以进行转换的,转换也十分方便,直接使用类型方法就可以了。
# 调整数据类型
arr_2 = arr.astype(np.int64)
print(arr_2.dtype)
---
int64
这里给大家多讲一个小技巧,让我们看看如何生成随机小数:
# 使用Python语法,保留两位
print(round(random.random(), 2))
# Numpy生成数组
arr_3 = np.round([random.random() for i in range(10)],2)
print(arr_3)
---
0.47
[0.97 0.81 0.1 0.23 0.66 0.98 0.06 0.44 0.33 0.14]
既然我们知道了dtype是numpy中的数据类型,那么对于数组来说,都有哪些类型呢?这里给大家一个表:

数组的计算
numpy的广播机制在运算过程中,加减乘除的值被广播到所有的元素上面:
t1 = np.arange(24).reshape((6,4))
print('原数组:\n', t1)
print('加2:\n', t1+2)
print('乘2:\n', t1*2)
print('除2:\n', t1/2)
---
原数组:
[[ 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:
[[ 2 3 4 5]
[ 6 7 8 9]
[10 11 12 13]
[14 15 16 17]
[18 19 20 21]
[22 23 24 25]]
乘2:
[[ 0 2 4 6]
[ 8 10 12 14]
[16 18 20 22]
[24 26 28 30]
[32 34 36 38]
[40 42 44 46]]
除2:
[[ 0. 0.5 1. 1.5]
[ 2. 2.5 3. 3.5]
[ 4. 4.5 5. 5.5]
[ 6. 6.5 7. 7.5]
[ 8. 8.5 9. 9.5]
[10. 10.5 11. 11.5]]
除了和数字进行计算之外,同种形状的数组之间也是可以进行计算(对应位置进行计算操作)。
t1 = np.arange(24).reshape(6,4)
t2 = np.arange(100, 124).reshape(6,4)
print('相加:\n',t1+t2)
print('相乘:\n',t1*t2)
---
相加:
[[100 102 104 106]
[108 110 112 114]
[116 118 120 122]
[124 126 128 130]
[132 134 136 138]
[140 142 144 146]]
相乘:
[[ 0 101 204 309]
[ 416 525 636 749]
[ 864 981 1100 1221]
[1344 1469 1596 1725]
[1856 1989 2124 2261]
[2400 2541 2684 2829]]
那么,不同形状的多维数组能否可以计算呢?来,一起试试看:
t1 = np.arange(24).reshape((4,6))
t2 = np.arange(18).reshape((3,6))
print(t1)
print(t2)
print(t1-t2)
---
[[ 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 1 2 3 4 5]
[ 6 7 8 9 10 11]
[12 13 14 15 16 17]]
ValueError: operands could not be broadcast together with shapes (4,6) (3,6)
做相加操作的时候报错了,所以我们平时在处理数据的时候一定要多注意这种情况发生。不同形状的多维数组是不能进行计算的。
我们继续来往后做实验看看,行数或者列数相同的一维数组和多维数组可以进行计算吗?
先看看行形状相同的情况:
t1 = np.arange(24).reshape(4,6)
t2 = np.arange(0, 6)
print(t1 - t2)
---
[[ 0 0 0 0 0 0]
[ 6 6 6 6 6 6]
[12 12 12 12 12 12]
[18 18 18 18 18 18]]
看到结果我们就明白了,多维数组中的每一行数组都分别和一维数组中的数据进行操作,也就是会与每一行数组的对应位相操作。
那么列形状相同是否也是相同情况?
t1 = np.arange(24).reshape(4,6)
t2 = np.arange(4).reshape(4,1)
print(t1-t2)
---
[[ 0 1 2 3 4 5]
[ 5 6 7 8 9 10]
[10 11 12 13 14 15]
[15 16 17 18 19 20]]
就跟预料的一样,每一列的数组的对应位都进行了操作。
数组的轴
在理解了一维数组和二维数组之后,我们来看看数组中的轴。
什么是轴?在Numpy中,我们可以讲轴理解为方向,使用0,1,2数字来表示,对于一个一维数组,只有一个0轴。二维数组(shape(2,2))呢,就有0轴和1轴,那同理向上推断,三维数组(shape(2,2,3))会有0,1,2三个轴。
那么我们到底为什么要了解和学习轴呢?有了轴的概念之后,我们计算讲会更加方便,比如计算一个二维数组的平均值,必须制定是计算哪个方向上面的数字的平均值。
下图中,我列出了不同数组的轴,看着图相信会好理解很多:
一维数组:

二维数组:

三维数组:

在去重的方法中,我们都有两个变量去接收返回值,也就是方法返回了两个值,其中u这个变量就代表的是改变之后的数组,而indices则是重新组成的数组中的数值在原数组中第一次出现的下标位置,又或者是出现次数。以方法不同,但是u肯定是去重后的重新组成的数组。
我标识了一下原数组中的下标,并且用颜色区分了一下。在重新组成的去重的数组中,可以看到已经没有重复的数值了,然后对比原数组,每一个不重复的数值也都还在。而默认的排序方式就是从小到大的排序。所以重新组成的数值中的排序和原数组中不太一样。
然后我们再来看最下面「去重下标」部分,虽然这也是一个数组,但是实际上它是一个完全由下标位置组成的数组,然后我们来看去重数组,我们是拿第一次出现的下标来算。其中2对应原数组就的下标就是1, 5第一次出现是0的位置,6是2, 7是4,9这是最后一位,也就是下标9。
所以对重新组成的数组稍微一分析,就能明白每个方法的返回值说代表的意义。
相信到这里,大家应该也都能理解了。
接下来,我们要来看看重头戏,NumPy的计算。
NumPy的计算
NumPy原本就是一个科学计算的第三方库,所以计算能力应该算是NumPy里的重点。在原始方法中,有许许多多的方法用于数据的计算。包括求最大值,求最小值,平均值,累计和等等。除了计算整体之外,还支持在不同轴上进行计算。下面我们来看看,NumPy到底为我们都提供了哪些好用的方法,最开始,还是让我们来生成一组新的数据:
score = np.array([[80,88],[82,81],[75,81]])
score
---
array([[80, 88],
[82, 81],
[75, 81]])
获取所有数据最大值
result = np.max(score)
print(result)
---
88
获取某一个轴上的数据最大值
result = np.max(score,axis=0)
print(result)
---
[82 88]
获取最小值
result = np.min(score)
print(result)
---
75
获取某一个轴上的数据最小值
result = np.min(score,axis=1)
print(result)
---
[80 81 75]
数据的比较
第一个参数中的每一个数与第二个参数比较返回大的
result = np.maximum([-2, -1, 0, 1, 2], 0)
print(result)
---
[0 0 0 1 2]
第一个参数中的每一个数与第二个参数比较返回小的
result = np.minimum([-2, -1, 0, 1, 2], 0)
print(result)
---
[-2 -1 0 0 0]
以上两组代码中,当方法内接受两个参数,当然也可以大小一致; 当第二个参数只是一个单独的值时,其实是用到了维度的广播机制。如果第二个参数是一个同样长度的数组,会分别比较不同位置。
result = np.maximum([-2, -1, 0, 1, 2], [1,2,3,4,5])
print(result)
---
[1 2 3 4 5]
咱们在这里稍微讲一下NumPy中的广播机制,其实是有点晦涩难懂。大家尝试理解一下看看。
广播机制是Numpy让两个不同shape的数组能够做一些运算,需要对参与运算的两个数组做一些处理或者说扩展,最终是参与运算的两个数组的shape一样,然后广播计算(对应位置数据进行某运算)得到结果。
以我们做数据比较的第一组为例,当我们去对比第一个参数和第二个参数返回大的,这个时候我们给到的两个参数分别是:
[-2,-1,0,1,2]和0,但是由于广播机制的存在,在NumPy中实际上是这么处理的,第一个参数还是[-2,-1,0,1,2], 而第二个参数实际上是[0,0,0,0,0]。
获取所有数据的平均值
result = np.mean(score)
print(result)
---
81.16666666666667
获取某一行或一列的平均值
result = np.mean(score, axis=0)
print(result)
---
[79. 83.33333333]
返回给定axis上的累计和
t1 = np.array([[1,2,3],[4,5,6]])
print(t1)
print(t1.cumsum(0))
---
[[1 2 3]
[4 5 6]]
[[1 2 3]
[5 7 9]]
在来看值为axis=1时:
print(t1.cumsum(1))
---
[[ 1 3 6]
[ 4 9 15]]
我们可以这样认为,当cumsum(0),也就是axis=0时,是这样计算的:
[1 2 3] --------> |1 |2 |3 |
[5 7 9] --------> |5=1+4 |7=2+5 |9=3+6|
当cumsum(1),也就是axis=1时,是这样计算的
[ 1 3 6] ------> |1 |3=2+1 |6=3+2+1 |
[ 4 9 15] ------> |4 |9=4+5 |15=4+5+6 |
argmin求最小值索引
result = np.argmin(score, axis=0)
print(result)
---
[2 1]
我们看score这组数据中,75是第一列最小值,81是第二列最小值。当然,第二列的数据中有两个81。现在让我们讲数组的值换一下看看
score[2,1] = 64
result = np.argmin(score, axis=0)
print(result)
---
[2 2]
很显然,我们第二列的最小值变成了64, 在那一列中,它的坐标为2。
求每一列的标准差
标准差是一组数据平均值分散程度的一种度量。一个较大的标准差,代表大部分数值和其平均值之间差异较大;一个较小的标准差,代表这些数据较接近平均值反应出数据的波动稳定情况,越大表示波动越大,越不稳定。
result = np.std(score, axis=0)
print(result)
---
[ 2.94392029 10.07747764]
从结果中可以看出来,我们第二列的波动较大。其原因正是因为我把[2,2]这个位置的值替换成了64。和其他值拉大了差距。
极值
result = np.ptp(score,axis=None)
print(result)
---
24
计算极值,其实也就是最大值和最小值的差。在score中,最大值为88,最小值为64。
除了我们目前测试的这些方法之外,NumPy中还有很多其他方法。比如,计算反差的var, 协方差cov,平均值average, 中位数median。在这里,我们就不一一测试了,方法都比较简单,拿来直接用的那种。
我将通用函数和解释在这里列一个表:

数组的拼接
有的时候我们需要将两个数据加起来一起研究分析,我们就可以将其进行拼接然后分析。
我们先创建两组数组:
a = np.array([[1,2],[3,4]])
b = np.array([[5,6],[7,8]])
然后我们现在先根据轴连接的数组序列
先沿轴0连接两个数组:
print(np.concatenate((a,b), axis=0))
---
[[1 2]
[3 4]
[5 6]
[7 8]]
再沿轴1连接两个数组:
print(np.concatenate((a,b), axis=1))
---
[[1 2 5 6]
[3 4 7 8]]
根据轴进行堆叠
沿轴0堆叠两个数组:
print(np.stack((a,b), axis=0))
---
[[[1 2]
[3 4]]
[[5 6]
[7 8]]]
沿轴1堆叠两个数组:
print(np.stack((a,b), axis=1))
---
[[[1 2]
[5 6]]
[[3 4]
[7 8]]]
矩阵垂直拼接
v1 = [[0,1,2,3,4,5], [6,7,8,9,10,11]]
v2 = [[12,13,14,15,16,17],[18,19,20,21,22,23]]
result = np.vstack((v1, v2))
print(result)
---
[[ 0 1 2 3 4 5]
[ 6 7 8 9 10 11]
[12 13 14 15 16 17]
[18 19 20 21 22 23]]
矩阵水平拼接
result = np.hstack((v1, v2))
print(result)
---
[[ 0 1 2 3 4 5 12 13 14 15 16 17]
[ 6 7 8 9 10 11 18 19 20 21 22 23]]
数组的分割
将一个数组分割为多个子数组
参数说明:
ary:被分割的数组
indices_or_sections:果是一个整数,就用该数平均切分,如果是一个数组,为沿轴切分的位置(左开右 闭)
axis:沿着哪个维度进行切向,默认为0,横向切分。为1时,纵向切分
arr = np.arange(9).reshape(3,3)
print('将数组分成三个大小相等的子数组:')
b = np.split(arr,3)
print(b)
---
将数组分成三个大小相等的子数组:
[array([[0, 1, 2]]), array([[3, 4, 5]]), array([[6, 7, 8]])]
numpy.hsplit函数用于水平分割数组,通过指定要返回的相同形状的数组数量来拆分原数组。
harr = np.floor(10 * np.random.random((2,6)))
print(f'原array:\n{harr}')
print(f'\n水平分割后:\n{np.hsplit(harr, 3)}')
---
原array:
[[1. 1. 8. 2. 3. 9.]
[3. 1. 6. 6. 5. 6.]]
水平分割后:
[array([[1., 1.],
[3., 1.]]), array([[8., 2.],
[6., 6.]]), array([[3., 9.],
[5., 6.]])]
这里我们说一下floor(), 这个方法会返回数值的下舍整数(舍去小数点求整型)。
numpy.vsplit会沿垂直轴分割
a = np.arange(16).reshape(4,4)
print(f'第一个数组:\n{a}')
print(f'\n垂直分割之后:\n{np.vsplit(a,2)}')
---
第一个数组:
[[ 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]])]
nan和inf
C 语言中表示最大的正整数值是0x7FFFFFFF,最小的负整数是0x80000000。inf 表示无穷大,需要使用 float(‘inf’)函数来转化,那么对应的就有 float('-inf')表示无穷小了。这样你就可以使用任意数来判断和它的关系了。
那什么时候会出现inf呢?比如一个数字除以0,Python中会报错,但是numpy中会是一个inf或者-inf
另外还有 nan,这种写法在pandas中常见,表示缺失的数据,所以一般用nan来表示。任何与其做运算结果都是nan。
a = np.nan
b = np.inf
print(a, type(a))
print(b, type(b))
---
nan <class 'float'>
inf <class 'float'>
判断数组中为nan的个数(注意:float类型的数据才能赋值nan)
t = np.arange(24,dtype=float).reshape(4,6)
可以使用np.count_nonzero()来判断非零的个数
print(np.count_nonzero(t))
---
23
将三行四列的数改成nan
t[3,4] = np.nan
print(t[3,4] != np.nan)
---
True
注意到没有,np.nan != np.nan居然是True。难道我们更改数据失败了?我们打出来看看:
print(t)
---
[[ 0. 1. 2. 3. 4. 5.]
[ 6. 7. 8. 9. 10. 11.]
[12. 13. 14. 15. 16. 17.]
[18. 19. 20. 21. nan 23.]]
没错,t[3,4]确实被改变了,那只能说明np.nan != np.nan是确实存在的。
所以,我们就可以使用这两个结合使用判断nan的个数:
print(np.count_nonzero(t != t))
---
1
我们之前讲过,nan和任何数计算都为nan
print(np.sum(t,axis=0))
---
[36. 40. 44. 48. nan 56.]
接下来,让我们做一个具体的练习,在练习中,我们将处理数组中的nan
# 练习,处理数组中的nan
t = np.arange(24).reshape(4,6).astype('float')
# 将数组中的一部分替换nan
t[1, 3:] = np.nan
print(t)
---
[[ 0. 1. 2. 3. 4. 5.]
[ 6. 7. 8. nan nan nan]
[12. 13. 14. 15. 16. 17.]
[18. 19. 20. 21. 22. 23.]]
现在我们得到了一组包含nan的数组,接着我们来处理这组数据:
# 尝试便利每一列,然后判断每一列是否有`nan`
for i in range(t.shape[1]):
# 获取当前列数据
temp_col = t[:, i]
# 判断当前列的数据中是否含有nan
nan_num = np.count_nonzero(temp_col != temp_col)
# 条件成立说明含有nan
if nan_num != 0:
# 将这一列部位nan的数据拿出来
temp_col_not_nan = temp_col[temp_col == temp_col]
# 将nan替换成这一列的平均值
temp_col[np.isnan(temp_col)] = np.mean(temp_col_not_nan)
print(t)
---
[[ 0. 1. 2. 3. 4. 5.]
[ 6. 7. 8. 13. 14. 15.]
[12. 13. 14. 15. 16. 17.]
[18. 19. 20. 21. 22. 23.]]
这样,我们就处理了这组数据中的nan,至于替换成平均值填补空缺数据,这个是清洗数据的通用做法。
二维数组的转置
针对二维数组的转置,也就是对换数组的维度。说的直白一点,就是行转列,列转行。
这在处理数据的时候,也是我们经常要做的操作:
a = np.arange(12).reshape(3,4)
print (f'原数组:\n{a}')
print (f'\n对换数组:\n{np.transpose(a)}')
---
原数组:
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
对换数组:
[[ 0 4 8]
[ 1 5 9]
[ 2 6 10]
[ 3 7 11]]
让我们再来看一种处理方式,与transpose方法一致:
a = np.arange(12).reshape(3,4)
print (f'原数组:\n{a}')
print (f'\n转置数组:\n{a.T}')
---
原数组:
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
转置数组:
[[ 0 4 8]
[ 1 5 9]
[ 2 6 10]
[ 3 7 11]]
接着我们尝试一个函数用于交换数组的两个轴
t1 = np.arange(24).reshape(4,6)
re = t1.swapaxes(1,0)
print (f'\n原数组:\n{t1}')
print (f'\n调用 swapaxes 函数后的数组:\n{re}')
---
原数组:
[[ 0 1 2 3 4 5]
[ 6 7 8 9 10 11]
[12 13 14 15 16 17]
[18 19 20 21 22 23]]
调用 swapaxes 函数后的数组:
[[ 0 6 12 18]
[ 1 7 13 19]
[ 2 8 14 20]
[ 3 9 15 21]
[ 4 10 16 22]
[ 5 11 17 23]]
这几种方式都完成了转置操作,平时工作中,我们可以都尝试一下。
总结
最后,我们还是对NumPy整个的做一个总结

然后,让我们留点作业吧:
练习矩阵相乘
练习数组索引
练习数组形状修改
大家要好好的完成作业。那本节课就到这里了,下课。