目录
一、ndarray对象
列表的缺点:
NumPy的优点:
使用方法:
多维数组
ndarray对象:形状,shape
元素类型
元素类型强制转换
从数列创建ndarray数组
用from系列方法创建ndarray
结构数组
掩膜数组
数组下标使用技巧
二、ufunc函数
ufunc的算术运算符/比较运算符
ufunc函数测速
ufunc函数:自定义
广播
三、多维数组的下标存取
数组下标使用技巧
四、NumPy文件读写
NumPy文件读写
python标准库中的列表(list)可以当数组用,支持动态内存分配和垃圾收集。
列表元素可以是任何对象,功能强大!
NumPy官方提供丰富的中文资源
# 导入用conda或者pip安装到python默认路径下的包
import numpy as np # 导入名为numpy的包,起个昵称叫np
import numpy # 导入名为numpy的包
from numpy import array as ar # 从numpy中导入array,起个昵称叫ar
np = __import__("numpy") # 知道模块的名字就可以导入,这里使用的是import的函数形式 __import__(str) 注意双下划线
# Create the data.
from numpy import pi, sin, cos, mgrid
dphi, dtheta = pi/250.0, pi/250.0
[phi,theta] = mgrid[0:pi+dphi*1.5:dphi,0:2*pi+dtheta*1.5:dtheta]
m0 = 4; m1 = 3; m2 = 2; m3 = 3; m4 = 6; m5 = 2; m6 = 6; m7 = 4;
r = sin(m0*phi)**m1 + cos(m2*phi)**m3 + sin(m4*theta)**m5 + cos(m6*theta)**m7
x = r*sin(phi)*cos(theta)
y = r*cos(phi)
z = r*sin(phi)*sin(theta)
# View it.
from mayavi import mlab
s = mlab.mesh(x, y, z)
mlab.show()
多维数组ndarray(n-dimensional array object)是NumPy的核心对象
它存储单一类型的多维数组,注意与列表(list)的区别
ndarray对象的构造函数创建
array函数创建ndarray
import numpy as np
# 直接用ndarray对象的构造函数创建
# 它的参数是多维数组的维度
a = np.ndarray([2,3,4])
print(type(a))
print(a.shape) # a的维度通过shape属性获得,它是一个元组
print(a)
# 使用array函数创建ndarray
# 给np.array()函数传递python序列对象,将序列对象转换为ndarray对象
a=np.array([1,2,3,4])
b=np.array((5,6,7,8))
c=np.array([[1,2,3,4],[4,5,6,7],[7,8,9,10]])
print(type(a))
print('a = ',a)
print('b = ',b)
print('c = ',c)
import numpy as np
a = np.array([1.1, 2.0, 3.5])
print(a,type(a),a.dtype,a.shape)
b = np.array([1.1 + 2.5j, 2.0 + 5.1j, 3.5 + 2.7j])
print(b,type(b),b.dtype,b.shape)
c = np.array(['hello, world!','hello, numpy array!','我就试试中文'])
print(c,type(c),c.dtype,c.shape)
# 也可以用zeros, ones, empty和full函数,创建指定大小,值为0/1/空/指定数值的数组
zz=np.zeros((2,3,4))
oo=np.ones((2,3,4))
ee=np.empty((2,3,4))
ff=np.full((2,3,4),999)
print('zz = ', zz)
print('oo = ', oo)
print('ee = ', ee)
print('ff = ', ff)
# empty只分配内存,不赋值,最快。但是里面的内容是啥就不一定了!使用empty创建的ndarray,一定得初始化再使用。
# 创建形状类型与a相同的数组
za = np.zeros_like(a)
oa = np.ones_like(a)
ea = np.empty_like(a)
fa = np.full_like(a,999)
print('za = ', za)
print('oa = ', oa)
print('ea = ', ea)
print('fa = ', fa)
数组对象的形状通过shape属性获得,返回一个描述数组各个轴的长度的元组(tuple),元组的长度等于数组的维数
ndarray类型的对象里面,数据都是一维化之后存储在连续分配的内存中,ndarray的维度仅仅是告诉numpy如何读取而已
改变shape属性,改变数组的形状。
import numpy as np
c = np.array([[[1,2,3,4],[5,6,7,8],[9,10,11,12]],\
[[13,14,15,16],[17,18,19,20],[21,22,23,24]]])
print(c.shape)
print(c) # 2片,3行,4列(第0轴长度为2,第1轴长度为3,第2轴长度为4)
# 改变数组的形状
c.shape = (2,4,3) # 注意这不是转置!!!改变形状之后,数据的顺序是不变的。
print(c.shape)
print(c)
# 用-1表示这一个维度的长度是自动计算的
c.shape = 3,-1
print(c.shape)
print(c)
import numpy as np
# 用-1表示这一个维度的长度是自动计算的
c = np.array([[[1,2,3,4],[5,6,7,8],[9,10,11,12]],\
[[13,14,15,16],[17,18,19,20],[21,22,23,24]]])
print(c.shape)
print(c) # 2片,3行,4列(第0轴长度为2,第1轴长度为3,第2轴长度为4)
# 使用reshape创建指定形状的新数组
d = c.reshape((2,3,4))
print('d.shape = ', d.shape)
print('d = ', d)
# c的形状不变
print('c.shape = ', c.shape)
# 改变d的元素,c的元素仍会改变!
# 完全复制建议使用copy.deepcopy()
c[0,0] = 2233
print('c = ', c)
print('d = ', d)
d = c.reshape((3,2,4))
print('d = ', d)
import numpy as np
c = np.array([[[1,2,3,4],[5,6,7,8],[9,10,11,12]],\
[[13,14,15,16],[17,18,19,20],[21,22,23,24]]])
print(c.shape)
# 看看ndarray c的类型
print(c.dtype)
# 创建array的默认数据类型是?
a = np.array([1,2,3,4])
print(a.dtype)
b = np.array([1.0, 2.0, 3.0, 4.0])
print(b.dtype)
c = np.zeros(4)
print(c.dtype)
import numpy as np
# 创建数组时指定数据类型
ai32 = np.array([1, 2, 3, 4], dtype=np.int32)
af = np.array([1, 2, 3, 4], dtype=float)
ac = np.array([1, 2, 3, 4], dtype=complex)
# 其中np.int32时numpy的数据类型;float和complex是python内置的类型,会自动转换为numpy的数据类型
print(ai32.dtype)
print(af.dtype)
print(ac.dtype)
# 用 set(np.typeDict.values()) 查看numpy支持的类型
set(np.typeDict.values())
import numpy as np
# 类型转换
# np.int16将数值转换为C中的int16型,行为与C语言中的对应类型一致
a=np.int16(200)
# 下面这句话会导致溢出,看看是什么么结果?
print(a*a)
# 数据强制转换为numpy对象后,由于还要套着python对象,运算速度慢,不建议单独使用!
# 对numpy的对象,使用numpy的内置功能才能提高速度
v1 = 3.14
v2 = np.float64(v1)
print('python内置float类型测速:')
%timeit v1*v1
print('numpy内置float64类型测速:')
%timeit v2*v2
# 数组的类型转换
t1 = np.array([1, 2, 3, 4], dtype=np.float)
t2 = np.array([1, 2, 3, 4], dtype=np.complex)
# 使用astype方法对数组元素进行类型转换,返回一个新的数组,原数组不变
t3 = t1.astype(np.int32)
t4 = t2.astype(np.complex64)
print(t1.dtype)
print(t2.dtype)
print(t3.dtype)
print(t4.dtype)
np.arange()
np.linspace()
np.logspace()
import numpy as np
# 通过开始值、终值和步长来创建等差数列
np.arange(0, 1, 0.1) # 从0开始,到1结束,步长0.1,注意1不在数组中!
# 通过开始值、终值和元素个数创建等差数列
# np.linspace(0, 1, 10) # 从0开始,到1结束,10个元素的等差数列
np.linspace(0, 1, 10, endpoint=False) # 可以通过endpoint参数指定是否包含终值,默认值为True,即包含终值
# 通过开始值、终值和元素个数创建等比数列
# np.logspace(0, 2, 5) # 从0开始,到2结束,5个元素的等比数列
np.logspace(0, 1, 12, base=2, endpoint=False) # 可以通过base更改底数,默认为10
# 可以通过endpoint参数指定是否包含终值,默认值为True
from系列方法,可以从三个“流”式类型中直接创建出ndarray
fromstring():从字符串类型(str)创建
frombugffer():从字节序列类型(bytes)创建
fromfile():从文件类型(file)创建
除此之外,还有fromfunction方法,从函数创建ndarray
import numpy as np
# 从字符串创建ndarray
s = '1 2 3 4 5'
a = np.fromstring(s, dtype=float, sep=' ')
print(a)
# 改变dtype参数和sep参数(灵活切换各种分隔符)
s = '1, 2, 3, 4, 5'
a = np.fromstring(s, dtype=np.int8, sep=',')
print(a)
# 如果字符串里面保存的是字符而不是数字,转换会有问题
# 报警告提示类型不对
s = 'abcdefgh'
a = np.fromstring(s, dtype=np.int8)
print(a)
# 警告提示该功能已经废弃,
# 如果把字符串(真的含有英文字符的串)转化为ndarray,
# 应该用frombuffer以二进制的形式进行。
import numpy as np
# 从字节序列创建ndarray
# 可以将字符转换为数组,按照ASCII码转换
# 字符串之前加b,表示用bytes模式(字节序列)保存的字符串
s = b"abcdefgh"
a = np.frombuffer(s, dtype=np.int8)
print(a)
# 字符串的b模式只能支持ASCII码
s = b"中文字符串"
a = np.frombuffer(s, dtype=np.int8)
print(a)
# fromfile可以读取文本文件和二进制文件,这里我们以文本文件为例
with open('data.txt','r') as f:
a = np.fromfile(f)
print(a)
# 用fromfile的默认参数读取,好像不大对劲,哪里错了?
with open('data.txt','r') as f:
a = np.fromfile(f, dtype=np.int8)
print(a)
# 好像还是不对劲啊?
with open('data.txt','r') as f:
a = np.fromfile(f, dtype=np.int8, sep=' ')
print(a)
# 要设定正确的类型和分隔符,才能正确读取文本文件!
# fromfile相当于对文件对象f,使用fromstring或者frombuffer方法。
import numpy as np
# 先定义一个从下标计算数值的函数
def func1d(i):
return i % 4 +1
# 再用fromfunction创建指定大小的ndarray,其中的每个元素都通过下标来计算
# 注意数组的大小要用元祖表示。只有一个元素的元祖用(x,)这样的写法,如果只写一个数字会报错
a = np.fromfunction(func1d, (10,))
print(a)
# 从函数生成二维数组
def func2d(i,j):
return (i+1)*(j+1)
a = np.fromfunction(func2d, (9,9))
print(a)
# 从函数生成三维数组
def func3d(i,j,k):
return (i+1)*(j+1)*(k+1)
a = np.fromfunction(func3d, (2,5,5))
print(a)
# 来看看帮助
help(np.fromfile)
# help(np.fromstring)
# help(np.frombuffer)
# help(np.fromfunction)
import numpy as np
# 定义一个结构体,首先创建一个np.dtype对象persontype
# 它的参数是一个描述结构类型的各个字段的字典
# 字段有两个键:names和formats,每个键对应的值都是一个列表。names是字段名称,formats是字段类型。
persontype = np.dtype({
'names': ['name', 'age', 'weight'],
'formats': ['S30', 'i', 'f']}, align=True)
# 'S30'表示长度为30个字节的字符串类型。结构数组中的每个元素的长度必须是固定值,因此字符串类型也必须指定长度
# 'i'表示32位整型,相当于np.int32
# 'f'表示32位单精度浮点型,相当于np.float32
import numpy as np
# 定义一个结构体,首先创建一个np.dtype对象persontype
# 它的参数是一个描述结构类型的各个字段的字典
# 字段有两个键:names和formats,每个键对应的值都是一个列表。names是字段名称,formats是字段类型。
persontype = np.dtype({
'names': ['name', 'age', 'weight'],
'formats': ['S30', 'i', 'f']}, align=True)
# 'S30'表示长度为30个字节的字符串类型。结构数组中的每个元素的长度必须是固定值,因此字符串类型也必须指定长度
# 'i'表示32位整型,相当于np.int32
# 'f'表示32位单精度浮点型,相当于np.float32
# 用np.array()创建结构数组,其中的每个结构体元素用元祖表示
# 注意要设定dtype参数,设定位persontype
a = np.array([('Zhang',32,75.5),('Wang',24,65.2)], dtype=persontype)
# a = np.array([('张三',32,75.5),('Wang',24,65.2)], dtype=persontype)
# 上面这句话会报错,因为NumPy中的字符串还是ASCII字符串,使用的是b模式,不支持多国语言
# 结构数组的存取方式和一般数组一样,通过下标存取。
# 结构数组的元素看上去像是元组,但实际上是预先定义好的结构类型(在这里是persontype类型)
print(a)
print(a[0])
print(a[0].dtype)
# 可以使用字段名作为下标获取对应的字段值
print(a[0]['name'])
import numpy as np
# 定义一个结构体,首先创建一个np.dtype对象persontype
persontype = np.dtype({
'names': ['name', 'age', 'weight'],
'formats': ['S30', 'i', 'f']}, align=True)
a = np.array([('Zhang',32,75.5),('Wang',24,65.2)], dtype=persontype)
# 修改结构元素的字段,结构数组中对应的部分也会被修改
c = a[0]
c['name'] = 'Li'
print(c)
print(a)
# 可以直接获得结构数组的字段,返回的是原始数组的“视图”.
# 这个字段视图其实也是一个ndarray对象,对这个视图的修改会反映在结构数组上
b = a['age']
print(b)
print(type(b)) # 字段视图其实是ndarray对象
b += 5 # 对ndarray进行计算
print(b)
print(a) # 结果反映在原始的结构数组上
# 当某个字段类型为数组时,用元组的第三个元素表示数组形状
# 可以用下面的形式创建新的dtype类型,以列表为构造方法的输入
datatype = np.dtype([('data1','i4'),('data2','f8',(2,3)),('data3','f8',(10,10))])
print(datatype)
如果numpy数组中某些数值有缺失,或者我们不希望某些数值参与后面的运算,可以将数组的一部分掩盖起来。这种做法称之为掩膜(mask)。
import numpy as np
a = np.random.normal(size=(3,5))
print(a)
# 把小于0的都屏蔽掉
b = np.ma.masked_where(a < 0, a)
print(b)
下标方式:a[2]
切片方式:
通过切片获取的数组是原数组的一个“视图”,与原数组共享同一存储空间,因此修改结果数组会改变原始数组
import numpy as np
a = np.array([1,2,3,4,5,6,7])
a[2]
a[3:5]
a[:5]
a[:-1]
a[1:-1:2]
a[::-1]
a[5:1:-2]
ufunc是universal function的缩写,它是一种对数组的每个元素进行运算的函数
NumPy内置的许多ufunc函数都是用c语言实现的,速度很快
NumPy的数组对象支持加减乘除等操作
因为加减乘除操作在NumPy中使用ufunc实现,实际上是调用了ufunc
算术运算符:加减乘除乘方同余...
比较运算符:大于小于等于不等于...
数组对象支持操作符,极大的方便了程序编写。但是要注意如果算式很复杂、数组很大的时候,会产生过多的中间变量,降低程序运行速度。
可以适当考虑多用原位操作符,例如 x += y,复杂算式多分几行,减少对中间变量的内存分配
import numpy as np
x1 = np.array([1,2,3,4])
x2 = np.array([5,6,7,8])
y = x1 + x2 # add
print(y)
y = x1 - x2 # subtract
print(y)
y = x1 * x2 # multiply
print(y)
y = x1 / x2 # divide
print(y)
y = x1 // x2 # floor divide
print(y)
y = -x1 # negative
print(y)
y = x1 ** x2 # power
print(y)
y = x1 % x2 # remainder
print(y)
y += x1 # 原位操作符
print(y)
y = x1 == x2 # equal
print(y)
y = x1 != x2 # not equal
print(y)
y = x1 < x2 # less
print(y)
y = x1 <= x2 # less_equal
print(y)
y = x1 > x2 # greater
print(y)
y = x1 >= x2 # greater_equal
print(y)
下面比较四种方式计算正弦函数的速度,看看谁更快
import numpy as np
import math
import numpy as np
import copy
x = [i * 0.001 for i in range(1000000)]
def sin_math_loop(x):
for i, t in enumerate(x):
x[i] = math.sin(t)
def sin_math_list(x):
x = [math.sin(t) for t in x]
def sin_numpy(x):
np.sin(x, out = x)
# 由于np.sin是一个ufunc函数,因此在其内部对数组x的每个元素进行循环,分别计算它们的正弦值。
# np.sin的返回值是一个保存了计算结果的数组,这个数组是新建的。运算之后x的值并没有改变,仍然保持原状。
# 可以通过指定out参数指定保存计算结果的数组。如果希望进行原位计算,可以设定out=x。
def sin_numpy_loop(x):
for i, t in enumerate(x):
x[i] = np.sin(t)
# math.sin配合python的for循环
# 速度比较慢
x1 = copy.deepcopy(x)
%time sin_math_loop(x1)
# math.sin配合python的列表推导式
# 使用列表推导式可以稍微加快一点速度,但是不多
x2 = copy.deepcopy(x)
%time sin_math_list(x2)
# np.sin使用ufunc功能对数组直接计算
# 速度最快,比前面两个快10倍以上,这得益于numpy在c语言级别的循环
x3 = np.array(x)
%time sin_numpy(x3)
# np.sin配合python的for循环
# 最慢。比直接使用numpy函数在numpy数组上计算要满100倍。说明使用ufunction的必要性。
# np.sin为了同时支持对数组和单个数值的计算,它的内部实现比math.sin复杂的多。
x4 = copy.deepcopy(x)
%time sin_numpy_loop(x4)
使用frompyfunc(func, nin, nout)
其中func是python函数,nin是func的输入参数个数,nout是func的返回值个数
import numpy as np
# 自定义ufunc
def myfunc(x):
return x**2 + 1
my_ufunc = np.frompyfunc(myfunc, 1, 1)
x = np.linspace(0,10,11)
%timeit y = my_ufunc(x)
%timeit y = myfunc(x)
y = my_ufunc(x)
print(y)
import numpy as np
# 自定义ufunc
def triangle_wave(x, c, c0, hc):
x = x - int(x) # 三角波的周期为1,因此只取x坐标的小数部分进行计算
if x >= c: r = 0.0
elif x < c0: r = x / c0 * hc
else: r = (c-x) / (c-c0) * hc
return r
x = np.linspace(0, 2, 1000)
y1 = np.array([triangle_wave(t, 0.6, 0.4, 1.0) for t in x])
triangle_ufunc1 = np.frompyfunc(triangle_wave, 4, 1)
y2 = triangle_ufunc1(x, 0.6, 0.4, 1.0)
%C y2.dtype; y2.astype(np.float).dtype
triangle_ufunc2 = np.vectorize(triangle_wave, otypes=[np.float])
y3 = triangle_ufunc2(x, 0.6, 0.4, 1.0)
如果ufunc输入参数有多个数组,形状不同,会自动进行广播操作
import numpy as np
a = np.arange(0, 60, 10).reshape(-1,1)
print(a)
print(a.shape)
b = np.arange(0, 5)
print(b)
print(b.shape)
# 计算a+b的和,得到一个加法表。看numpy是如何广播的。
c = a + b
print(c)
print(c.shape)
# 根据规则1,b的shape维度差了1,前面增补1维
# 根据规则2,输出数组的长度是输入数组的各个轴的长度的最大值
# 上面的广播过程,相当于对a和b进行了扩展
# 可以用repeat显示的进行扩展
a = a.repeat(5, axis = 1)
print(a)
print(a.shape)
b.shape = (1,5)
b = b.repeat(6, axis = 0)
print(b)
print(b.shape)
# 这种广播方式很常用,numpy提供了ogrid对象,用于创建广播运算用的可以配对的数组
x, y = np.ogrid[:5,:5]
print(x)
print(y)
# numpy还提供了mgrid对象,它返回的是广播之后的数组
x, y = np.mgrid[:5,:5]
print(x)
print(y)
import numpy as np
x = np.arange(5,0,-1)
a = x[np.array([True, False, True, False, False])]
b = x[x>2]
c = x[[True, False, True, False, False]]
print('x = ', x)
print('a = ', a)
print('b = ', b)
print('c = ', c)
# 使用列表作为下标得到的数组不与原数组共享内存空间,对它的更改不反映到原数组上
b[0] = 2233
print('b = ', b)
print('x = ', x)
print('a = ', a)
import numpy as np
from mayavi import mlab
x, y, z = np.mgrid[:6,:7,:8]
c = np.zeros((6, 7, 8), dtype=np.int)
c.fill(1)
k = np.random.randint(2,5,size=(6, 7))
idx_i, idx_j, _ = np.ogrid[:6, :7, :8]
idx_k = k[:,:, np.newaxis] + np.arange(3)
c[idx_i, idx_j, idx_k] = np.random.randint(2,6, size=(6,7,3))
mlab.points3d(x[c>1], y[c>1], z[c>1], c[c>1], mode="cube", scale_factor=0.8,
scale_mode="none", transparent=True, vmin=0, vmax=8, colormap="Blues")
mlab.points3d(x[c==1], y[c==1], z[c==1], c[c==1], mode="cube", scale_factor=0.8,
scale_mode="none", transparent=True, vmin=0, vmax=8, colormap="Vega20", opacity = 0.2)
mlab.gcf().scene.background = (1,1,1)
mlab.figure()
x, y, z = np.mgrid[:6,:7,:3]
mlab.points3d(x, y, z, c[idx_i, idx_j, idx_k], mode="cube", scale_factor=0.8,
scale_mode="none", transparent=True, vmin=0, vmax=8, colormap="Purples", opacity = 1)
mlab.gcf().scene.background = (1,1,1)
mlab.show()
NumPy提供了一系列简便方法,可以直接将ndarray对象保存到文件,或者从文件加载ndarray对象。
下面主要介绍这些方法:
save,savez和load
savetxt和loadtxt
tofile
import numpy as np
# 随机生成ndarray
a = np.random.random((5,5))
b = np.random.normal(size=(5,5))
print(a)
print(b)
# 使用save,以二进制形式保存单个ndarray对象
# 注意save保存的文件的扩展名为npy
# 也可以省略扩展名,在保存的时候自动添加
np.save('a.npy',a)
np.save('b.npy',b)
# 使用load加载刚刚保存的ndarray对象
# 读文件的时候,扩展名npy必须要写
af = np.load('a.npy')
bf = np.load('b.npy')
print(af)
print(bf)
import numpy as np
# 随机生成ndarray
a = np.random.random((5,5))
b = np.random.normal(size=(5,5))
print(a)
print(b)
# 使用savez打包多个ndarray对象
# 打包的扩展名为npz,其实是多个npy组成的压缩包
np.savez('ab.npz',a,b)
# 使用load加载压缩包
zf = np.load('ab.npz')
# 查看各个数组的名称,发现数组按照顺序被自动命名了
print(zf.files)
# 从打包对象中,解包出原始的ndarray
print(zf['arr_0'])
print(zf['arr_1'])
# 使用savez的时候,可以用关键字参数,给每个ndarray对象起名字,这样就不会搞混了
np.savez('ab.npz',a=a,b=b)
zf = np.load('ab.npz')
print(zf.files)
print(zf['a'])
print(zf['b'])
# 使用savetxt,可以将ndarray对象输出为文本文件
# 但它只支持1维或2维ndarray
# 文件的扩展名可以自由设定,savetxt不会给你自动添加。一般设定为txt。
np.savetxt('a.txt', a, fmt='%10.8f', delimiter=' ', header='a0 a1 a2 a3 a4', comments='#')
# savetxt函数有很多参数,可以实现复杂的格式化输出
# help(np.savetxt)
import numpy as np
# 随机生成ndarray
a = np.random.random((5,5))
b = np.random.normal(size=(5,5))
# 使用loadtxt,从文本文件读取ndarray对象
af = np.loadtxt('a.txt')
print(af)
# loadtxt函数也有很多参数,可以从多种多样的文本文件读取数据,例如csv文件
# help(np.loadtxt)
# 每个ndarray对象都有tofile方法,可以快速输出为文本文件或二进制文件
# 但是tofile方法的选项比较少
# 设定sep参数为非空的字符串,tofile会输出为文本文件
a.tofile('a.txt',sep=',',format='%10.8f')
# 可以用fromfile读取,要注意设定正确的sep参数才行
# 读取的数组的维度信息并没有被保存,丢失了
af = np.fromfile('a.txt',sep=',')
print(af)
# 使用tofile保存二进制文件,这是tofile的默认方式
a.tofile('a.bin')
# 使用fromfile同样也能读二进制文件,数组的维度信息也丢失了
af = np.fromfile('a.bin')
print(af)