NumPy是Python中用于数据分析、机器学习、科学计算的重要软件包。它极大地简化了向量和矩阵的操作及处理。python的不少数据处理软件包依赖于NumPy作为其基础架构的核心部分(例如scikit-learn、SciPy、pandas和tensorflow)。除了数据切片和数据切块的功能之外,掌握numpy也使得开发者在使用各数据处理库调试和处理复杂用例时更具优势。
安装Numpy:
pip install numpy
Numpy数组是一个多维数组对象,称为ndarray,由两部分组成
import numpy as np
arr = np.array(
[
[1,2,3,4,5,6],
[7,8,9,10,11,12]
]
)
print(arr, type(arr)) # [[ 1 2 3 4 5 6] [ 7 8 9 10 11 12]]
print(arr.ndim) # 2,输出数组维度的个数(轴数),或者说“秩”,维度的数量也称为rank
print(arr.shape) # (3, 6),三行六列
print(arr.size) # 18,总元素个数
print(arr.dtype) # int32,元素的数据类型
print(arr.itemsize) # 4,每个元素的字节大小
print(arr.data) # ,整个数组元素的缓冲区
我们可以通过将python列表传入np.array来创建一个NumPy数组(也就是强大的ndarray)。在下面的例子里,创建出的数组如右边所示,通常情况下,我们希望NumPy为我们初始化数组的值,为此NumPy提供了诸如ones,zeros和random.random之类的方法。我们只需传入元素个数即可:
arr1 = np.array(range(10))
print(arr1)
arr2 = np.arange(10)
print(arr2)
arr3 = np.array([[1,2,3,4],[5,6,7,8]])
# arr3元素中只要有一个元素为str类型,那么所有的元素都会被转换为str类型数据
# arr3中如果两个列表中的元素个数相等,就是二维数组;如果元素个数不相等,会变成一维数组
print(arr3)
arr4 = np.random.rand(10).reshape(2,5)
# arr4:先给数组随机生成10个元素,然后将形状改变为2行5列
print(arr4)
# linspace():返回在间隔【开始,停止】上计算的num个均匀间隔的样本
arr6 = np.linspace(10,20,num = 21) # num=21将10-20平均分成20份
print(arr6)
# [10. 10.5 11. 11.5 12. 12.5 13. 13.5 14. 14.5 15. 15.5 16. 16.5
# 17. 17.5 18. 18.5 19. 19.5 20. ]
# np.linspace(10, 20, num=21, endpoint=False) # endpoint默认为True,即包含20;如为False则不包含20,即10-19
arr6 = np.linspace(10,20,num = 21, retstep=True) # retstep为True,表示显示步长
print(arr6)
# (array([10. , 10.5, 11. , 11.5, 12. , 12.5, 13. , 13.5, 14. , 14.5, 15. ,
# 15.5, 16. , 16.5, 17. , 17.5, 18. , 18.5, 19. , 19.5, 20. ]), 0.5)
# 创建元素都为0的数组
arr1 = np.zeros((3,5)) # 元素默认为浮点数类型,第一个参数为指定数组形状
print(arr1)
arr2 = np.zeros((3,5), dtype=np.int) # 元素为整型,默认为int32
还有一个与zeros用法相似的np.ones
# 创建元素都为1的数组
arr = np.ones(9)
arr1 = np.ones((2,3,4))
print(arr)
print(arr1)
arr1 = np.eye(5) # 创建一个正方的N*N的单位矩阵,对角线值为1,其余为0
print(arr1)
#[[1. 0. 0. 0. 0.]
# [0. 1. 0. 0. 0.]
# [0. 0. 1. 0. 0.]
# [0. 0. 0. 1. 0.]
# [0. 0. 0. 0. 1.]]
T方法:转置。如:原shape为(3,4)/(2,3,4),转置后为(4,3)/(4,3,2),所以一维数组转置后结果不变
arr1 = np.arange(10)
arr2 = np.zeros((2,5)) # 2行5列的数组
print(arr1.shape) # (10,)
print(arr2.shape) # (2, 5)
print(arr1.T)
print(arr2.T)
arr2原shape为两行五列的数组:
[
[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]
]
经过T方法转置后变成五行两列的数组:
[
[0. 0.]
[0. 0.]
[0. 0.]
[0. 0.]
[0. 0.]
]
重置数组的形状,但是注意:重置前后数组中的元素个数必须要保持一致。
arr1 = np.arange(10)
print(arr1)
print(arr1.reshape(2,5))
print(arr1.reshape(5,2))
[0 1 2 3 4 5 6 7 8 9]
[[0 1 2 3 4]
[5 6 7 8 9]]
[[0 1]
[2 3]
[4 5]
[6 7]
[8 9]]
带三个参数的reshape:
arr1 = np.arange(12).reshape(3,2,2) # 三个两行两列的数组
print(arr1)
[[[ 0 1]
[ 2 3]]
[[ 4 5]
[ 6 7]]
[[ 8 9]
[10 11]]]
arr1 = np.arange(5)
print(arr1)
print(np.resize(arr1, (3,4)))
[0 1 2 3 4]
[[0 1 2 3]
[4 0 1 2]
[3 4 0 1]]
resize返回一个新的形状的数组,不要元素个数相等。
如果元素个数少于resize指定的元素个数(3,4),即3*4=12个元素,那么resize会将元素随机排序形成新的数组元素;如果多于resize指定的元素个数,那么resize会将多于的元素丢弃。
arr1 = np.arange(10)
arr2 = arr1
print(arr1 is arr2)
arr1[2] = 100
print(arr1 is arr2)
# True
# True
通过上面的实例可以看出,赋值操作,arr1数组与arr2数组是完全一样的,改变两个数组中的任何一个元素,另一个数组中的元素也会随着改变。
如果想得到一个新的数组,可以使用copy方法
arr1 = np.arange(10)
arr2 = arr1.copy()
print(arr1 is arr2)
arr1[2] = 100
print(arr1)
print(arr2)
False
[ 0 1 100 3 4 5 6 7 8 9]
[0 1 2 3 4 5 6 7 8 9]
arr1 = np.arange(10, dtype = float)
print(arr1)
arr2 = arr1.astype(np.int64)
print(arr2, arr2.dtype)
[0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]
[0 1 2 3 4 5 6 7 8 9] int64
数组横向连接:
arr1 = np.arange(5)
arr2 = np.arange(5,9)
print(arr1)
print(arr2)
print(np.hstack((arr1,arr2))) # 横向连接
[0 1 2 3 4]
[5 6 7 8]
[0 1 2 3 4 5 6 7 8]
数组竖向连接(元素个数必须要相同):
arr3 = np.array([[1],[2],[3]])
arr4 = np.array([['a'],['b'],['c']])
print(np.vstack((arr3,arr4))) # 竖向连接
[['1']
['2']
['3']
['a']
['b']
['c']]
任意堆叠(stack):
arr1 = np.arange(5)
arr2 = np.arange(5,10)
print(arr1)
print(arr2)
# [0 1 2 3 4]
# [5 6 7 8 9]
print(np.stack((arr1,arr2))) # 参数axis值默认为0
[[0 1 2 3 4]
[5 6 7 8 9]]
print(np.stack((arr1,arr2), axis = 1))
[[0 5]
[1 6]
[2 7]
[3 8]
[4 9]]
arr1 = np.arange(16).reshape(4,4)
print(arr1)
print(np.hsplit(arr1,2))
[
[ 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]])
]
print(np.vsplit(arr1,2))
[
array([
[0, 1, 2, 3],
[4, 5, 6, 7]]),
array([
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
]
让我们创建两个NumPy数组,分别称作data和ones:
若要计算两个数组的加法,只需简单地敲入data + ones,就可以实现对应位置上的数据相加的操作(即每行数据进行相加),这种操作比循环读取数组的方法代码实现更加简洁。
许多情况下,我们希望进行数组和单个数值的操作(也称作向量和标量之间的操作)。比如:如果数组表示的是以英里为单位的距离,我们的目标是将其转换为公里数。可以简单的写作data * 1.6:
NumPy通过数组广播(broadcasting)知道这种操作需要和数组的每个元素相乘。
我们可以像python列表操作那样对NumPy数组进行索引和切片,如下图所示:
arr = np.array([
[1,2,3,4],
[5,6,7,8],
[11,22,33,44],
[55,66,77,12],
[88,99,78,79]
])
# 取行
print(arr[2]) # 取第3行
print(arr[2:]) # 取第3行到末行
print(arr[[2,4]]) # 取不连续的行,第3行与第5行
print('=' * 100)
# 取列(逗号前表示行,后表示列)
print(arr[:,0]) # 第1列
print(arr[:, 2:]) # 取第3列到最后列
print(arr[:, [0,2]]) # 取第1列,第3列
print('=' * 100)
# 取行又取列
print(arr[2:4,1:3]) # 取第3行到第5行,第2列到第4列(含头不含尾)
输出结果 :
[11 22 33 44]
[[11 22 33 44]
[55 66 77 12]
[88 99 78 79]]
[[11 22 33 44]
[88 99 78 79]]
====================================================================================================
[ 1 5 11 55 88]
[[ 3 4]
[ 7 8]
[33 44]
[77 12]
[78 79]]
[[ 1 3]
[ 5 7]
[11 33]
[55 77]
[88 78]]
====================================================================================================
[[22 33]
[66 77]]
NumPy为我们带来的便利还有聚合函数,聚合函数可以将数据进行压缩,统计数组中的一些特征值:
除了min,max和sum等函数,还有mean(均值),prod(数据乘法)计算所有元素的乘积,std(标准差),等等。上面的所有例子都在一个维度上处理向量。除此之外,NumPy之美的一个关键之处是它能够将之前所看到的所有函数应用到任意维度上。
我们可以通过将二维列表传给Numpy来创建矩阵。
np.array([[1,2],[3,4]])
除此外,也可以使用上文提到的ones、zeros和random.random来创建矩阵,只需传入一个元组来描述矩阵的维度:
对于大小相同的两个矩阵,我们可以使用算术运算符(±*/)将其相加或者相乘。NumPy对这类运算采用对应位置(position-wise)操作处理:
对于不同大小的矩阵,只有两个矩阵的维度同为1时(例如矩阵只有一列或一行),我们才能进行这些算术运算,在这种情况下,NumPy使用广播规则(broadcast)进行操作处理:
与算术运算有很大区别是使用点积的矩阵乘法。NumPy提供了dot方法,可用于矩阵之间进行点积运算:
索引和切片功能在操作矩阵时变得更加有用。可以在不同维度上使用索引操作来对数据进行切片。
不仅可以聚合矩阵中的所有值,还可以使用axis参数指定行和列的聚合:
处理矩阵时经常需要对矩阵进行转置操作,常见的情况如计算两个矩阵的点积。NumPy数组的属性T可用于获取矩阵的转置。
在较为复杂的用例中,你可能会发现自己需要改变某个矩阵的维度。这在机器学习应用中很常见,例如模型的输入矩阵形状与数据集不同,可以使用NumPy的reshape方法。只需将矩阵所需的新维度传入即可。也可以传入-1,NumPy可以根据你的矩阵推断出正确的维度:
上文中的所有功能都适用于多维数据,其中心数据结构称为ndarray(N维数组)。
很多时候,改变维度只需在NumPy函数的参数中添加一个逗号,如下图所示:
NumPy的关键用例是实现适用于矩阵和向量的数学公式。这也Python中常用NumPy的原因。例如,均方误差是监督机器学习模型处理回归问题的核心:
在NumPy中可以很容易地实现均方误差:
这样做的好处是,numpy无需考虑predictions与labels具体包含的值。文摘菌将通过一个示例来逐步执行上面代码行中的四个操作:
预测(predictions)和标签(labels)向量都包含三个值。这意味着n的值为3。在我们执行减法后,我们最终得到如下值:
日常接触到的数据类型,如电子表格,图像,音频…等,如何表示呢?Numpy可以解决这个问题。
电子表格或数据表都是二维矩阵。电子表格中的每个工作表都可以是自己的变量。python中类似的结构是pandas数据帧(dataframe),它实际上使用NumPy来构建的。
音频文件是一维样本数组。每个样本都是代表一小段音频信号的数字。CD质量的音频每秒可能有44,100个采样样本,每个样本是一个-65535到65536之间的整数。这意味着如果你有一个10秒的CD质量的WAVE文件,你可以将它加载到长度为10 * 44,100 = 441,000个样本的NumPy数组中。想要提取音频的第一秒?只需将文件加载到我们称之为audio的NumPy数组中,然后截取audio[:44100]。
时间序列数据也是如此(例如,股票价格随时间变化的序列)。
图像是大小为(高度×宽度)的像素矩阵。如果图像是黑白图像(也称为灰度图像),则每个像素可以由单个数字表示(通常在0(黑色)和255(白色)之间)。如果对图像做处理,裁剪图像的左上角10 x 10大小的一块像素区域,用NumPy中的image[:10,:10]就可以实现。
如果图像是彩色的,则每个像素由三个数字表示 :红色,绿色和蓝色。在这种情况下,我们需要第三维(因为每个单元格只能包含一个数字)。因此彩色图像由尺寸为(高x宽x 3)的ndarray表示。
如果我们处理文本,情况就会有所不同。用数字表示文本需要两个步骤,构建词汇表(模型知道的所有唯一单词的清单)和嵌入(embedding)。让我们看看用数字表示这个(翻译的)古语引用的步骤:“Have the bards who preceded me left any theme unsung?”
模型需要先训练大量文本才能用数字表示这位战场诗人的诗句。我们可以让模型处理一个小数据集,并使用这个数据集来构建一个词汇表(71,290个单词):
然后可以将句子划分成一系列“词”token(基于通用规则的单词或单词部分):
然后我们用词汇表中的id替换每个单词:
这些ID仍然不能为模型提供有价值的信息。因此,在将一系列单词送入模型之前,需要使用嵌入(embedding)来替换token/单词(在本例子中使用50维度的word2vec嵌入):
你可以看到此NumPy数组的维度为[embedding_dimension x sequence_length]。
在实践中,这些数值不一定是这样的,但我以这种方式呈现它是为了视觉上的一致。出于性能原因,深度学习模型倾向于保留批数据大小的第一维(因为如果并行训练多个示例,则可以更快地训练模型)。很明显,这里非常适合使用reshape。例如,像BERT这样的模型会期望其输入矩阵的形状为:[batch_size,sequence_length,embedding_size]。
这是一个数字合集,模型可以处理并执行各种有用的操作。我留空了许多行,可以用其他示例填充以供模型训练(或预测)。
事实证明,在我们的例子中,那位诗人的话语比其他诗人的诗句更加名垂千古。尽管生而为奴,诗人安塔拉(Antarah)的英勇和语言能力使他获得了自由和神话般的地位,他的诗是伊斯兰教以前的阿拉伯半岛《悬诗》的七首诗之一。
numpy.random包含多种概率分布的随机样本,是数据分析辅助的重点工具之一。
print(np.random.normal(size = (4,4))) # 生成四行四列的数组
[[ 0.58060145 -1.37796471 0.76148182 -1.57750076]
[ 0.63511395 -0.80521374 -1.58209363 -1.33898238]
[-0.12763347 -0.5719946 -0.06165193 0.55572598]
[ 1.732809 0.32639484 -1.30179119 0.48939601]]
print(np.random.rand()) # 默认生成0-1之间的随机浮点数
print(np.random.rand(4)) # 生成一维四元素数组
print(np.random.rand(2,4)) # 生成二维四元素数组,元素为0-1的随机浮点数
numpy.random.randint(low, high=None, size=None, dtype='1');
生成一个整数或N维整数数组
若high不为None时,取[low,high]之间随机整数,否则取值[0, low]之间随机数,且hight必须大于low
dtype参数:只能是int类型
print(np.random.randint(2)) # 0-2之间整数,不包含2
print(np.random.randint(2,10)) # 不包含10
print(np.random.randint(10, size = 10)) # 生成一个一维数组,元素0-10,不包含10,size指定数组中有10个元素
print(np.random.randint(10, size=(2,5))) # 生成一个两行五列的二维数组,元素大小随机0-10,不包含10
np.loadtxt(frame.dtype=np.float.delimiter=None, skiprows=0, usecols=None, unpack=False)
参数 | 解释 |
---|---|
frame | 文件,字符串或产生器,可以是.gz或bz2压缩文件 |
dtype | 数据类型,可选,CSV的字符串以什么数据类型读入数组中,默认np.float |
delimiter | 分隔字符串,默认是任何空格 |
skiprows | 跳过前x行,一般跳过第一行表头 |
usecols | 读取指定的列,索引,元组类型 |
unpack | 如果True,读入属性将分别写入不同数组变量;False读入数据只写入一个数组变量,默认为False |
案例:现在这里有一个英国和美国各自youtube1000多个视频的点击,喜欢,不喜欢,评论数量([“views”,“likes”,“dislikes”,“comment_total”])的csv,尝试对其进行操作
import numpy as np
us_file_path = './files/US_video_data_numbers.csv'
uk_file_path = './files/GB_video_data_numbers.csv'
t1 = np.loadtxt(us_file_path, delimiter = ',', dtype = 'int')
t2 = np.loadtxt(us_file_path, delimiter = ',', dtype = 'int', unpack = True)
# delimiter:不指定分隔符会导致每行数据为一个整体的字符串而报错
# dtype:默认对较大的数据会使用科学计数法进行显示
print(t1)
print('='*50)
print(t2)
输出结果为:
# 注意下面unpack=True与False的区别
[[4394029 320053 5931 46245]
[7860119 185853 26679 0]
[5845909 576597 39774 170708]
...
[ 142463 4231 148 279]
[2162240 41032 1384 4737]
[ 515000 34727 195 4722]]
==================================================
[[4394029 7860119 5845909 ... 142463 2162240 515000]
[ 320053 185853 576597 ... 4231 41032 34727]
[ 5931 26679 39774 ... 148 1384 195]
[ 46245 0 170708 ... 279 4737 4722]]
将数组保存为npy文件:
arr1 = np.random.rand(5,5)
print(arr1)
np.save('test.npy', arr1)
从npy文件读取数据:
arr_load = np.load('test.npy')
print(arr_load)
将数据保存为txt文件:
np.savetxt('test.txt', arr1, delimiter = ',', fmt = '%.2f')
# delimiter:指定分隔符为逗号
# 默认保存到txt文件的元素使用的都是科学记数法,fmt设置元素为浮点数且保留两位小数位
从txt文件读取数据:
data = np.loadtxt('test.txt', delimiter = ',')