Python库第一课:基础Numpy知识(上):数组

        这两日,温习了python的基础知识,在编程的时候,如果忘记了,便可快速查看,以达到重复复习的效果。记录自己的学习工程是一个好事情,不仅能快速复习并加深印象,还可以给后面学习的朋友带来灵感,或许就是某个问题,或者某个处理方式,给很多人节约分钟甚至数天的时间。

        今日,我将温习一下专题python库中的Numpy库,这个库是我们课题组的重点数据库之一,对于特殊专题的课程,不会像基础课程一般按部就班各个知识都涉及一点,往往都是最基础的入门一做,就带入项目,在实践中进步。

        对于Numpy以及所有库的学习,官方文件是一个极好的途径,然而,官方就算是基础知识,都是需要一定的门槛的,所以,我将官方的入门作为明日的特殊专题基础课程,今日采用的书籍是董付国编写的《Python数据分析、挖掘与可视化》中这本书的Numpy部分。

        由于是计算,这本书对于运算列举极为详细,本章节十分适合基础。然而,正因为是计算,所以我们需要学习一下《线性代数》矩阵行列式最基础的计算,这一点,线性代数的学习才是最基础的。由于本人刚从考研上退下来,线性代数记忆还比较深刻,因此,直接从Numpy的矩阵开始温习。

        说实话,Numpy这个库,也是老熟人了,打了两年的交道,基础的用法脑子里还是比较清晰的,但更深层的应用还是需要加强的。

        本章知识需要有一定的线性代数知识和python基础方可完全理解。Numpy中有两大重要成员——数组、矩阵。本节学习Numpy中的两大重要成员之一,数组。

一、Numpy的调用

        接下来,特殊专栏Numpy库的基础开始。

        首先,我们可以在Numpy的官方文件看到这样一句总体介绍:

        NumPy是Python中科学计算的基础包。它是一个Python库,提供多维数组对象,各种派生对象(如掩码数组和矩阵),以及用于数组快速操作的各种API,有包括数学、逻辑、形状操作、排序、选择、输入输出、离散傅立叶变换、基本线性代数,基本统计运算和随机模拟等等。

        因此,我们基础的内容主要为数学计算与数学的线性代数的的运算,深入学习之后,还能使用各种内置模型,尤其是数学建模中Numpy的贡献极大。

        本书中需要掌握的内容为:Numpy的数组计算、矩阵乘法、数据相关系数与方差等、计算特征值与特征向量、计算矩阵的逆(矩阵的痛苦呀,算错整个答题扣完!)、求解线性方程、计算向量和矩阵范数、理解奇异值分解。

        对于此节内容出现的import,为导入python第三方库,由于python默认没有安装Numpy库,因此,需要我们在终端面板上,也就是Terminal面板进行安装,我一般选择用国内清华源镜像安装,安装方式为:

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple some-package

        需要使用哪种包就将some-package换成包名即可。调用的方式为:

import numpy as np

        其实你as为任何东西都可以,只不过是数据分析中常用的三剑客numpy—>np,   pandas>pd, 这等于是一种俗成的约定。  还有matlab库,但matlab库内部太多,每次都采用from中调用需要的函数,这里不做介绍。

二、运用numpy函数生成基础数组

        在实际处理数据时,会用到大量的数组和矩阵运算,这里,将列出numpy所能涉及的基础数组和矩阵,没有捷径,多记多练,方可记住,程序这个东西是一个记忆力的问题,多用才会习惯。

import numpy as np


# 将列表转换为数组
a = np.array([1, 2, 3, 4, 5])
print(a)
# 将元组转换为数组
b = np.array((1, 2, 3, 4, 5))
print(b)
# 将range数组转换为数组
c = np.array(range(5))
print(c)
# 二维数组
d = np.array([[1, 2, 3], [4, 5, 6]])
print(d)
# 从0生成数组的arange函数
e = np.arange(8)
print(e)
# 可以看出该函数内部含有三个基本参数
# 分别为开始的数字(包含),结束的数字(不含),间隔
f = np.arange(start=1, stop=10, step=2,)
print(f)
# 等差数组
g = np.linspace(0, 10, 11, endpoint=False)
print(g)
# 底数为10的对应等差数组方(10**linspace)
h = np.logspace(0, 10, 11)
print(h)
# 自定义底数为2
i_2 = np.logspace(0, 10, 11, base=2)
print(i_2)
i_3 = np.logspace(0, 10, 11, base=2, endpoint=False,)
print(i_3)


# 全0的一维数组
j = np.zeros(3)
print(j)
# 全1的一维数组
k = np.ones(3)
print(k)
# 全0的二维数组(3x3)
m = np.zeros(shape=(3, 3))
print(m)
# 全0二维数组(3x1)
n = np.zeros((3, 1))
print(n)


# 单位矩阵
p = np.identity(3)
print(p)
# 生成空数组
q = np.empty((4, 4))
print(q)


# Hamming窗口
r = np.hamming(5)
print(r)
# Blackman窗口
s = np.blackman(13)
print(s)
# kaiser窗口
t = np.kaiser(12, 5)
print(t)


# 生成随机数组
u = np.random.randint(0, 50, size=(2, 5, 3))
print(u)
v = np.random.randint(0, 50, size=(3, 5))
print(v)
# 生成0-1之间的随机数
w = np.random.rand(12)
print(w)
# 从标准正态分布中随机找5个数
x = np.random.standard_normal(size=(2, 3, 4))
print(x)

# 生成对角矩阵
y = np.diag(v=[1, 8, 7, 5])
print(y)
# 对角线下移
z = np.diag(v=[1, 8, 7, 5], k=-1)
print(z)

        这些基础的数组应当熟练掌握,其所有的答案分别为:

a
[1 2 3 4 5]
b
[1 2 3 4 5]
c
[0 1 2 3 4]
d
[[1 2 3]
 [4 5 6]]
e
[0 1 2 3 4 5 6 7]
f
[1 3 5 7 9]
g
[0.         0.90909091 1.81818182 2.72727273 3.63636364 4.54545455    
 5.45454545 6.36363636 7.27272727 8.18181818 9.09090909]
h
[1.e+00 1.e+01 1.e+02 1.e+03 1.e+04 1.e+05 1.e+06 1.e+07 1.e+08 1.e+09
 1.e+10]
i2
[1.000e+00 2.000e+00 4.000e+00 8.000e+00 1.600e+01 3.200e+01 6.400e+01
 1.280e+02 2.560e+02 5.120e+02 1.024e+03]
i3
[  1.           1.87786182   3.52636502   6.62202624  12.43525025
  23.35168169  43.85123151  82.34655347 154.63544888 290.38400568
 545.30103779]
j
[0. 0. 0.]
k
[1. 1. 1.]
m
[[0. 0. 0.]
 [0. 0. 0.]
 [0. 0. 0.]]
n
[[0.]
 [0.]
 [0.]]
p
[[1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]]
q
[[0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]]
r
[0.08 0.54 1.   0.54 0.08]
s
[-1.38777878e-17  2.69872981e-02  1.30000000e-01  3.40000000e-01
  6.30000000e-01  8.93012702e-01  1.00000000e+00  8.93012702e-01
  6.30000000e-01  3.40000000e-01  1.30000000e-01  2.69872981e-02
 -1.38777878e-17]
t
[0.03671089 0.16199525 0.36683806 0.61609304 0.84458838 0.98167828
 0.98167828 0.84458838 0.61609304 0.36683806 0.16199525 0.03671089]
u
[[[38 29 12]
  [41 14  2]
  [44 25 33]
  [32 10  4]
  [25 12 24]]

 [[47 34 23]
  [28 17 28]
  [ 6 23 14]
  [ 1 18 48]
  [45 40  7]]]
v
[[36 24 39 42 31]
 [ 9 48 35 11  9]
 [43  4  5 39 29]]
w
[0.5843105  0.75747057 0.61130295 0.03738209 0.53470112 0.05576978
 0.49353422 0.00518441 0.46670852 0.08334915 0.06009784 0.20298728]
x
[[[ 0.04817853  0.25735623 -0.34895119 -0.23421412]
  [-1.06543704 -3.08229685 -0.79797196  0.24007462]
  [-0.91013329  0.53839569  0.68694889 -0.57340191]]

 [[-1.72272241 -0.72050414  2.00270342  1.11692233]
  [-0.68038513 -0.40099731 -1.59659828  0.9263204 ]
  [ 2.53093823  1.07188614  0.48997709  0.06527654]]]
y
[[1 0 0 0]
 [0 8 0 0]
 [0 0 7 0]
 [0 0 0 5]]
z
[[0 0 0 0 0]
 [1 0 0 0 0]
 [0 8 0 0 0]
 [0 0 7 0 0]
 [0 0 0 5 0]]

二、数组的比较

        数组比较通常采用两种方案,全局比较函数(allclose),对应位置比较函数(isclose),数组之间的而比较必须满足同种形状,否则在比较之时便会报错,其产生的错误为:

Traceback (most recent call last):
  File "d:\PythonLearn\PythonWork\专题Numpy基础12.4.py", line 92, in <module>
    a=np.allclose(one_arr,two_arr)
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\Python312\Lib\site-packages\numpy\core\numeric.py", line 2241, in allclose
    res = all(isclose(a, b, rtol=rtol, atol=atol, equal_nan=equal_nan))
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\Python312\Lib\site-packages\numpy\core\numeric.py", line 2351, in isclose
    return within_tol(x, y, atol, rtol)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\Python312\Lib\site-packages\numpy\core\numeric.py", line 2332, in within_tol
    return less_equal(abs(x-y), atol + rtol * abs(y))
                          ~^~
ValueError: operands could not be broadcast together with shapes (5,) (4,)

        我们可以看到,他给的错误原因为92行代码中除了问题,其主要违规了Numpy的下面三行中的规则,看不懂怎么办,不要慌,我们目前阶段可以直接看Error:他说,这个操作不能一起传播进行比较,也就是他们的形状是不同的。

        因此,两者比较务必在同一形状的前提下使用。具体的使用如下,我们对整个数组进行比较、整体相对误差的比较、整体绝对误差的比较、个元素对应比较。

# 整体比较
print(np.allclose(one_arr,two_arr))
# 相对误差比较
print(np.allclose(one_arr,two_arr,rtol=0.2))
# 绝对误差比较
print(np.allclose(one_arr,two_arr,atol=0.2))
# 局部对应比较
print(np.isclose(one_arr,two_arr))

        其返回的具体结果如下:

False
False
True
True
[ True False  True False  True]

        清晰可见!

三、修改数组的形状

        考虑到后面出现的问题都会引出一个改变原形状的函数出现。每当我们用range函数生成一个很长的一维数组的狮虎,涉及到需要将其形状改变加以后续操作。改变形状numpy涉及到桑额常用函数,分别为shape()、reshape()、resize()。

        shape和resize他们的不同点在于reshape的原数是不改变的,如果全新的元素个数与初始的不同便会产生错误。而resize的形状改变可以不与原始个数相等。

        在这里,使用三种表达法,第一种是不改变原始数组,采用全新变量承接,两者可替换。

# 改变函数的形状
diff_arr = np.array(range(9))
# reshape和resize对原数组不改变
diff_arr_2 = diff_arr.reshape(3, 3)
diff_arr_3 = np.resize(diff_arr, (4, 3))

        可看到,resize可以不等,输出的结果为会自动根据前排元素填充,而reshape必须要相等,不然会报错。

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

        接下来,再学习一种会改变原始数组的改变方法,具体格式如下:

# shape改变原数组
diff_arr = np.array(range(9))
diff_arr.shape = 3, 3
# resize改变原数组
diff_arr = np.array(range(9))
diff_arr.resize(3, 3)
# reshape改变原数组
diff_arr = np.array(range(9))
diff_arr.reshape(3, 3)

         其输出的结果均为:[[0 1 2] \n [4 5 6] \n [7 8 9]]。其中是有微小区别的。一般我习惯于采用改变原数组的reshape,有助于辨别是否出错,因为改变形状意味着原数组的函数不可达成此操作,添加此步骤,最最初是的数组是我们不需要的。

四、修改单维数组元素的值

        数组元素的而修改既接受对列表的系列操作,也可以用下标直接修改一个或多个元素的值,第二种改下标的方式类似于修改字典的索引key,只不过对于数组索引的仅仅只能为位置罢了。

        对于数组的修改与列表等不同的在于,通过函数的修改对原数组是不影响的,因此,需要用全新的变量来保存新产生的数组。

# 修改向量数组单个元素
sim_arr=np.array(range(9))
print(sim_arr)
# 举一个简单的例子说明
tset_arr=np.append(sim_arr,[10,11])
print(tset_arr)
print(sim_arr)
# 对单个元素下标方式修改
sim_arr[2]=8
print(sim_arr)

        而对于通过下标修改,等于在原式子上进行修改,所以会产生影响,原数组发生改变,上述结果输出如下:

[0 1 2 3 4 5 6 7 8]
[ 0  1  2  3  4  5  6  7  8 10 11]
[0 1 2 3 4 5 6 7 8]
[0 1 8 3 4 5 6 7 8]

        由此,简单的数组修改到此便完成。接下来,针对多维数组,简单的下标是无法处理的,而又不能使用逐层的定位,因为数组整体被默认为是一整个部分。

        这个时候,我们就得换一个想法了,在一维中采用线定位的方式,只需要提供一个单维坐标即可。而多维向量相当于横排数列的向量。因此,在这里,我们将修改单维与多维分开处理,在对多维数组进行处理之前我们需要学习一下对于函数的索引。

五、多维数组切片与修改

 1、多维数组切片

        上一部分所说的单维元素的修改是极其简单的,类同于字典的索引,仅仅定义其位置便可以了。那么对于多维数组,我们定位其中一个或者多个元素,俗称为“切片”。就是将横排数列的元素按照位置选定某个或者多个元素,返回一个数组。

        对于单元素切片采用的定位法则为(1)平面单次定位或者(1)逐层深入定位。

# 区别修改元素与定位元素
diff_arr=np.array(range(9));print(diff_arr)
diff_arr=diff_arr.reshape(3,3);print(diff_arr)
# 按位置索引其多维数组的元素
print(diff_arr[1,1],diff_arr[1][1])

        从图中可以看到,这里为了节约行数,用分号表示下一行,如果采用都好,表示还在这一行函数,那么这个变量等于还没有被赋值。变量赋值的规则为该行代码结束,值被赋予进去。最后一行代码为索引方式,对于输出的结果:

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

        两种索引方式都是适合的,你可以直接选择平面单次定位,也可以选择逐层进入数组。

        在切片时候,我们索引元素的时候同样采用位置,不过这里使用的不是单个位置,而是从第几行到第几行,第几列到第几列。如下是索引3x3数组的第一行:

print(diff_arr[0])

        输出的结果为:[0 1 2]。而对于某个多行多列的索引,完善[x,y]的坐标即可,x和y可以是既定的位置,也可以是某个从a:b的范围内,包含a不含b,因此,上述方括号可以写成[0:1]。

        以下是针对矩形输出:

# 索引前两行元素
print(diff_arr[0:2,:]);print(diff_arr[:2])

        其两者的输出结果是完全一致的[[0 1 2] \n[3 4 5]]。由此,我们可以发现,对于此索引,其实是对x和y的范围进行一个限定。:表示从开始到结束,可省略,因为本就是从第一列到最后一列。

# 区别易错点
print(diff_arr[0,1]);print(diff_arr[[0,1]])

        接下来,这里是一个切片所会经常犯的毛病与基础知识掌握不牢固。看到上面这个,难免会认为输出的结果一致,然而,最终的结果却为:

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

        为什么会这样呢?仔细一看,会发现第二个函数多一个中括号,因此,第二个函数的完整版本应当是print(diff_arr[[0,1],0:3]),而第一个本身就没有省略,按照上面的索引规则,oh瞬间就明白了,它取的是第一行的前三列和第二行的前三列。

        我们可以这样看,当其取行的就是一条横线,其取列的就在对应列画竖线,由此,将交点全部组合起来,便是最佳答案。

        那么,根据这个逻辑,就会发现我们接下来的独立元素索引方式,采用的正式指定特殊的x列和y列,取其中的交叉值。

# 取独立的元素
depen_arr = np.array(range(9));depen_arr.shape = 3, 3
print(depen_arr[[0,2,1],[2,1,0]])

        输出的结果是否为全部呢?按照以上逻辑,取了三行三列,应当是全局呀!

        然而,答案输出为[2 7 3],可以看出,其x与y坐标呈现了一一对应的关系。

        在这里有一个值得注意的地方为,这种x与y轴的绝对关系只能定位一次,这也就是为什么3行3列最终只产生三个元素的原因,本质是它是第一行第三列做一次交叉,后续的对应做一次交叉,实质上多余的交线是没有产生交点的,甚至如此方案取几个离散点,元素务必一一对应,缺一个或者多一个均不可。

        那么,有没有将所有交叉元素都取出来的方法呢?

        其实上述已经介绍,那就是对一个轴运用离散点,另一个轴运用连续点,方可取出连续的段落。然而,这样取出来的片段仍然是部分存在连续的部分。有没有一种取出所有交叉离散点的方法呢?

        接下来,将继续介绍一种,组合所有交叉离散点。也就是我们所采用的二次进入方法,两个方括号,表示程序将第一个方括号的内容找到之后,再在这基础上继续接入第二个程序进行离散切片。

# 取所有离散交叉点元素组合
print(depen_arr[[0,2],][:,[2,1]])

        其输出的结果为:

[[2 1] 
 [8 7]]

        由此可见,其含义解释为首先第一步取第1行和第3行的两条横线,y轴的0:3以及前面的逗号完全省略掉了。第二步是在第一步取全部横排的基础上,基础不能省略,再取第3列和第2列组合。

 2、多维数组修改

        有了以上的切片基础,我们的多维数组的切片几乎就已经告一段落了,这里仅仅列出单个修改。

# 平面单次切片(离散点)
mult_arr[[0, 2], [0, 2]] = 999
print(mult_arr)

        其输出的结果为:

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

        在这里值得注意的是,零散的点以目前的掌握知识是不能够全局对应更换数组的,修改数组的前提要么指定数值可以零散修改,要么就一个一个零散点慢慢改吧。

六、数组的运算

 1、数组的排序

        运算前,我们先学习一下数组的排序,首先是argsort()函数,他将原数组的数字排序并返回其位置坐标,往往与数组x[_]返回其对应坐标的数值搭配使用。

# 数组的排序
sor_arr = np.array([2, 9, 1, 6])
# 排序后返回原下标
x = np.argsort(sor_arr) ; print(x)
# 排序后返回原数值
y = sor_arr[np.argsort(sor_arr)] ; print(y)

         其输出的结果分别为:[2 0 3 1]、[1 2 6 9]。除此之外,可在多维数组中按照下标同理,阮昭原数值,也是同类操作,只列举下标排序:

# 多维数组的排序排列
sor_arr = np.array([2, 9, 1, 6])
sor_arr.shape = 2, 2
z = sor_arr.argsort(axis=0) ; print(z)

        结果为 [[1 1] [0 0]]。

 2、数组与标量的计算

        此运算是最为简单的单列(行)矩阵,在线性代数中叫做向量,向量做任何运算,均为所有元素独立运算,不影响其他元素,且每个元素均同时做同样运算,不做列举。

 3、数组与数组运算

        我们知道就按单向量的运算中,也就是数组的运算中,应当是对应元素与对应元素相互运算,并生成新的运算,无论是:加、减、乘、除、幂运算,皆是如此。因此,这里对等长的同纬度数组运算不做任何列举。仅在下面列举一下广播数组。

        广播数组,是一维数组与同等长度的多维数组之间的乘积,无论乘积先后,计算方法为一维数组中的每一个元素数乘多维数组每一列的元素,并在对应位置生成新的元素。这一点其实也十分方便理解,首先我们彻底将数组与矩阵分开,两者的运算天壤之别,一定要区分开。

4、数组的内积运算

        数组的内积运算,其实就是对应元素相乘的和,得到的是一个数字,其公式为:

x.y=\sum_{i=1}^{n}x_{i}.y_{i}

        内积函数为dot,其计算如下:

in_mult = np.dot(a, a) ; print(in_mult)

        其输出的值为14。

 5、numpy中内置函数计算

        对于数组的计算,仍然是每个元素做相应的运算,这里介绍几种常规的np的运算函数,比初始的math的函数计算更快。sin,cos,round(四舍五入),ceil(向上取整),floor(向下取整),无比前面加入np.使用,毕竟我们通常都as为np,用包要导入其前缀。

 6、数组布尔运算

        布尔运算为比较运算,这里一数组x举例,若整个数组大于1(即x>1),返回同样大小的数组,数组内的元素变为False或者True,取决于正确与否。若是数组内部大于1(即x[x>1])这个函数生成的是符合条件的元素组成新的数组。

        还有全部条件np.all(x>1),全部元素满足即返回True,否则返回False。对于其他的,any则表示存在其一则满足True。

        对于同型数组比较,则对应位置返回布判定结果。而不同型的数组不可进行比较。

 7、分段函数

        针对分段函数的处理,采用wher函数和piecewise函数。

        where函数的用法为where(x,condlist,a,b),满足条件的输出a,不满足条件的输出b。piecewise(x,[condition1,condition2],[a,b]),该函数满足条件则对应输出,其好处在于条件是无限的,以下为例子:

x = np . random . randint(0, 10, size=(1, 10))
np . where(x < 5, 0, 1)
print(x)
x . resize((2, 5))
# 小于4的元素乘以2
# 大于7的元素乘以3
# 其他元素变为0
np . piecewise(x, [x < 4, x > 7], [lambda x:x*2, lambda x:x*4])
print(x)
# 小于3的元素变为﹣1
# 大于3小于5的元素变为1
# 大于7的元素乘以4,条件没有覆盖到的其他元素变为0
np . piecewise(x, [x < 3, (3 < x) & (x < 5), x > 7], [-1, 1, lambda x: x * 4])
print(x)

        其输出结果为:

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

 8、数组的堆叠

        堆叠十分简单,水平堆叠采用hstack()函数,即在数组右侧组合新数组增加列。垂直堆叠采用vstack()函数,即在数组下面进行组合新数组,增加行。concatenate()函数,默认按行合并,也就是合并时增加行数,axis=1时,为按照列合并,即增加列。也就是axis=1时为水平堆叠,为0时为垂直堆叠。

        到此,我们的Numpy初步学习暂时到这里,我将本次的学习分为上集和下集,下集将学习矩阵的使用,此部分内容主要涉及到线性代数,因此,代码部分比较简单,只需要认识部分函数即可。在明天学习完毕之后,矩阵学习部分将会发出,点滴积累,铸成江河。

  • 44
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值