导入numpy
import numpy as np
Numpy最重要的一个特点就是它可以快速地创建一个N维数组对象,即numpy.ndarray。(本专栏ndarray对象和数组并不做概念上的区分)。
然后你可以利用ndarray这种数据结构非常高效地执行一些数学运算,并且语法风格和Python基本一致。
一、创建数组
1、用np.array()创建数组
通过array函数创建ndarray对象,即将python数组转换为ndarray对象
(1)创建一维数组
ndarray是一个通用的同构数据
多维容器,其中的所有的元素必须是相同的类型,numpy会根据输入的实际情况进行转换。
data0 = [2, 4, 6.5, 8]
arr0 = np.array(data0)
(2)创建多维数组
data1 = [[1, 2, 3, 4], [5, 6, 7, 8]]
arr1 = np.array(data1)
(3)查看创建的arr0的变量类型
type(arr0)
(4)判断是否是ndarray类型
isinstance(arr0, np.ndarray)
(5)利用dtype关键字,传入合适的数据类型,显式地定义
arr2 = np.array(data1, dtype=np.float)
dtype的类型
np.float
np.int
np.str S1
np.unicode U1
np.bool
np.complex
(6)更改数组的数据类型
arr2.astype(np.int)
(7)数组的格式化输出
#对浮点数数组,保留3位有效数字,并禁用科学计数法;小数位数不够,后面不会补0
arr3 = np.array([3.141592653, 9.8], dtype=np.float16) # 定义一个一维数组
np.set_printoptions(precision=3, suppress=True)
2、创建数组的其他方式
(1)创建一个大小为10的全0数组
np.zeros(10, dtype=np.int)
(2)创建一个大小为5的全1数组
np.ones(5, dtype=np.int)
(3)创建空的(2,3)二维数组
np.empty((2, 3), dtype=np.int)
(4)创建一个大小为3×3的单位矩阵
# np.identity(n, dtype=<type ‘float’>)
np.identity(3, dtype=np.int)
Out: array([[1, 0, 0],
[0, 1, 0],
[0, 0, 1]], dtype=int8)
(5)创建3×4的矩形矩阵,并且为1的对角线向右偏移1个单位。
# np.eye(N, M=None, k=0, dtype=float, order='C')
np.eye(N=3, M=4, k=1, dtype=np.int)
Out: array([[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]], dtype=int)
3、创建等差数组
(1)np.arange(start,stop,step)
指定 start、stop、以及step。arange和range一样,是左闭右开的区间。
也可以只传入一个参数,这种情况下默认start=0,step=1
np.arange(10)
np.arange(1, 10, 1)
np.arange(1.2, 3.8, 0.3)
(2)np.linspace(start, stop[, num=50[, endpoint=True[, retstep=False[, dtype=None]]]]])
start、stop参数,和arange()中一致;
num为待创建的数组中的元素的个数,默认为50
endpoint=True,则为左闭右闭区间,默认为True;endpoint=False,则为左闭右开区间
retstep用来控制返回值的形式。默认为False,返回数组;若为True,则返回由数组和步长组成的元祖
arr_linspace = np.linspace(1, 99, 11, endpoint=False)
(3)ndarray.reshape()
这里定义了一个长度为20的等差数组,然后通过reshape方法,调整数组的大小为5×4
arr_linspace.reshape(5, 4)
4、创建等比数组
(3)geomspace()方法,创建指数等比数列
np.geomspace(start, stop, num=50, endpoint=True, dtype=None)
start和stop,分别为区间的起始和终止值,为强制参数;
num 为待生成等比数列的长度,指定后,程序会自动计算取等比数列的公比;
endpoint默认为True,结果为左闭右必区间。否则为False,左闭右开区间;
np.geomspace(2, 16, 4)
(4)logspace()方法,创建对数等比数列
np.logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None)
start:区间起始值为base的start次方
stop:区间终止值为base的stop次方(是否取得到,需要设定参数endpoint)
num:为待生成等比数列的长度。按照对数,即start和stop值进行等分。默认值为50
endpoint:若为True(默认),则可以取到区间终止值,即左闭右闭区间,规则同上
np.logspace(1, 4, num=4, base=2)
4、创建随机数数组
(1)np.random.rand()方法,创建[0, 1)之间的均匀分布的随机数组
np.random.rand(d0, d1, ..., dn)
函数的输入为若干个整数,表示输出随机数的大小为d0×d1× ...×dn
如果没有参数输入,则返回一个float型的随机数
# 产生一个大小为3×2,符合0-1之间的均匀分布的数组
np.random.rand(3, 2)
(2)np.random.uniform()方法,创建[low, high)之间的均匀分布的随机数组
numpy.random.uniform(low=0.0, high=1.0, size=None)
uniform方法可以指定产生随机数的范围[low,high),size为数组的形状,输入格式为整形(一维)或者整形元祖
如果不指定size的话,则返回一个服从该分布的随机数
# 产生一个大小为3×2,符合0-10之间的均匀分布的数组
np.random.uniform(1, 10, (3, 2))
(3)np.random.randn()方法,创建服从标准正态分布的数组(均值为0,方差为1)
np.random.randn(d0, d1, ..., dn)
该方法和rand类似,函数的输入为若干个整数,表示输出随机数的大小为d0×d1× ...×dn
如果没有参数输入,则返回一个服从标准正态分布的float型随机数
# 产生一个大小为3×2,符合标准正态分布的数组
np.random.randn(3, 2)
(4)np.random.normal()方法,创建服从μ=loc,σ=scale的正态分布的数组
np.random.normal(loc=0.0, scale=1.0, size=None)
loc:指定均值 μ; scale:指定标准差 σ
size:输入格式为整形(一维)或者整形元祖,指定了数组的形状
# 产生一个大小为3×2,符合均值为5,标准差为10的正态分布的数组
np.random.normal(5, 10, (3, 2))
(5)np.random.randint()方法,在指定区间[low, high)中离散均匀抽样的数组
np.random.randint(low, high=None, size=None, dtype=np.int64)
函数返回区间[low, high)内的离散均匀抽样,dtype指定返回抽样数组的数据类型,默认为整形
size:输入格式为整形(一维)或者整形元祖,指定了数组的形状
# 在[1, 5)之间离散均匀抽样,数组形状为3行2列
np.random.randint(1, 5, (3, 2))
(6)numpy.random.choice()方法,对具体样本进行有放回或者无放回的抽样
np.random.choice(a, size=None, replace=True, p=None)
从样本a中进行抽样,a可以为数组、列表或者整数,若为整数,表示[0,a)的离散抽样;
replace为False,表示无放回抽样;replace为True,表示有放回抽样
size为生成样本的大小
p为给定数组中元素出现的概率
因为理想情况下,每次投篮都不影响下一次的结果,所以把这个问题归结为有放回的抽样,一共进行10次
shoot_lst用来存储投篮的结果
# 从["命中", "未命中"]中有放回抽样,其中命中的概率为0.65,共抽取10次,返回的格式为为ndarray
np.random.choice(["命中", "未命中"], size=10, replace=True, p=[0.65, 0.35])
二、数组索引与切片
-
一维数组
ar1 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
-
二维数组
ar2 = [[0, 1, 2],
[3, 4, 5],
[6, 7, 8]] -
三维数组
ar3 = [[[1, 2, 3],
[4, 5, 6]],
[[7, 8, 9],
[10, 11, 12]],
[[13, 14, 15],
[16, 17, 18]]]
1、索引
array[index]
ar1[1]
# Out: 1, 从0开始,1为第2个
ar1[-1]
# Out: 9,-1为末尾
2、切片
array[start:stop:step=1]
start从0开始计数,stop从1开始计数
start为0时可以省略,stop=len(array)时可以省略
ar11 = ar1[1:8:2]
# Out: [1, 3, 5, 7]
ar22 = ar1[1:3, 1]
ar33 = ar1[1:3, 1, :3]
3、布尔型索引
cities = np.array(["hz", "sh", "hz", "bj"])
arr_rnd = np.arange(4, 4)
(1) 利用数组的比较运算,生成一个布尔类型的数组
cities == "hz"
# Out: array([ True, False, True, False])
利用布尔型数组,进行数组索引;观察索引的规律
我们可以做这样一个推断:布尔型数组的长度要和被索引的轴的长度一致
(2)利用布尔型数组、切片进行1个维度的索引
arr_rnd[cities == "hz"]
# Out:
# array([[0, 1, 2, 3],
# [8, 9, 10, 11]])
# 即从arr_rnd中取出cities2为True的对应元素
(3)利用布尔型数组、切片进行2个维度的索引
arr_rnd[cities == "hz", :3]
# cities == "hz" 为第一维度
# :3 为第二维度
# Out:
# array([[0,1,2],[8,9,10]])
arr_rnd[:3, cities == "hz"]
# 这样也成立,但不等于arr_rnd[cities == "hz", :3]
# Out:
# array([[0,2],[4,6],[6,8])
(4)将arr_rnd中>0的元素置为0
arr_rnd[arr_rnd > 0] = 0
# Out:
# array([[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]])
(5)实现多个布尔型数组之间的“和”、“或”、“非”运算:
# 非运算 ~
~(cities == "hz")
# Out: array([False, True, False, True, True, True, True])
# 和运算 &
(cities == "hz") & (cities == "sz")
# Out: array([False, False, False, False, False, False, False])
# 或运算 |
(cities == "hz") | (cities == "sz")
# Out: array([ True, False, True, False, False, False, True])
4、 花式索引
新建4×6的二维数组arr_demo01
arr_demo01 = np.arange(24).reshape(4, 6)
(1)方法1:分别将4个角的元素索引出来,然后把取出来的4个元素,重新组成一个新2×2的数组
arr_method1 = np.array([[arr_demo01[0, 0], arr_demo01[0, -1]],
[arr_demo01[-1, 0], arr_demo01[-1, -1]]])
# Out:
# array([[0, 5],
# [18, 23]])
(2)方法2:利用布尔索引,可以同时索引不连续的行。分别对axis 0方向和axis 1方向进行索引。但是需要注意的是,得分2次索引;
arr_method2 = arr_demo01[[True, False, False, True]][:, [True, False, False, False, False, True]]
# Out:
# array([[0, 5],
# [18, 23]])
花式索引外围必须有一个中括号[],即索引值不为整数,而是数组或其他类型数据
我们传入一个整数数组,对axis 0方向进行索引,并且索引结果的顺序和传入的整数数组一一对应:
arr_demo01[[2, 0]]
# Out:
# array([[12, 13, 14, 15, 16, 17],
# [0, 1, 2, 3, 4, 5]])
如果同时传入2个整数数组,中间用逗号分开。 那么这两个数组会以两两配对的形式,对元素进行索引。而并不是一个矩形状的索引区域!
即arr_demo01[0,0]和arr_demo01[-1,-1],类似arr_demo01[0][0]和arr_demo01[-1][-1]
arr_demo01[[0, -1], [0, -1]]
# Out:
# array([ 0, 23])
(3)方法3:分别传入4个角的坐标,请朋友们注意观察传入的2个整数数组的规律
arr_demo01[[0, 0, -1, -1], [0, -1, 0, -1]]
# 即arr_demo01[0,0]、arr_demo01[0,-1]、arr_demo01[-1,0]和arr_demo01[-1,-1]
# Out: array([ 0, 5, 18, 23])
arr_demo01[[0, 0, -1, -1], [0, -1, 0, -1]].reshape(2, 2)
# Out:
# array([[0, 5],
# [18, 23]])
(4)方法4:利用花式索引和切片混用。也是通过连续2次索引,得到一个矩形状的区域
arr_demo01[[0, -1]][:, [0, -1]]
# 第一次索引 [[0, -1]]
# Out:
# array([[0, 1, 2, 3, 4, 5],
# [18, 19, 20, 21, 22, 23]])
# 第二次索引 [:, [0, -1]]
# Out:
# array([[0, 5],
# [18, 23]])
(5)方法5:利用函数np.ix_,构建矩形索引器:
# 即arr_demo01[0,0]、arr_demo01[0,-1]、arr_demo01[-1,0]和arr_demo01[-1,-1]
arr_demo01[np.ix_([0, -1], [0, -1])]
# Out:
# array([[0, 5],
# [18, 23]])
三、数组的运算
1、数组与标量之间的运算
arr = np.array([[1, 2, 3], [4, 5, 6]])
通常我们把单独的数叫做标量,数组可以直接与标量进行计算,计算逻辑会自动传播到数组的全部元素中。
arr + 5
arr - 5
arr * 5
arr / 5
arr ** 5
2、数组的通用函数运算
(1)四则运算
最简单的通用函数就是数组与数组的四则运算。但是在进行数组的四则运算的时候,我们需要保证二者的维数一样
数组对应位置的元素加减乘除,并不是高等数学上的矩阵运算。
Numpy也封装了针对四则运算的函数
arr + arr # np.add(arr, arr)
arr - arr # np.subtract(arr, arr)
arr * arr # np.multiply(arr, arr)
arr / arr # np.divide(arr, arr)
(2)一元函数
arr_rnd = np.random.normal(5, 10, (3, 4))
对数组进行四舍五入运算。需要注意的是,结果数组仍然保留了输入数组的dtype属性
np.rint(arr_rnd)
# 函数 说明
# abs,fabs 计算绝对值,对于非负数值,可以使用更快的fass
# sqrt,square,exp 求各元素的平方根、平方、指数ex
# log,log10,log2,log1p 分别计算自然对数(底数为e)、底数为10的log、底数为2的log、log(1+x)
# sign 计算各元素的正负号:1(整数)、0(零)、-1(负数)
# ceil 计算各元素的、大于等于该值的最小整数
# floor 计算各元素的、大于等于该值的最大整数
# rint 将各元素值四舍五入到最接近的整数,并保留dtype
# modf 把数组的小数和整数部分以两个独立的数组分别返回
# isnan 判断各元素是否为空NaN,返回布尔型
# cos,cosh,sin,sinh,tan,tanh 普通型和双曲型三角函数
(3)二元运算
利用随机函数,产生2个数组
x = np.random.normal(5, 10, (3, 1)) # np.random.normal(5, 10, 3)
y = np.random.normal(5, 10, (3, 1))
计算,比较元素级的最大值
np.maximum(x, y)
计算,比较元素级的最小值
np.minimum(x, y)
计算,执行元素级的比较
np.greater(x, y)
# 函数 说明
# maximum,fmax 计算元素级的最大值,fmax自动忽略空值NaN
# minimum,fmin 计算元素级的最小值,fmin自动忽略空值NaN
# greater,greater_equal 执行元素级的比较,生产布尔型数组。效果相当于>,≥
# less,less_equal 执行元素级的比较,生产布尔型数组。效果相当于<,≤
# equal,not_equal 执行元素级的比较,生产布尔型数组。效果相当于==,!=
# logical_and,logical_or,logic_xor 执行元素级的逻辑运算,相当于执行运算符&、|、^
3、数组的线性代数运算
(1)矩阵乘法
矩阵的乘法,输入的2个数组的维度需要满足矩阵乘法的要求,否则会报错;
# arr.T表示对arr数组进行转置
# np.dot表示对输入的两个数组进行矩阵乘法运算
np.dot(arr, arr.T)
# Out:
# array([[14, 32],
# [32, 77]])
(2)numpy.linalg工具:矩阵运算
# 函数 说明
# diag 以一维数组的形式返回方阵的对角线(或非对角线)元素,或将一维数组转换为方阵
# trace 计算对角线元素的和
# det 计算矩阵行列式
# eig 计算方阵的本征值和本征向量
# inv 计算方阵的逆
# pinv 计算矩阵的Moore-Penrose伪逆
# qr 计算QR分解
# svd 计算奇异值分解(SVD)
# solve 解线性方程
# lstsq 计算Ax=b的最小二乘解
4、数组的聚合函数运算
# 常用的聚合函数
# 函数 说明
# sum 求和运算
# cumsum 累积求和运算
# min 求最小值
# max 求最大值
# mean 求均值
# median 求中位数
# var 求方差
# std 求标准差
5、Numpy运算方向axis详解
我们把二维数组的垂直方向定义为axis 0轴,水平方向为axis 1轴。
当我们在对Numpy进行运算时,我们把axis=0指定为垂直方向的计算,axis=1指定为水平方向的运算。
规定求最大值的方向为垂直方向即可,即axis=0
np.max(arr_rnd, axis=0)
6、利用Numpy实现条件判断
where 函数中输入3个参数,分别是判断条件、为真时的值,为假时的值
在Numpy中,空值是一种新的数据格式,我们用np.nan产生空值
np.where(arr_rnd < 5, np.nan, arr_rnd)
7、np.frompyfunc
如果还是找不到合适的函数来实现自己的目的,那不妨自己写一个,也很简单。
我们只需要利用frompyfunc函数,将计算单元素的函数转换成,能对数组的每个元素进行操作的函数即可。
frompyfunc函数有三个输入参数,分别是待转化的函数、函数的输入参数的个数、函数的返回值的个数
四、Python 引用
a = ["a", "b", "c"]
b = a
a is b
# Out:
# True
# b是a的引用,id(a)=id(b),id(a)为a的地址
1、Python的深拷贝与浅拷贝
import copy
m = ["Jack", "Tom", "Brown"]
2、深拷贝
n = copy.deepcopy(m)
# 判断二者是否相等,结果显而易见
m == n
# Out: True
# 判断二者是否具有同一性,答案为否。也就是说,列表m和列表n是存储在不同的地址里。
m is n
# Out: False
3、浅拷贝
n = copy.copy(m)
# 判断二者是否相等,结果显而易见
m == n
# 判断浅拷贝前后是否具有同一性,答案是不具备同一性
m is n
# Out: False
4、深拷贝和浅拷贝的区别
# students列表的长度为3,其中首位为字符串,其他位均为列表
students = ["Class 1", ["Jack", 178, 120], ["Tom", 174, 109]]
students_c = copy.copy(students)
# 查看内嵌的列表是否具备同一性
students[1] is students_c[1]
# Out: True
# 尝试更改students中某位学生的信息,通过测试更改后的students和students_c
students[1][1] = 180
students
# Out: ['Class 1', ['Jack', 180, 120], ['Tom', 174, 109]]
students_c
# Out: ['Class 1', ['Jack', 180, 120], ['Tom', 174, 109]]
# 发现:students_c也跟着改变了,这说明对于嵌套列表里的可变元素(深层次的数据结构),浅拷贝并没有进行拷贝,只是对其进行了引用
# 我们接着尝试更改students中的班级信息
students[0] = "Class 2"
students
# Out: ['Class 2', ['Jack', 180, 120], ['Tom', 174, 109]]
students_c
# Out: ['Class 1', ['Jack', 180, 120], ['Tom', 174, 109]]
# 发现:students_c没有发生变化,这说明对于嵌套列表里的不可变元素,浅拷贝和深拷贝效果一样
1、由不可变对象组成的列表,浅拷贝和深拷贝效果一样,拷贝前后互相独立,互不影响;
2、当列表中含有可变元素时,浅拷贝只是建立了一个由该元素指向新列表的引用(指针),当该元素发生变化的时候,拷贝后的对象也会发生变化;
3、深拷贝完全不考虑节约内存,浅拷贝则相对来讲比较节约内存,浅拷贝仅仅是拷贝第一层元素。
切片其实就是对源列表进行部分元素的浅拷贝!
5、视图view是对数据的引用,通过该引用,可以方便地访问、操作原有数据,但原有数据不会产生拷贝。
arr_ = np.arange(12).reshape(3,4)
view_ = arr_.view()
view_.shape = (4, 3) # 更改维度
slice_ = arr_[:6]
视图是新建了一个引用,如果我们对视图进行修改,它会影响到原始数据,因为它们的物理内存在同一位置。
但是更改视图的维数,并不会引起原始数组的变化
6、副本是对数据的完整拷贝(Python中深拷贝的概念)。
如果我们对副本进行修改,它不会影响到原始数据,它们的物理内存不在同一位置。
Numpy建立副本的方法稍有不同。
方法一,是利用Numy自带的copy函数;
copy_ = arr_.copy()
# 比较发现,建立副本后,二者互不影响。
方法二,是利用deepcopy()函数。
网友评论