Numpy的ndarray:一种多维数组对象
NumPy最重要的一个特点就是其N维数组对象(即ndarray),该对象是一个快速而灵活的大数据集容器。你可以利用这种数组对整块数据执行一些数学运算。其语法跟标量元素之间的运算一样:
from numpy.random import randn
data=randn(2,3)
print(data)
print(data*10)
print(data+data)
#shape表示各维度大小的元组
print(data.shape)
#dtype是用于说明数组类型的对象
print(data.dtype)
输出结果:
[[ 0.19944519 -0.61506 -0.62834285]
[-0.35818596 -0.02595529 -1.02141258]]
[[ 1.99445186 -6.15060001 -6.28342852]
[ -3.58185957 -0.25955286 -10.2141258 ]]
[[ 0.39889037 -1.23012 -1.2566857 ]
[-0.71637191 -0.05191057 -2.04282516]]
(2, 3)
float64
创建ndarray
创建数组最简单的办法就是使用array函数。它接受一切序列型的对象(包括其他数组),然后产生一个新的含有传入数据的NumPy数组。以一个列表的转换为例:
import numpy as np
data1=[6,7.5,8,0,1]
arr1=np.array(data1)
print(arr1)
#嵌套序列(比如由一组等长列表组成的列表)将会被转换为一个多维数组
data2=[[1,2,3,4],[5,6,7,8]]
arr2=np.array(data2)
print(arr2)
print(arr2.ndim)
print(arr2.shape)
输出结果:
[ 6. 7.5 8. 0. 1. ]
[[1 2 3 4]
[5 6 7 8]]
2
(2, 4)
除np.array之外,还有一些函数也可以新建数组。比如,zeros和ones分别可以创建指定长度或形状的全0或全1数组。empty可以创建一个没有任何具体值的数组。要用这些方法创建多维数组,只需传入一个表示形状的元组即可:
print(np.zeros(10))
print(np.zeros((3, 6)))
y = np.arange(3)
zerolike=np.zeros_like(y)
print(zerolike)
#empty返回的不是0,而是一些未初始化的随机值
print(np.empty((2, 3, 2)))
#arange是Python内置函数range的数组版
print(np.arange(15))
#创建一个正方形的单位矩阵,eye和identity一样
print(np.eye(3))
y = np.arange(3)
zerolike=np.zeros_like(y)
print(zerolike)
#empty返回的不是0,而是一些未初始化的随机值
print(np.empty((2, 3, 2)))
#arange是Python内置函数range的数组版
print(np.arange(15))
#创建一个正方形的单位矩阵,eye和identity一样
print(np.eye(3))
输出结果:
[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[[ 0. 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0. 0.]]
[0 0 0]
[[[ 4.87953219e-316 4.83833942e-316]
[ 4.74148279e-316 4.34361042e-311]
[ 4.81748589e-316 4.34361042e-311]]
[[ 2.93185076e-316 7.79101500e-315]
[ 4.88288319e-316 4.78211988e-316]
[ 6.76676135e-315 6.76668368e-315]]]
[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14]
[[ 1. 0. 0.]
[ 0. 1. 0.]
[ 0. 0. 1.]]
ndarray的数据类型
dtype(数据类型)是一个特殊的对象,它含有ndarray将一块内存解释为特定数据类型所需的信息:
arr1 = np.array([1, 2, 3], dtype=np.float64)
arr2 = np.array([1, 2, 3], dtype=np.int32)
print(arr1.dtype)
print(arr2.dtype)
输出结果:
float64
int32
你可以通过ndarray的astype方法显式地转换其dtype:
arr = np.array([1, 2, 3, 4, 5])
print(arr.dtype)
float_arr = arr.astype(np.float64)
print(float_arr.dtype)
输出结果:
int32
float64
在本例中,整数被转换成了浮点数。如果将浮点数转换成整数,则小数部分将会被截断:
arr = np.array([3.7, -1.2, -2.6, 0.5, 12.9, 10.1])
print(arr)
print(arr.astype(np.int32))
输出结果:
[ 3.7 -1.2 -2.6 0.5 12.9 10.1]
[ 3 -1 -2 0 12 10]
数组的dtype还有另外一个用法:
int_array = np.arange(10)
calibers = np.array([.22, .270, .357, .380, .44, .50], dtype=np.float64)
print(int_array.astype(calibers.dtype))
输出结果:
[ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]
你还可以用简洁的类型代码来表示dtype:
empty_uint32 = np.empty(8, dtype='u4')
print(empty_uint32)
输出结果:
[14352383 0 14389440 0 0 0 0 0]
数组和标量之间的运算
数组很重要,因为它使你不用编写循环即可对数据执行批量运算。这通常就叫做矢量化。大小相等的数组之间的任何运算都会将运算应用到元素级:
arr = np.array([[1., 2., 3.], [4., 5., 6.]])
print(arr)
print(arr * arr)
print(arr - arr)
#同样,数组与标量的算术运算也会将那个标量值传播到各个元素:
print(1 / arr)
print(arr ** 0.5)
输出结果:
[[ 1. 2. 3.]
[ 4. 5. 6.]]
[[ 1. 4. 9.]
[ 16. 25. 36.]]
[[ 0. 0. 0.]
[ 0. 0. 0.]]
[[ 1. 0.5 0.33333333]
[ 0.25 0.2 0.16666667]]
[[ 1. 1.41421356 1.73205081]
[ 2. 2.23606798 2.44948974]]
不同大小的数组之间的运算叫做广播,我们将在第12章中对其进行详细讨论
基本的索引和切片
NumPy数组的索引是一个内容丰富的主题,因为选取数据子集或单个元素的方式有很多。一维数组很简单。从表面上看,它们跟Python列表的功能差不多:
arr=np.arange(10)
print(arr[5])
print(arr[5:8])
arr[5:8]=12
print(arr)
输出结果:
5
[5 6 7]
[ 0 1 2 3 4 12 12 12 8 9]
复制副本只能用copy()方法
arr_slice1=arr[5:8].copy()
arr_slice1[:]=64
print(arr)
arr_slice = arr[5:8]
arr_slice[:] = 64
print(arr)
输出结果:
[0 1 2 3 4 5 6 7 8 9]
[ 0 1 2 3 4 64 64 64 8 9]
递归访问和切片索引
arr2d=np.array([[1,2,3],[4,5,6],[7,8,9]])
print(arr2d[0][2])
#也可以写成下面的形式
print(arr2d[0,2])
print(arr2d[:2,1:])
print(arr2d[:,:1])
输出结果:
3
3
[[2 3]
[5 6]]
[[1]
[4]
[7]]
布尔型索引
来看这样一个例子,假设我们有一个用于存储数据的数组以及一个存储姓名的数组(含有重复项)。在这里,我将使用numpy.random中的randn函数生成一些正态分布的随机数据。假设每个名字都对应data数组中的一行,而我们想要选出对应于名字“Bob”的所有行。跟算术运算一样,数组的比较运算(如==)也是矢量化的。因此,对names和字符串“Bob”的比较运算将会产生一个布尔型数组:
names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
data = randn(7, 4)
print(data)
print(names=='Bob')
print(data[names=='Bob'])
输出结果:
[[ 1.7250243 -0.90324766 -0.80458208 0.58658337]
[ 1.13815199 -0.54509708 -0.55398749 0.52387601]
[ 1.29044203 1.16643074 -1.34865016 -0.48044707]
[-0.12598368 1.14410135 -0.98494842 1.12368884]
[ 0.35468068 -0.56479507 -0.81163169 0.08580436]
[ 0.2285224 -2.07053732 -0.71711736 0.42071483]
[-0.75537553 0.45912645 0.74415315 -1.78303911]]
[ True False False True False False False]
[[ 1.7250243 -0.90324766 -0.80458208 0.58658337]
[-0.12598368 1.14410135 -0.98494842 1.12368884]]
花式索引
花式索引是一个NumPy术语,它指的是利用整数数组进行索引。假设我们有一个8*4数组:
arr = np.empty((8, 4))
for i in range(8):
arr[i] = i
print(arr)
print(arr[[4, 3, 0, 6]])
输出结果:
[[ 0. 0. 0. 0.]
[ 1. 1. 1. 1.]
[ 2. 2. 2. 2.]
[ 3. 3. 3. 3.]
[ 4. 4. 4. 4.]
[ 5. 5. 5. 5.]
[ 6. 6. 6. 6.]
[ 7. 7. 7. 7.]]
[[ 4. 4. 4. 4.]
[ 3. 3. 3. 3.]
[ 0. 0. 0. 0.]
[ 6. 6. 6. 6.]]
数组转置和轴对换
转置是重塑的一种特殊形式,它返回的是源数据的视图(不会进行任何复制操作)。数组不仅有transpose方法,还有一个特殊的T属性:
arr = np.arange(15).reshape((3, 5))
print(arr)
print(arr.T)
arr1=np.arange(16).reshape((2,2,4))
print(arr1)
print(arr1.transpose((1,0,2)))
输出结果:
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]]
[[ 0 5 10]
[ 1 6 11]
[ 2 7 12]
[ 3 8 13]
[ 4 9 14]]
[[[ 0 1 2 3]
[ 4 5 6 7]]
[[ 8 9 10 11]
[12 13 14 15]]]
[[[ 0 1 2 3]
[ 8 9 10 11]]
[[ 4 5 6 7]
[12 13 14 15]]]
理解transpose()中的参数的意义,数组a的shape为(2,2,4),是一个三维数组,这个元组对应的索引为:(0,1,2),也就是a.shape的下标:(2[0], 2[1], 4[2]), []中对应的是shape元组的索引。那么,现在,通过b = a.transpose(1, 0, 2),那么b.shape就变成(2, 2, 4),这就是说transpose就是改变高维数组的形状,形状改变了,那么里面的元素自然也要重新排列。比如:元素4在a中的位置是a[0][1][1],经过b = a.transpose(1, 0, 2)之后,11在b中的位置就变成b[1][0][1]。再比如元素11,在a中的位置a[1][0][3],在b中为:a[0][1][3]。
通用函数(Universal Functions)
一元通用函数
函数 | 说明 |
abs、fabs | 计算整数、浮点数或复数的绝对值。对于非复数值,可以使用更快的fabs |
sqrt | 计算各元素的平方根。相当于arr**0.5 |
square | 计算各元素的平方。相当于arr**2 |
exp | 计算各元素的指数e^x |
log、log10、log2、log1p | 分别为自然对数(底数为e)、底数为10的log、底数为2的log、log(1+x) |
sign | 计算各元素的正负号:1(正数)、0(零)、-1(负数) |
ceil | 计算各元素的ceiling值,即大于等于该值的最小整数 |
floor | floor值,即小于等于该值的最大整数 |
rint | 四舍五入,保留dtype |
modf | 将数组的小数和整数部分以两个独立数组形式返回 |
isnan | 返回一个表示是否为空的数组 |
isfinite、isinf | 返回一个表示是否有限或是否无穷的数组 |
cos、cosh、sin、sinh、tan、tanh | 普通型和双曲型三角函数 |
arccos、arccosh、arcsin、arcsinh、arctan、arctanh | 反三角函数 |
logical_not | 计算个元素not x的真值。相当于 -arr |
二元通用函数
函数 | 说明 |
add | 元素加 |
subtract | 元素减 |
multiply | 元素乘 |
divide/floor_divide | 元素除/元素向下整除 |
power | 第一个元素A,第二个元素B,则表示计算A的B次方 |
maximum/fmax | 元素级最大值/抛弃NAN |
minimum/fmin | 元素级最小值/抛弃NAN |
mod | 元素级求模 |
greater、greater_equal、less、less_equal、equal、not_equal | 执行元素级别的比较运算,最终产生布尔型数组,>、>=、<、<=、==、!= |
利用数组进行数据处理
meshgrid函数,np.arange(-1, 1, 0.01)变成了矩阵x的行向量,np.arange(-1, 1, 0.01)变成了矩阵y的列向量
x, y = np.meshgrid(np.arange(-1, 1, 0.01), np.arange(-1, 1, 0.01))
print(x)
print(y)
输出结果:
[[-1. -0.99 -0.98 ..., 0.97 0.98 0.99]
[-1. -0.99 -0.98 ..., 0.97 0.98 0.99]
[-1. -0.99 -0.98 ..., 0.97 0.98 0.99]
...,
[-1. -0.99 -0.98 ..., 0.97 0.98 0.99]
[-1. -0.99 -0.98 ..., 0.97 0.98 0.99]
[-1. -0.99 -0.98 ..., 0.97 0.98 0.99]]
[[-1. -1. -1. ..., -1. -1. -1. ]
[-0.99 -0.99 -0.99 ..., -0.99 -0.99 -0.99]
[-0.98 -0.98 -0.98 ..., -0.98 -0.98 -0.98]
...,
[ 0.97 0.97 0.97 ..., 0.97 0.97 0.97]
[ 0.98 0.98 0.98 ..., 0.98 0.98 0.98]
[ 0.99 0.99 0.99 ..., 0.99 0.99 0.99]]
将条件逻辑表述为数组运算
xarr = np.array([1.1, 1.2, 1.3, 1.4, 1.5])
yarr = np.array([2.1, 2.2, 2.3, 2.4, 2.5])
cond = np.array([True, False, True, True, False])
result=[(x if c else y) for x,y,c in zip(xarr,yarr,cond)]
print(result)
#用np.where将更加简洁
result1=np.where(cond,xarr,yarr)
print(result1)
输出结果:
[1.1000000000000001, 2.2000000000000002, 1.3, 1.3999999999999999, 2.5]
[ 1.1 2.2 1.3 1.4 2.5]
np.where的第二个和第三个参数不必是数组,它们都可以是标量值。在数据分析工作中,where通常用于根据另一个数组而产生一个新的数组。假设有一个由随机数据组成的矩阵,你希望将所有正值替换为2,将所有负值替换为-2。若利用np.where,则会非常简单:
arr = randn(4, 4)
print(arr)
#将正值设置为2,负值设置为-2
print(np.where(arr > 0, 2, -2))
#只将正值设置为2
print(np.where(arr > 0, 2, arr))
输出结果:
[[-0.73105396 -0.78999342 0.41619064 0.15435376]
[-0.72645598 -1.54292098 -0.12804657 0.08311469]
[ 0.54339172 -0.24310633 0.30142777 0.70569636]
[-0.30168264 -0.12709612 -0.98928312 0.57834097]]
[[-2 -2 2 2]
[-2 -2 -2 2]
[ 2 -2 2 2]
[-2 -2 -2 2]]
[[-0.73105396 -0.78999342 2. 2. ]
[-0.72645598 -1.54292098 -0.12804657 2. ]
[ 2. -0.24310633 2. 2. ]
[-0.30168264 -0.12709612 -0.98928312 2. ]]
数学和统计方法
可以通过数组上的一组数学函数对整个数组或某个轴向的数据进行统计计算。
arr = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
print(arr)
print(arr.cumsum(0))
print(arr.cumsum(1))
输出结果
[[0 1 2]
[3 4 5]
[6 7 8]]
[[ 0 1 2]
[ 3 5 7]
[ 9 12 15]]
[[ 0 1 3]
[ 3 7 12]
[ 6 13 21]]
基本数组统计方法
方法 | 说明 |
sum | 对数组中全部或者某轴向的元素求和。 |
mean | 算术平均数 |
std、var | 标准差和方差,自由度可调 |
min、max | 最大值和最小值 |
argmin、argmax | 分别为最大和最小元素的索引 |
cumsum | 所有元素的累计和,参数0表示列,参数1表示行 |
cumprod | 所有元素的累计积,参数0表示列,参数1表示行 |
用于布尔型数组的方法
在上面这些方法中,布尔值会被强制转换为1(True)和0(False)。因此,sum经常被用来对布尔型数组中的True值计数:
arr = randn(100)
print((arr > 0).sum())
#any用于测试数组中是否存在一个或多个True,而all则检查数组中所有值是否都是True
bools = np.array([False, False, True, False])
print(bools.any())
print(bools.all())
输出结果:
49
True
False
排序
跟Python内置的列表类型一样,NumPy数组也可以通过sort方法就地排序:
arr = randn(8)
arr.sort()
print(arr)
输出结果:
[-2.00572783 -0.76284135 -0.64955149 -0.17702569 -0.13612327 0.26842501 0.78287524 1.02383655]
唯一化以及其他的集合逻辑
Numpy提供了一些针对一维ndarray的基本集合运算。最常用的可能要数np.unique了,它用于找出数组中的唯一值并返回已排序额结果:
names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
ints = np.array([3, 3, 3, 2, 2, 1, 1, 4, 4])
print(np.unique(names))
print(np.unique(ints))
#np.inld用于测试一个数组中的值在另一个数组中的成员资格,返回一个布尔型数组:
values = np.array([6, 0, 0, 3, 2, 5, 6])
print(np.in1d(values, [2, 3, 6]))
输出结果:
['Bob' 'Joe' 'Will']
[1 2 3 4]
[ True False False True True False True]
最后就是随机数的常用函数,都罗列在下面了:
函数 | 说明 |
seed | 确定随机数生成器的种子 |
permutation | 返回一个序列的随机排列或返回一个随机排列的范围 |
shuffle | 对一个序列就地随机排列 |
rand | 产生均匀分布的样本值 |
randint | 从给定的上下限范围内随机选取整数 |
randn | 产生正态分布(均值为0,方差为1)的样本值 |
binomial | 产生二项分布的样本值 |
normal | 产生正太分布的样本值 |
beta | 产生Beta分布的样本值 |
chisquare | 产生卡方分布的样本值 |
gamma | 产生Gamma分布的样本值 |
uniform | 产生再[0,1)中均匀分布的样本值 |
至此,我们numpy部分的内容就结束了