Numpy ndarry:多维数组对象

Numpy的核心特征之一就是ndarray
一个ndarray是一个通用的多维同类数据容器,也即每一个元素均为相同类型。每一个数组都有一个shape属性,用来表征数组每一纬度的数量。每一个数组都有一个dtype属性,用来描述数组的数据类型

1
2
data.shape ==> (2,3)
data.dtype ==> dtype('float64')

生成 ndarrays

生成数组最简单的方式就是使用array函数。array可以接收任何序列型对象(也包括其他数组),生成一个新的包含传递数据的Numpy数组

1
2
3
4
data = [[1,2,3,4],[5,6,7,8]]
arr = np.array(data)
print(arr)
==> [[1,2,3,4],[5,6,7,8]]

我们可以通过ndimshape属性来验证

1
2
arr.ndim ==> 2
arr.shape ==>(2,4)

np.array 会自动推断生成数组的数据类型,数据类型被保存在特殊的元数据dtype

load failed

ndarray的数据类型

dtype是一个特殊的对象,它包含了ndarray需要为某一种类型数据所声明的内存信息(即数据的数据)。

1
arr = np.array([1,2,3], dtype=np.floart(64))

可用astype方法显式地转换数组的数据类型(可用于将含数字字符串变为数字)

1
2
3
4
arr = np.array([1,2,3,4,5])
print(arr.dtype) ==>int64
arr2 = arr.astype(np.float64)
print(arr2.dtype) ==>float64

np.float处也可以替换为其他数组的dtype类型

注意:astype时总是生成一个新的数组

numpy数组算术

numpy允许进行批量化操作而无需任何for循环,称这种特性为向量化

1
2
arr = np.array([[1. ,2. ,3.],[4. ,5. ,6.]])
print(arr*arr) ==> [[1.,4.,9.],[16.,25.,36.]]

带有标量计算的算术操作会把值传递给每一个元素

1
2
3
arr = np.array([[1. ,2. ,3.],[4. ,5. ,6.]])
print(arr ** 0.5) ==>[[1. 1.41421356 1.73205081]
[2. 2.23606798 2.44948974]]

同理同尺寸数组之间比较会产生一个布尔值数组。而不同尺寸的比较需要用到广播特性,将会在其他文章中的讲到。

基础索引与切片

数组可以像列表那样进行切片,但区别于python内建链表的是数组的切片是原数组的视图这意味着数组不会被复制,任何对于视图的修改都会反映到原数组上

1
2
3
4
5
arr = np.arange(10)
arr_slice = arr[5:8]
print(arr_slice) ==> [5,6,7]
arr_slice[1]=12345
print(arr) ==>[0,1,2,3,4,5,12345,7,8,9]

如果只是想要一份数组切片的拷贝的话就必须使用arr[5:8]显式地复制这个数组

对于多维数组,切片意味着切下低一维的元素

1
2
3
4
arr = np.array([[1,2,3],
[4,5,6],
[7,8,9]])
print(arr[:2]) ==> [[1, 2, 3], [4, 5, 6]]

如果将索引和切片混合使用则可以得到低纬度的元素

1
2
3
4
arr = np.array([[1,2,3],
[4,5,6],
[7,8,9]])
print(arr[1, :2]) ==> [4, 5]

注意:单独一个冒号表示整个轴上的数组,对切片表达式赋值的时候,整个切片都会重新赋值。

布尔索引

布尔索引将广泛用于数据处理中,例如我们有:

1
2
3
4
5
6
7
8
9
names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
data = np.random.randn(7,4)
==>[[-1.90435897 0.6234921 -1.96570929 2.38031689]
[ 0.38075269 0.06411856 0.32644763 0.97140279]
[-0.31573477 2.8429299 0.35218581 -0.58226666]
[-0.24289945 -0.63850929 -0.9746088 -0.45977418]
[ 1.0430502 -1.45662082 -0.41700062 -0.92820579]
[-0.35345436 0.33968872 0.01477487 1.63924287]
[-0.27604883 0.67801237 -0.62261439 -1.33900017]]

如果说每一个姓名对应于一行数据,那么当我们在筛选的时候使用

1
2
print(names=='Bob')
==>[ True False False True False False False]

这里就生成了一个布尔索引数组,用于指明匹配情况,于是就可以用到我们的数组上面去了

1
2
3
print(data[names=='Bob'])
==>[[-0.17055152 0.34287196 1.23624302 0.82019015]
[ 0.55839142 -0.68480323 -2.85594084 1.16463299]]

这里就把和数据集中和Bob对应的数据筛选出来了

如果要反选可以使用,比如:

1
2
cond = names =='Bob'
print(data[~cond])

注意:1、使用布尔值索引选择数据时,总是生成数据的拷贝 2、python关键字and和or无效,应使用& 和 |

神奇索引

神奇索引是numpy中的术语,用于描述使用整数数组进行数据索引

1
2
3
4
5
6
7
8
arr = np.empty((8,4))
for i in range(8):
arr[i] = i
print(arr[[4,3,5,6]])
==>[[4. 4. 4. 4.]
[3. 3. 3. 3.]
[5. 5. 5. 5.]
[6. 6. 6. 6.]]

这里的4,3,5,6就是指定在arr中的行数,如果索引为负则会从尾部选择

注意:神奇索引与切片不同,它总是将数据复制到一个新的数组中

数组的转置和换轴

转置是一种特殊的数据重组形式,可以返回底层数据的视图而不需要复制任何内容。数组拥有transpose方法,也有特殊的T属性

1
2
3
4
5
6
7
8
9
10
11
arr = np.arange(15).reshape((3,5))
print(arr) ==>
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]]
print(arr.T) ==>
[[ 0 5 10]
[ 1 6 11]
[ 2 7 12]
[ 3 8 13]
[ 4 9 14]]

这里将横轴数轴进行了调换

对于更高维度的数组,transpose方法可以接收包含轴编号的元组:

1
2
3
4
5
6
7
8
9
10
11
12
arr = np.arange(16).reshape((2,2,4))
print(arr) ==>
[[[ 0 1 2 3]
[ 4 5 6 7]]
[[ 8 9 10 11]
[12 13 14 15]]]
arr.transpose((1,0,2))
print(arr) ==>
[[[ 0 1 2 3]
[ 4 5 6 7]]
[[ 8 9 10 11]
[12 13 14 15]]]

这里的数据构于三维空间,0 1 2 3这样的四组数据沿第三轴分布。这里(0,1,2)表示将1,2轴进行互换,于是就得到了上面的结果。

还有一个swapaxes可用于接收一对轴号并对其进行重组,例如:arr.swapaxes(1,2)。这里不再给出具体例子。

注意:swapaxes返回的也是视图,并没有对数据进行复制

Author: YihangBao
Link: https://roarboil.github.io/2019/09/02/ndarray/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.