NumPy是一个开源的Python数据分析和科学计算库,全称为“Numerical Python”,主要用于数组计算。NumPy是作为数据分析必备库之一,是从事数据分析行业人员必要了解和学习的一个库,下面我们就来一起了解下NumPy。
为了能有一个直观的感受,我们尝试写一个示例,实现的功能是分别用Python的内置方法和NumPy的方法实现一亿个随机数的和,实现代码如下:
# 导入相关模块
import random
import time
import numpy as np
# 随机数存放list
numList = []
for i in range(100000000):
numList.append(random.random())
# 记录当前时间戳
t1 = time.time()
# Python内置函数求和
sum1 = sum(numList)
t2 = time.time()
# numpy生成一维数组
npList = np.array(numList)
t3 = time.time()
# numpy求和
sum3 = np.sum(npList)
t4 = time.time()
# 输出运行时间
print("Python:{},NumPy:{}".format(int(round((t2 - t1) * 1000)), int(round((t4 - t3) * 1000))))
#打印输出运行结果:
Python:450,NumPy:113
结果很明显,内置方法耗时约450毫秒,NumPy耗时约113毫秒,由于生成1亿个的随机数的时间比较长,整个代码运行时间会比较长,而输出的时间差仅仅是1亿个随机数相加和的耗时。从结果上看NumPy的速度约是Python内置方法的4倍。
注意:选用一亿个的参数原因是,如果数据量太少,运行时间相差不足几毫秒,不能显著比较速度差异。如果一次性数据量太大容易导致内存溢出,从而程序无法运行。
由于在代码中numpy 会使用的比较多,所以习惯上会给numpy起一个别名np。在后面中只要是np就是代表是numpy。当然你也可以给它起个其他的别名,不过在多数长江使用行为上会用np代替。
NumPy的官网为:https://numpy.org/ 在这里,你可以找到所有关于NumPy最权威的资料,包括最新版本的Api,但是官网是纯英文的,阅读难度有点大,有一定的挑战性。
首先看一个例子:
#导入numpy库
import numpy as np
numList = [1, 2]
npList = np.array(numList)
#打印相关参数
print(numList)
print(npList)
print(type(numList))
print(type(npList))
#代码运行结果:
[1, 2]
[1 2]
<class 'list'>
<class 'numpy.ndarray'>
这里numList 是一个list对象,而npList是一个ndarray对象,这是一个一维数组对象,是同类型的数据集合。在list 对象中,可以存放多种数据类型,比如整数、浮点数、字符串等,但是ndarray对象中仅仅支持一种数据类型。为了达到快速运算的目的,就不能支持太多的数据类型。
Ndarray对象的创建方式只需要调用np.array()即可,np.array()的详细参数如下:
array(p_object, dtype=None, copy=True, order='K', subok=False, ndmin=0)
p_object:一个数组或者嵌套数列,仅支持列表和元组的类型
dtype:数组元素的数据类型,可选
copy:是否复制对象,默认为True
order:创建数组的样式,可选, C为行方向,F为列方向,默认按照行方向创建
subok:是否返回一个与基类一样的数组,默认为True
ndmin:指定结果的最小维数
在dtype类型的具体有很多,下表中是常用的numpy数据类型:
类别 | 类型 | 值 |
---|---|---|
布尔型 | bool_ | True 或者 False |
整型 | intc | 和 C 的 int 相同(一般为 int64 或 int32) |
int8 | 字节(-128 到 127) | |
int16 | 整数(-32768 到 32767) | |
int32 | 整数(-2147483648 到 2147483647) | |
int64 | 整数(-9223372036854775808 到 9223372036854775807) | |
uint8 | 无符号整数(0 to 255) | |
uint16 | 无符号整数(0 to 65535) | |
uint32 | 无符号整数(0 to 4294967295) | |
uint64 | 无符号整数(0 to 18446744073709551615) | |
浮点型 | float_ | float64 类型的简写 |
float16 | 半精度浮点数 | |
float32 | 单精度浮点数 | |
float64 | 双精度浮点数 | |
复数型 | complex_ | complex128 类型的简写, |
complex64 | 复数,表示双 32 位浮点数(实数部分和虚数部分) | |
complex128 | 复数,表示双 64 位浮点数(实数部分和虚数部分) | |
字符串 | string_ | ASCII字符串 |
unicode_ | Unicode字符串 |
如果想要更改numpy元素的数据类型,直接在创建的array方法的参数中指定即可。
import numpy as np
#创建一个整数列表
a = [1, 2, 3]
b= np.array(a)
print(b.dtype)
# int32转成int64
b = np.array(a, dtype=np.int64)
print(b.dtype)
#代码运行结果
int32
int64
默认是int32的数据类型,通过dtype可以更改为int64类型,同理其他的数据类型也是一样。但是如果原类型是浮点型,转成整型,会造成数据精度缺失。
import numpy as np
a = [1.2, 2.6, 3.0]
b = np.array(a)
print(b.dtype)
#浮点型转成int64
b = np.array(a, dtype=np.int64)
print(b)
print(b.dtype)
#代码结果:
float64
[1 2 3]
int64
先看一段代码示例:
import numpy as np
#一维数组
a = np.array([1, 2, 3, 4])
print("a数据类型:", type(a))
print("a数组元素数据类型:", a.dtype)
print("a数组元素总数:", a.size)
print("a数组形状:", a.shape)
print("a数组的维度数目", a.ndim)
print("一维数组访问:", a[1])
#二维数组
b = np.array([[1, 2, 3],
[4, 5, 6]])
print("b数据类型:", type(b))
print("b数组元素数据类型:", b.dtype)
print("b数组元素总数:", b.size)
print("b数组形状:", b.shape)
print("b数组的维度数目", b.ndim)
print("二维数组访问:", b[1][1])
#代码结果:
a数据类型: <class 'numpy.ndarray'>
a数组元素数据类型:int32
a数组元素总数:4
a数组形状:(4,)
a数组的维度数目 1
一维数组访问: 2
b数据类型: <class 'numpy.ndarray'>
b数组元素数据类型:int32
b数组元素总数:6
b数组形状:(2, 3)
b数组的维度数目 2
二维数组访问: 5
上面示例中,ndarray对象a是一维数组,ndarray对象b是二维数组。一维数组本质上一个相同类型数据的线性集合,每个元素都只带有一个下标,而二维数组中每个元素都是一个一维数组,本质就是以数组作为数组元素的数组。每个元素会有两个下标,表示几行几列。二维数组又称为矩阵,行列数相等的矩阵称为方阵,学习过线性代数的读者对这些概念 并不会陌生。
一维数组的shape为(4,),只有一个数字,后面为空,表示一个4个元素的一维数组。而二维数组的shape是(2,3),表示两行三列的数组。二维数组除了可以使用b[1][1],同样可以写在一个[]中,使用b[1,1],两者是同样的表达意思。
注意:在二维数组中因为有行和列,表示所有的元素,但是有时候仅仅只是想对行或者对列进行操作,那么这时候会定义轴,用axis表示,axis=0表示从上往下,表示列,axis=1从左往右,表示行。在内存中的空间示意图如图所示。
arange(start, stop,step, dtype)
作用:创建指定数值范围内的数组
start:开始值
stop:结尾值
step:步长
dtype:数据类型,如果不指定则推断数据类型
import numpy as np
#从1到10,步长为2的数组
a = np.arange(1, 10, 2)
print(a)
#从1到10,步长为4的数组
b = np.arange(1, 10, 4)
print(b)
#运行结果:
[1 3 5 7 9]
[1 5 9]
linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None,axis=0)
作用:创建等差数组
start:开始值
stop:结尾值
num:设置生成元素个数,默认值50
endpoint:是否包含结尾数值,默认包含
dtype:数据类型,如果不指定则推断数据类型
retstep:步长,设置是否返回步长。如果为True,返回一个元组,包含ndarray和步长。
axis:存储结果中的轴。仅当start或stop是数组是才有用,默认情况下为0。
1.创建1-10的4个元素的等差数组,元素类型默认float64
import numpy as np
a = np.linspace(1, 10, 4)
print(a)
#运行结果:
[ 1. 4. 7. 10.]
2.创建1-10的4个元素的等差数组,并且返回步长
import numpy as np
a = np.linspace(1, 10, 4, retstep=True)
print(a)
#运行结果:
(array([ 1., 4., 7., 10.]), 3.0)
3.创建1-10的4个元素的等差数组,不包含结尾值
import numpy as np
a = np.linspace(1, 10, 4, endpoint=True)
print(a)
#运行结果:
[1. 3.25 5.5 7.75]
4.创建开始值为[1,1],结尾值为10的4个元素的等差数组
import numpy as np
a = np.linspace(1, 10, 4, endpoint=True)
print(a)
#运行结果:
[[ 1. 1.]
[ 4. 4.]
[ 7. 7.]
[10. 10.]]
5.创建开始值为[1,1],结尾值为10的4个元素的等差数组,轴为-1
import numpy as np
a = np.linspace(1, 10, 4, endpoint=True,)
print(a)
#运行结果:
[[ 1. 4. 7. 10.]
[ 1. 4. 7. 10.]]
logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None,axis=0)
作用:创建等比数组
start:开始值 base** start(base的start次幂)
stop:结尾值 base** stop(base的stop次幂)
base:底数,默认是10
1.创建开始值为10**0=1,结束值为10**3=1000的4个元素的等比数组,底数为10
import numpy as np
a = np.logspace(0, 3, 4)
print(a)
#运行结果:
[ 1. 10. 100. 1000.]
2.创建开始值为2**0=1,结束值为2**3=8的4个元素的等比数组,底数是2
import numpy as np
a = np.logspace(0, 3, 4, base=2)
print(a)
#运行结果:
[1. 2. 4. 8.]
3. 创建开始值为2**0=1,结束值为2**3=8的4个元素的等比数组,不包含endpoint
import numpy as np
a = np.logspace(0, 3, 4, base=2, endpoint=False)
print(a)
#运行结果:
[1. 1.68179283 2.82842712 4.75682846]
这其实就是在开始值为1,结束值小于8的区间,创建4个元素的等比数组。所以数组的值跟开始和结尾有很大的关系,跟endpoint的取值也有很大的关系。
zeros(shape, dtype=None)
作用:根据指定形状和数据类型生成全是0的数组
shape:形状,几行几列,类型是列表或者元组
dtype:数据类型
import numpy as np
#生成2行3列的0数组
a = np.zeros([2, 3])
print(a)
#代码运行结果:
[[0. 0. 0.]
[0. 0. 0.]]
#生成1行2列的0数组,实际是一维数组
b= np.zeros((2))
print(b)
#代码运行结果:
[0. 0.]
ones(shape, dtype=None)
作用:根据指定形状和数据类型生成全是1的数组,参数跟zeros相同
import numpy as np
#生成2行3列的0数组
a = np.zeros([2, 3])
print(a)
#代码运行结果:
[[1. 1. 1.]
[1. 1. 1.]]
#生成1行2列的0数组,实际是一维数组
b= np.zeros((2))
print(b)
#代码运行结果:
[1. 1.]
full(shape, fill_value, dtype=None)
作用:根据指定形状和数据类型生成全是指定填充数的数组,参数比zeros和ones多了一个fill_value ,这个值就是指定的填充数。
import numpy as np
#生成2行3列的数值全为10的数组
a = np.full([2, 3], 10)
print(a)
#代码运行结果:
[[10 10 10]
[10 10 10]]
#生成1行5列的数值全为5的数组,实际是一维数组
b = np.full((5), 5)
print(b)
#代码运行结果:
[5 5 5 5 5]
identity(n, dtype=None)
作用:创建单位矩阵,对角线元素为1,其余为0
n:表示数组形状
import numpy as np
#生成一个4行4列的单位矩阵
a = np.identity(4)
print(a)
#代码运行结果:
[[1. 0. 0. 0.]
[0. 1. 0. 0.]
[0. 0. 1. 0.]
[0. 0. 0. 1.]]
random.rand()
作用:根据指定的形状生成一个0-1均匀分布的随机数
import numpy as np
#生成4个随机数的一维数组
a = np.random.rand(4)
print(a)
#代码运行结果:
[0.02112632 0.87365592 0.80945496 0.29566459]
#生成2行3列随机数的二维数组
b = np.random.rand(2, 3)
print(b)
#代码运行结果:
[[0.16006514 0.33356576 0.84293238]
[0.95196988 0.7396325 0.86680162]]
random. randint(low, high=None, size=None, dtype='l')
作用:生成low到high范围内size个整数
import numpy as np
#生成1个1-4的随机整数
a = np.random.randint(4)
print(a)
#代码运行结果:
2
#生成5个1-10的随机整数
b = np.random.randint(1, 10, 5)
print(b)
#代码运行结果:
[9 5 9 2 9]
除此之外,Random中还有许多其他的生成各种随机数的方法,这些数组创建的方法主要是应用于数据实验、分析、对比的初始化,可以快速生成指定形状和数组类型的数组进行后续的操作。
1.数组转置
学过线性代数的同学对这个不会很陌生,在线性代数中有矩阵转置的操作。就是行与列对调。原来第一行变成第一列,原来的第一列变成第一行,以此来推,就是转置操作。在numpy中可以直接跟矩阵转转置一样,使用T或者转置数组,同样可以使用transpose()函数来处理。
import numpy as np
a = np.array([[1, 2, 3],
[4, 5, 6]])
# 数组转置
b = a.T
c = a.transpose()
print(b)
print(c)
print(a)
#代码运行结果:
b
[[1 4]
[2 5]
[3 6]]
c
[[1 4]
[2 5]
[3 6]]
2.进行运算
我们对数进行最多的操作莫非是加减乘除,求和、平均、求最大值、求最小值,numpy中这些操作是完全可以实现的。
import numpy as np
# 一维数组加减乘除
a = np.array([1, 2])
b = np.array([3, 4])
print("a + b=")
print(a + b)
print("a - b=")
print(a - b)
print("a * b=")
print(a * b)
print("a / b=")
print(a / b)#代码运行结果:
a + b=
[4 6]
a - b=
[-2 -2]
a * b=
[3 8]
a / b=
[0.33333333 0.5 ]
# 二维数组加减乘除
c = np.array([[1, 2],
[3, 4]])
d = np.array([[5, 6],
[7, 8]])
print("c + d=")
print(c + d)
print("c - d=")
print(c - d)
print("c * d=")
print(c * d)
print("c / d=")
print(c / d)
#代码运行结果:
c + d=
[[ 6 8]
[10 12]]
c - d=
[[-4 -4]
[-4 -4]]
c * d=
[[ 5 12]
[21 32]]
c / d=
[[0.2 0.33333333]
[0.42857143 0.5 ]]
可见,numpy的数据运算操作是对相同位置的数值进行运算操作。
3.求最值
import numpy as np
#一维数组
a = np.array([3, 6, 2, 7, 8, 4])
print("a的最小值:" + str(a.min()))
print("a的最大值:" + str(a.max()))
print("a的和:" + str(a.sum()))
print("a的平均值:" + str(a.mean()))
#代码运行结果:
a的最小值:2
a的最大值:8
a的和:30
a的平均值:5.0
#二维数组
b = np.array([[3, 6, 2],
[7, 8, 4],
[10, 1, 4]])
print("b的最小值:" + str(b.min()))
print("b的最大值:" + str(b.max()))
print("b的和:" + str(b.sum()))
print("b的平均值:" + str(b.mean()))
#代码运行结果:
b的最小值:1
b的最大值:10
b的和:45
b的平均值:5.0
在二维数组中,如果没有指定方向,那么会根据全部的数据元素来运算,此外根据0轴还是1轴的方向来进行比较或者求值。
import numpy as np
b = np.array([[3, 6, 2],
[7, 8, 4],
[10, 1, 4]])
print("b数组0轴(每一列)最小值:")
print(b.min(axis=0))
print("b数组1轴(每一行)最小值:")
print(b.min(axis=1))
#代码运行结果:
b数组0轴(每一列)最小值:
[3 1 2]
b数组1轴(每一行)最小值:
[2 4 1]
同理,求最大值、平均值、求和等操作都可以对列或者行进行操作,需要指明作用轴即可。
import numpy as np
b = np.array([[3, 6, 2],
[7, 8, 4],
[10, 1, 4]])
print("b数组0轴(每一列)最小值:")
print(b.min(axis=0))
print("b数组1轴(每一行)最小值:")
print(b.min(axis=1))
#代码运行结果:
b数组0轴(每一列)最小值:
[3 1 2]
b数组1轴(每一行)最小值:
[2 4 1]
4.连接
numpy中的concatenate()函数用于连接两个或者多个数组。函数方法如下:
concatenate(arrays, axis=None, out=None)arrays:连接的两个或者多个数组axis:连接方向
import numpy as np
a = np.array([[1, 2, 3],
[4, 5, 6]])
b = np.array([[1, 2, 3],
[4, 5, 6]])
# 列连接
c = np.concatenate((a, b), axis=0)
# 行连接
d = np.concatenate((a, b), axis=1)
print("列连接")
print(c)
print("行连接")
print(d)
#代码运行结果:
列连接
[[1 2 3]
[4 5 6]
[1 2 3]
[4 5 6]]
行连接
[[1 2 3 1 2 3]
[4 5 6 4 5 6]]
当axis=0时,a、b是从上往下链接,当axis=1时,a、b是从左往右链接。