Numpy Learning

numpy可以理解为"number’s analysis of python"。简而言之,就是python专门用于数学科学处理的一个包,支持矩阵运算

本文简单介绍和总结numpy的基础使用方法。

  • Numpy Learning
  • 一.numpy基础
  • 1.numpy数组的创建以及array对象常用部分属性
  • 2.array数组提取元素
  • 3.numpy的数据类型
  • 4.array数组的形状改变
  • 5.array数组的组合
  • 6.array数组的分割
  • 7.array数组的属性
  • 8.array数组与python列表(list)的之间的转换

一.numpy基础

1.numpy数组的创建以及array对象常用部分属性

要了解numpy的基础,实际上就是要了解“矩阵运算”这一大功能。

矩阵运算包含了很多要素和知识点,比如说:数据类型、矩阵的创建和引用以及维度的提取和改变等等。

但是这一切的操作都是通过array这个数据类型来实现的,也就是我们说的数组

python把numpy数组里面的元素转成float numpy数组转化为int_数组

所以我们先从array这个数据对象开始.

# 创array对象--使用array函数可以将
# 指定的类array数据转换为array对象
import numpy as np

arr0=np.array([1,2,3,4])
print(arr0)
print(np.version.version)
print(arr0.dtype)
[1 2 3 4]
1.21.2
int32

可以看到在1.21.2的64位计算机中,如果使用numpy的array函数创建array对象,默认的数据类型为”“int64”。

官方函数文档给的解释是:

The desired data-type for the array.

If not given, then the type will be determined as

the minimum type required to hold the objects in the sequence.

翻译过来就是:

数组所需的数据类型。如果没有给出,那么类型将被确定为对象元素被存储在序列中的最小的类型。

那么如果想要制定array的数据类型呢?

arr0=np.array([1,2,3,4],dtype='float32')
print(arr0)
print(arr0.dtype)
[1. 2. 3. 4.]
float32

现在有一个问题:我们输入的object如果是一个矩阵,那么我如何才能确定这个矩阵成为array后的大小呢?难不成还得自己去记忆?并不是,可以通过array对象的一个属性shape来完成读取

arr0=np.array([1,2,3,4])
arr1=np.array([[1,2],[5,6],[3,6]])
arr2=np.array(1)
print("{}\n{}\n{}".format(arr0.shape,arr1.shape,arr2.shape))
(4,)
(3, 2)
()

这里有一个比较有意思的现象,假如说我输入的不是array_like的object,而是直接一个数字标量(scaler-维度为0),那么我们可以发现这个标量还是可以成为array,但是维度为0。

那么我就可以这样想:

1.维度为0:就是一个点,我们一般称作scaler-标量

2.维度为1:就是多个点构成的“一条”数据,但是他只有宽度,没有高度,我们一般称作vector-向量

3.维度为2:就是既有高度又有宽度,那就是一个平面了,我们一般称作matrix-矩阵

4.维度为3:相当于一个多面体了——多个平面在新的维度上无限重叠,称之为tensor-张量

这些概念是我们在python数据科学学习和深度学习中常用的概念。


Tips:其实vector可以看作matrix的特殊情况——把:
python把numpy数组里面的元素转成float numpy数组转化为int_数据类型_02
的矩阵称作向量。

线性代数中矩阵和向量可以看作同一个东西。


2.array数组提取元素

提取元素就是引用,array中的引用元素的方式跟python一样,这里不做过多解释:

1.格式:array[a:b][c]

2.如果是a:,:b,a:b,就表示提取一个范围的元素

3.如果知识单个数字就是提取特定数值

# arr3就是一个典型的rgb图数字化矩阵
arr3=np.random.rand(3,28,28)  
# 提取dim0单通道图片的数字矩阵
# print(arr3[0])
print(arr3[0].shape)
# 提取1-2通道数字矩阵
print(arr3[1:3])
print(arr3[1:3].shape)
# 提取dim2通道的(14,14)像素值
print(arr3[2][13][13])
print(type(arr3[2][13][13]))
(28, 28)
[[[0.48833191 0.49227286 0.88447359 ... 0.02841924 0.54437058 0.326423  ]
  [0.07196849 0.15764609 0.39646607 ... 0.46262122 0.27459863 0.59244376]
  [0.16523175 0.00289342 0.46085036 ... 0.12362236 0.75732681 0.61543985]
  ...
  [0.39153824 0.75012216 0.02920516 ... 0.99311179 0.25745318 0.38009034]
  [0.75869929 0.66871767 0.92249438 ... 0.48210471 0.88380983 0.05764281]
  [0.26983395 0.83233818 0.0950505  ... 0.16445969 0.60758993 0.3941144 ]]

 [[0.4411464  0.26172997 0.0181014  ... 0.23755507 0.85238282 0.52344811]
  [0.1808304  0.1239361  0.23753657 ... 0.54842241 0.30538224 0.85262662]
  [0.57752354 0.04522684 0.10117772 ... 0.675709   0.99090207 0.33322352]
  ...
  [0.67172909 0.90576698 0.59510418 ... 0.70290994 0.88010551 0.84262616]
  [0.66646581 0.99893419 0.98898849 ... 0.18653642 0.06164357 0.54193373]
  [0.74116007 0.9354973  0.5482605  ... 0.97091991 0.22159573 0.01893494]]]
(2, 28, 28)
0.6671540194279799
<class 'numpy.float64'>

Tips:就算是scaler数据,arr3[2][13][13]还是numpy数据哦,并不是python的基础数据类型——float


3.numpy的数据类型

python把numpy数组里面的元素转成float numpy数组转化为int_数据类型_03

# 每一种数据类型都有一个对应的转换函数
arr4=np.random.rand(3,28,28)  
print(arr4.dtype)
arr4=np.float32(arr4)
print(arr4.dtype)
float64
float32

Tips:

1.complex(复数)是不能转换成int(整型)的哦

2.complex也不能转换成float类型

3.但是float可以转换成complex,float数值充当complex指定部位数据


4.array数组的形状改变

一般来说我们不会单独使用numpy,而是会与其他一些包——比如说:Image、torch库联合使用从而完成某些特定任务。在torch中遵循的是“channel-first”原则,但是numpy没有特定要求,所以这就需要我们对数组形状进行改变。

arr5=np.random.randint(low=0,high=9,size=(5,5,3))

print(arr5.shape)
# ravel:完成展平操作,但是不会申请内存,只会返回一个视图
arr5.ravel()
print(arr5.ravel())
# 这里的展平看起来就是将数组一行一行拼接起来
print(arr5.shape)
# 这里就说明仅仅是返回view并不是原地修改
(5, 5, 3)
[7 3 3 5 8 8 7 0 0 8 8 8 2 5 0 3 6 2 6 4 1 5 4 4 6 2 4 7 0 2 3 5 4 5 4 4 7
 8 2 3 1 4 4 2 7 2 6 7 7 5 7 4 1 5 2 6 3 6 7 4 4 2 4 0 0 4 0 6 7 6 6 6 2 2
 2]
(5, 5, 3)
# flatten:功能与ravel一样但是会申请内存存储展平数据
print(arr5)
print(arr5.flatten())
[[[7 3 3]
  [5 8 8]
  [7 0 0]
  [8 8 8]
  [2 5 0]]

 [[3 6 2]
  [6 4 1]
  [5 4 4]
  [6 2 4]
  [7 0 2]]

 [[3 5 4]
  [5 4 4]
  [7 8 2]
  [3 1 4]
  [4 2 7]]

 [[2 6 7]
  [7 5 7]
  [4 1 5]
  [2 6 3]
  [6 7 4]]

 [[4 2 4]
  [0 0 4]
  [0 6 7]
  [6 6 6]
  [2 2 2]]]
[7 3 3 5 8 8 7 0 0 8 8 8 2 5 0 3 6 2 6 4 1 5 4 4 6 2 4 7 0 2 3 5 4 5 4 4 7
 8 2 3 1 4 4 2 7 2 6 7 7 5 7 4 1 5 2 6 3 6 7 4 4 2 4 0 0 4 0 6 7 6 6 6 2 2
 2]
# reshape:输入一个整型元组表示目标shape来完成数组形状的修改
print(arr5.shape)
arr5.reshape((3,5,5))
print(arr5.shape)
# 我们发现reshape并不是“原地修改”,而是有一个返回值。
arr6=arr5.reshape((3,5,5))
print(arr6.shape)
(5, 5, 3)
(5, 5, 3)
(3, 5, 5)
# 那么如果我想要完成原地修改呢?可以使用resize
print(arr5.shape)
arr5.resize(3,5,5)
print(arr5.shape)
(5, 5, 3)
(3, 5, 5)
# transpose函数:pytorch中也有这个函数,作用就是直接输入维度index来完成形状转换
arr6=np.random.randint(0,9,(5,))
print(arr6)
print(arr6.transpose())
# 看得出来transpose对于向量是没有作用的
[7 5 5 2 3]
[7 5 5 2 3]
arr7=np.random.randint(0,9,(5,5))
print(arr7)
print(arr7.transpose())
# 对于二维矩阵是完成了转置
[[2 3 6 5 4]
 [5 4 1 1 7]
 [3 4 5 8 3]
 [4 2 2 2 4]
 [4 2 4 6 5]]
[[2 5 3 4 4]
 [3 4 4 2 2]
 [6 1 5 2 4]
 [5 1 8 2 6]
 [4 7 3 4 5]]
arr8=np.random.randint(0,9,(3,4,2))
print(arr8.shape)
print(arr8.transpose().shape)
# 如果对于一个tensor就是交换0,2维度
(3, 4, 2)
(2, 4, 3)
arr8=np.random.randint(0,9,(3,4,2,1))
print(arr8.shape)
print(arr8.transpose().shape)
# 现在我们就可以发现transpose其实完成的就是一个维度对称交换
(3, 4, 2, 1)
(1, 2, 4, 3)

Tips:

值得一提的是如果我们对1.21.1的numpy.ndarray.transpose进行参数输入会发生报错

arr8.transpose(axes=[3,2,1,0])
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

~\AppData\Local\Temp/ipykernel_19128/3580215624.py in <module>
----> 1 arr8.transpose(axes=[3,2,1,0])


TypeError: transpose() takes no keyword arguments

但是如果使用numpy.transpose(ndarray)就不会发生这种问题:

print(arr8.shape)
print(np.transpose(arr8,[3,2,1,0]).shape)
(3, 4, 2, 1)
(1, 2, 4, 3)

总结一下:

1.如果使用numpy.ndarray.transpose方法:

(1)不能输入参数(version=1.21.1)

(2)完成维度的对称交换

2.如果使用numpy.transpose函数:

(1)能输入参数序列,这个序列就是目标输出的维度下标序列

eg:(输入维度序列:[0,1,2,3]->输出维度序列[3,2,1,0])

(2)可以完成维度的任意交换,实用性更强


5.array数组的组合

# 三种维度的组合:水平组合(hstack)、垂直组合(vstack)、深度组合(dstack)
a0=np.random.randint(0,9,(3,3))
a1=np.eye(3,dtype='int')    # 生产单位矩阵
print("a0:\n{}\n\na1:\n{}\n".format(a0,a1))
print('hstack:\n{}\n'.format(np.hstack([a0,a1])))
print('vstack:\n{}\n'.format(np.vstack([a0,a1])))
print('dstack:\n{}\n'.format(np.dstack([a0,a1])))
a0:
[[6 3 2]
 [1 2 4]
 [5 3 2]]

a1:
[[1 0 0]
 [0 1 0]
 [0 0 1]]

hstack:
[[6 3 2 1 0 0]
 [1 2 4 0 1 0]
 [5 3 2 0 0 1]]

vstack:
[[6 3 2]
 [1 2 4]
 [5 3 2]
 [1 0 0]
 [0 1 0]
 [0 0 1]]

dstack:
[[[6 1]
  [3 0]
  [2 0]]

 [[1 0]
  [2 1]
  [4 0]]

 [[5 0]
  [3 0]
  [2 1]]]

这三种维度的组合很好理解就跟拼积木一样,但是需要注意:

a0=np.random.randint(0,9,(3))
a1=np.random.randint(0,9,(3))
print(np.dstack([a0,a1]).shape)
(1, 3, 2)
a0=np.random.randint(0,9,(3,3))
a1=np.random.randint(0,9,(3,3))
print(np.dstack([a0,a1]).shape)
(3, 3, 2)
a0=np.random.randint(0,9,(3,3,1))
a1=np.random.randint(0,9,(3,3,1))
print(np.dstack([a0,a1]).shape)
(3, 3, 2)
a0=np.random.randint(0,9,(1,3,3))
a1=np.random.randint(0,9,(1,3,3))
print(np.dstack([a0,a1]).shape)
(1, 3, 6)
a0=np.random.randint(0,9,(3,3,1,1))
a1=np.random.randint(0,9,(3,3,1,1))
print(np.dstack([a0,a1]).shape)
(3, 3, 2, 1)
a0=np.random.randint(0,9,(3,3,1,1,1))
a1=np.random.randint(0,9,(3,3,1,1,1))
print(np.dstack([a0,a1]).shape)
(3, 3, 2, 1, 1)

看出来没有?所谓的深度组合实际上是指定了dim2——第三维度上的叠加(若没有第三维度,则进行reshape((h,w,1)))

接下来再介绍2种方向组合以及一个“万能组合函数”。

a0=np.random.randint(0,9,(4,5,3))
a1=np.random.randint(0,9,(4,5,3))

# 行组合
print(np.row_stack([a0,a1]).shape)  # 顾名思义就是将数组一行一行地叠加起来
# 列组合
print(np.column_stack([a0,a1]).shape)   # (同上)
(8, 5, 3)
(4, 10, 3)

我们发现这两个的效果和上面的vstack、hstack有点相似,但是我们得“极端一下”来验证验证:

a0=np.random.randint(0,9,(4,5,7,3,2,3,1))
a1=np.random.randint(0,9,(4,5,7,3,2,3,1))
print(np.row_stack([a0,a1]).shape)
print(np.column_stack([a0,a1]).shape)
(8, 5, 7, 3, 2, 3, 1)
(4, 10, 7, 3, 2, 3, 1)

可以看到,我们的猜想是对的。

实际上你会发现:这些函数设计出来其实就是方便不同应用场合的理解,但实际上它们就是一个函数——concatenate(融合)

a0=np.random.randint(0,9,(4,5,7,3,2,3,1))
a1=np.random.randint(0,9,(4,5,7,3,2,3,1))
dims=a0.ndim    # 读取维度数(a0.ndim=a1.ndim)
for i in range(dims):
    print(np.concatenate([a0,a1],axis=i).shape)
(8, 5, 7, 3, 2, 3, 1)
(4, 10, 7, 3, 2, 3, 1)
(4, 5, 14, 3, 2, 3, 1)
(4, 5, 7, 6, 2, 3, 1)
(4, 5, 7, 3, 4, 3, 1)
(4, 5, 7, 3, 2, 6, 1)
(4, 5, 7, 3, 2, 3, 2)

用法很简单,指定要组合的序列,再指定要融合的维度下标即可。

6.array数组的分割

有组合,那就有对应的分割。跟组合一样,其实也是有专门的函数和万能分割函数(split)的。

这里我直接上代码,理解方式同组合。

但是我们首先明确一点:split操作相较于concatenate操作,参数有变化:

1.split输入单个array+切片序列(indices_sequence)+可选的维度下标
2.concatenate输入array_sequence+可选维度下标

a2=np.random.randint(0,9,(8,4,3,5,6,3))
print("a2's shape:\n{}\n".format(a2.shape))
# 水平切割,将其切割成4个数组后形成的list变成array,再输出shape
print(np.array(np.hsplit(a2,4)).shape)
# 垂直切割(2个生成数组)
print(np.array(np.vsplit(a2,2)).shape)
# 深度切割
print(np.array(np.dsplit(a2,3)).shape)
a2's shape:
(8, 4, 3, 5, 6, 3)

(4, 8, 1, 3, 5, 6, 3)
(2, 4, 4, 3, 5, 6, 3)
(3, 8, 4, 1, 5, 6, 3)

现在介绍一下分割操作中的万能函数——split

list_array=np.array(np.split(a2,[2,4,5,6],axis=0))
for arr in list_array:
    print(arr.shape)
(2, 4, 3, 5, 6, 3)
(2, 4, 3, 5, 6, 3)
(1, 4, 3, 5, 6, 3)
(1, 4, 3, 5, 6, 3)
(2, 4, 3, 5, 6, 3)

可以看到,有三个参数:

1.输入数组:ary

2.切片序列:[a0,a1,a2,…,an]或者是一个可以被:ary.shape[axis]整除的数字n。

3.可选参数:axis(默认值为0)

如果切片序列是一个可以被:ary.shape[axis]整除的数字n:

那么会在下标为axis的维度上将数组平均分为n个小数组并存储到list中

如果切片序列是[a0,a1,a2,…,an]这样的一个有序的下标序列,那么会将输入数组在axis维度上按[:a0],[a0:a1],[a1:a2]…的规律分为一个个小数组并存储到list中

7.array数组的属性

a3=np.random.randint(0,9,(8,4,3,5,6,3))

1.维度数

print(a3.ndim)
print(len(a3.shape)==a3.ndim)
6
True

2.元素个数

print(a3.size)
count=8*4*3*5*6*3
print(count==a3.size)
8640
True

3.元素所占字节数和整个数组所占字节数

print(a3.itemsize)
print(a3.nbytes)
print(a3.nbytes==a3.size*a3.itemsize)   # nbytes:数组所占空间大小(bit)
4
34560
True

4.Transpose

a3_t=a3.T
print(a3.shape)
print(a3_t.shape)
(8, 4, 3, 5, 6, 3)
(3, 6, 5, 3, 4, 8)

5.复数的实数、虚数读取

comp=np.array([1.j+1,2.j+3])    # numpy的复数的虚部用j表示
print(comp)
print(comp.real)
print(comp.imag)
[1.+1.j 3.+2.j]
[1. 3.]
[1. 2.]

6.扁平迭代器(flat_iter)


Tips:这是实例化flatiter对象的唯一方式.flatiter的构造函数无法访问


# 起作用就是让我们可以以遍历一维数组的方式遍历多维数组
a3=np.random.randint(0,9,(4,5))
print(a3)
sum=0
for item in a3.flat:
    if sum%5==0:
        print("\n")
    print(item,end=' ')
    sum+=1
[[7 3 1 5 3]
 [4 3 2 4 7]
 [7 0 7 1 3]
 [3 5 6 0 2]]


7 3 1 5 3 

4 3 2 4 7 

7 0 7 1 3 

3 5 6 0 2
# 同时flat属性可以赋值
a3.flat=1
print(a3)
# 这会导致flat全部元素直接被覆盖,从而完成原地修改
[[1 1 1 1 1]
 [1 1 1 1 1]
 [1 1 1 1 1]
 [1 1 1 1 1]]

总结一下:

python把numpy数组里面的元素转成float numpy数组转化为int_python_04

8.array数组与python列表(list)的之间的转换

# 使用tolist和astype函数

a3.astype(int)
list3=a3.tolist()
print(list3)
[[1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1]]