2.NumPy数组形态转换与计算
2.1 NumPy 数组创建函数
2.1.1 创建数组序列
np.linspace可以创建等差数列形式的 NumPy 数组np.power(x, y)会计算 x 的 y 次方np.logspace与np.linspace类似,但会增加一个base参数,用来生成以 base 为底的指数函数数列
python
arr = np.linspace(1, 10, 10) # 创建从 1 到 10 的等差数组,共 10 个点
arr # 查看数组
np.power(2, arr) # 计算 2 的 arr 次方,每个元素分别计算
arr = np.logspace(1, 10, 10, base=2, dtype='int64') # 以 2 为底,指数从 1 到 10
arr # 查看指数序列2.1.2 创建一维数组序列
np.random.normal会生成正态分布的数组- 前两个参数为均值和标准差
- 如果第三个参数为元组,那么将生成多维数组
- 生成的二维数组使用
[:,1]索引后,使用Series绘制密度图发现,确实比较符合正态分布(可见均值在 10 附近)
python
arr = np.random.normal(10,20,(10000,3)) # 生成正态分布数组
pd.Series(arr[:,1]).plot(kind='kde') # 绘制密度图2.1.3 创建多维数组序列
(1) np.logspace 创建
np.logspace 还可以创建多维数组,会根据不同的 base 参数,分别生成以不同 base 为底的指数函数数列,并最终返回多维数组
python
arr = np.logspace(1, 10, 10, base=[2, 3], dtype='int64')
# base 为多个值时,分别以 2 和 3 为底生成指数序列,并组合成二维数组
arr # 查看生成的多维指数数组(2) np.random.randn 创建
np.random.randn会生成标准正态分布的数组(均值 0、方差 1)- 如果参数为数值,则生成一维数组
- 如果参数为元组,则生成多维数组
- 生成的二维数组使用
[:,0]索引后,再用Series绘制密度图,可看到其分布确实接近标准正态分布(均值约为 0)
python
arr = np.random.randn(10) # 生成长度为 10 的标准正态一维数组
arr # 查看数组
arr = np.random.randn(10000, 2) # 生成 10000×2 的二维标准正态分布数组
pd.Series(arr[:, 0]).plot(kind='kde')
# 取第一列数据并绘制 KDE 密度图,检验正态分布(3) np.random.rand 创建
np.random.rand会生成[0,1]均匀分布的数组- 如果参数为数值那么生成一维数组
- 如果参数为元组那么将生成多维数组
- 生成的二维数组使用
[:,2]索引后,使用 Series 绘制密度图发现,确实比较符合[0,1]均匀分布(各组频数接近,且极值为 0 和 1)
python
# 生成一维均匀分布随机数
arr = np.random.rand(10)
arr
# 生成二维均匀分布随机数
arr = np.random.rand(10000, 3)
# 查看形状
arr.shape
# 使用Series绘制直方图
pd.Series(arr[:, 2]).plot(kind='hist', bins=50, edgecolor='k', color='orange')(4) np.random.uniform 创建
np.random.uniform会生成均匀分布的数组- 前两个参数为最小值和最大值
- 如果第三个参数为数字,则生成 1 维数组,如果参数为元组,那么将生成多维数组
- 生成的一维数组使用 Series 绘制密度图发现,确实比较符合均匀分布(可见最小值、最大值与设定参数相同,且各组频数接近)
python
# 创建 10000 个服从均匀分布 [176, 192] 的随机数
arr = np.random.uniform(176, 192, size=10000)
# 使用直方图检查分布
pd.Series(arr).plot( # 将数组转换为Series并开始绘图
kind='hist', # 直方图类型
edgecolor='k', # 直方图边框颜色为黑色
color='green', # 填充颜色为绿色
bins=25 # 分成25个柱子
)2.2 NumPy 数组形态转换
2.2.1 reshape 函数
reshape可以改变数组维度和形状,只要元素 size 不变- 根据代码和结果对比来总结规律
ravel/flatten/reshape(-1)是可以生成一维数组的几种方法
python
b = np.arange(24).reshape(2, 3, 4) # 创建 0~23 并 reshape 为 2×3×4
print(b) # 打印数组
print(b.shape) # 查看数组形状 (2,3,4)
print(b.ravel()) # ravel:返回一维视图(不复制)
print(b.flatten()) # flatten:返回一维拷贝(复制)
print(b.reshape(-1)) # reshape(-1):自动变成一维2.2.2 shape/resize 函数
- 可以通过
shape命令改变形状 resize命令与shape效果相等- 两个命令都会直接修改数组形状
python
b.shape = (6, 4) # 改变数组维度为 6×4
print(b) # 打印数组
b.resize((2, 12)) # resize 修改形状为 2×12
print(b) # 打印数组2.3 NumPy 数组计算
2.3.1 四则运算
np.add(arr1, arr2)两个数组形状相同,会按照对应位置元素相加np.subtract(arr1, arr2)会实现相减np.multiply(arr1, arr2)会实现相乘np.divide(arr1, arr2)会实现相除,arr1 / arr2np.floor_divide(arr1, arr2)会实现整除,arr1 // arr2
python
arr1 = np.array([[1, 2], # 定义数组 1
[3, 4]])
arr2 = np.array([[5, 6], # 定义数组 2
[8, 11]])
print(np.add(arr1, arr2)) # 对应元素相加
print(np.subtract(arr1, arr2)) # 对应元素相减
print(np.multiply(arr1, arr2)) # 对应元素相乘
print(np.divide(arr1, arr2)) # 对应元素相除(浮点结果)
print(np.floor_divide(arr1, arr2)) # 对应元素整除
print(np.floor_divide(arr2, arr1)) # 交换顺序再整除2.3.2 运算函数
np.negative(arr)会对数组元素都取相反数np.power(arr1, arr2)会实现 arr1 的 arr2 次方np.remainder(arr1, arr2)会计算 arr1 % arr2
python
import numpy as np
arr1 = np.array([[1, 2], [3, 4]]) # 示例数组1
arr2 = np.array([[5, 6], [8, 11]]) # 示例数组2
# 1. np.negative:元素取相反数
print(np.negative(arr1 - arr2)) # 先 arr1-arr2 再取相反数
# 2. np.power:数组元素按次方计算
print(np.power(arr1, arr2)) # arr1 ** arr2
print(np.power(arr2, arr1)) # arr2 ** arr1
print(np.power(arr1, 3)) # arr1 ** 3
print(np.power(2, arr2)) # 2 ** arr2
# 3. np.remainder:取余(模运算)
print(np.remainder(arr1, arr2)) # arr1 % arr2
print(np.remainder(arr2, arr1)) # arr2 % arr12.4 NumPy 数组比较运算
np.equal(arr1, arr2)会判断 arr1 和 arr2 每个对应位置的元素是否相等np.not_equal(arr1, arr2)会判断是否不相等np.less(arr1, arr2)会判断 arr1 每个元素是否小于 arr2 对应位置的元素np.less_equal(arr1, arr2)会判断是否小于等于np.greater(arr1, arr2)会判断是否大于np.greater_equal(arr1, arr2)会判断是否大于等于
python
# 对比 arr1 和 arr2
print(np.equal(arr1, arr2)) # 判断相等
print(np.not_equal(arr1, arr2)) # 判断不相等
print(np.less(arr1, arr2)) # 小于
print(np.less_equal(arr1, arr2)) # 小于等于
print(np.greater(arr1, arr2)) # 大于
print(np.greater_equal(arr1, arr2)) # 大于等于
# np.where 示例
print(np.where(arr1 * 3 > arr2, arr1, arr2)) # 按条件选择2.5 NumPy 数组逻辑运算
2.5.1 布尔值索引
(arr1 > 2.5)会返回由布尔值组成的数组arr1[arr1 > 2.5]使用布尔值数组对原数组进行筛选.shape可查看数组形状- 多维数组同样可以用布尔索引,布尔结果会按元素级比较
- 布尔索引会将结果展平成一维返回(即使原数组是多维)
python
(arr1 > 2.5) # 比较,生成布尔数组
print(arr1 > 2.5)
arr1[arr1 > 2.5] # 使用布尔索引筛选元素
print(arr1[arr1 > 2.5])
arr1.shape # 查看 arr1 的形状
print(arr1.shape)
arr_3 = np.arange(24).reshape(2, 4, 3) # 创建 2×4×3 的三维数组
print(arr_3)
arr_3[arr_3 > 2.5] # 对三维数组使用布尔索引(返回一维筛选结果)
print(arr_3[arr_3 > 2.5])2.5.2 all/any 函数
np.all(axis)按照指定的轴去一次计算是否所有元素都为 True,只要有 False 存在,则返回 False,否则返回 Truenp.any(axis)按照指定的轴去一次计算是否所有元素都为 False,只要有 True 存在,则返回 True,否则返回 False
python
print(np.all([-1, 4, 5])) # -1,4,5 都被视为 True → 全为 True → True
print(np.all([-1, 0, 5])) # 0 为 False → 存在 False → False
arr_3 = np.arange(24).reshape(2, 4, 3) # 创建 arr_3,形状为 (2,4,3)
print(np.all(np.where(arr_3 > 10, arr_3, 0))) # 判断整数组是否所有元素都 >10(False)
print(np.all(np.where(arr_3 > 10, arr_3, 0), axis=0)) # 沿 axis=0 判断
print(np.all(np.where(arr_3 > 10, arr_3, 0), axis=1)) # 沿 axis=1 判断
print(np.all(np.where(arr_3 > 10, arr_3, 0), axis=2)) # 沿 axis=2 判断
np.any([-1, 0, 5]) # 判断是否存在 True(非零为True)
np.any(np.where(arr_3 > 10, arr_3, 0)) # where:大于10保留,否则为0,再判断是否存在 True
np.any(np.where(arr_3 > 10, arr_3, 0), axis=0) # axis=0:按列判断是否存在 True
np.any(np.where(arr_3 > 10, arr_3, 0), axis=1) # axis=1:按行判断是否存在 True
np.any(np.where(arr_3 > 10, arr_3, 0), axis=2) # axis=2:按最内层维度判断是否存在 True2.6 NumPy 数组统计运算
2.6.1 聚合函数
(1) maximum/minimum 函数
max是把相同位置上的元素进行比较,取较大值重新组成的数组min是把相同位置上的元素进行比较,取较小值重新组成的数组- 计算结果与原数组
shape相同
python
# 最大值/最小值
x = np.random.randn(8) # 生成 8 个标准正态分布随机数
y = np.random.randn(8) # 生成另一个数组
print(x) # 打印数组 x
print(y) # 打印数组 y
print(np.maximum(x, y)) # 相同位置取最大
print(np.minimum(x, y)) # 相同位置取最小(2) ptp 等函数
np.ptp=np.max - np.minmedian是计算中位数np.average可以计算带权重的均值,参数为weights,average是计算加权平均的专属;除此之外常用的是np.meannp.std是计算标准差np.var是计算方差
python
print(np.ptp(x)) # 极差:max-min
print(np.mean(y)) # 平均值
print(np.average(y,weights=x)) # 加权平均
print(np.median(y)) # 中位数
print(np.var(y)) # 方差
print(np.std(y)) # 标准差(3) argmin/argmax 函数
argmax是把元素进行比较,取最大值所在的位置索引argmin是把元素进行比较,取最小值所在的位置索引- 经过右侧图片代码验证,索引值正确,不存在比
argmax对应值更大的元素;也不存在比argmin对应值更小的元素
python
print(np.argmin(x)) # 计算 x 中最小值的索引
x[3] # 访问索引 3 的元素
x[x < x[3]] # 过滤比 x[3] 更小的值(验证最小索引正确)
print(np.argmax(y)) # 计算 y 中最大值的索引
y[y > y[np.argmax(y)]] # 过滤比最大值更大的值(验证最大索引正确)2.6.2 取整拆分函数
np.modf函数可以拆分出整数部分和小数部分- 注意如果元素是负数,那么拆分出来的小数部分是负数,即拆分出来的整数绝对值不会大于拆分前
python
arr = np.random.randn(5) * 15 # 随机生成数组并放大
print(arr) # 查看原数组
print(np.modf(arr)) # modf:拆分小数部分和整数部分
print(np.modf(arr)[0]) # 小数部分
print(np.modf(arr)[1]) # 整数部分2.6.3 条件判断 np.where
- 条件判断是数据分析中常用的处理
np.where处理逻辑与 sql 中 if else 比较类似- 根据最终图片显示结果,可以看到
np.where实现了对应的处理逻辑
python
# 将条件逻辑表达为数组运算
xarr = np.array([1.1, 1.2, 1.3, 1.4, 1.5]) # x 数组
yarr = np.array([2.1, 2.2, 2.3, 2.4, 2.5]) # y 数组
cond = np.array([True, False, True, True, False]) # 条件布尔数组
# 使用 for 循环实现:c=True 取 x,否则取 y
result = [ (x if c else y) for x, y, c in zip(xarr, yarr, cond) ] # 列表推导式模拟 where 逻辑
# 将条件逻辑表达为数组运算np.where
result = np.where(cond, xarr, yarr) # cond=True 取 xarr,否则取 yarr
result
# 另一组例子
arr = np.random.randn(4, 4) # 生成 4×4 标准正态矩阵
print(arr)
print(np.where(arr > 0, 2, -2)) # arr>0 的位置替换为 2,否则为 -2
print(np.where(arr > 0, 2, arr)) # arr>0 的位置替换为 2,否则保留原值2.6.4 mean 方法
arr.mean()计算均值,如果无参数,会计算所有元素均值- 如果指定
axis,那么就按沿着指定的轴方向去计算 - 可以看到
axis=1,对于二维数组就是沿着列方向把每行求个均值
python
# 数学与统计计算
arr = np.random.randn(5, 4) # 生成 5×4 标准正态分布数据
arr # 查看数组
# 各种方式计算均值
print(arr.mean()) # 所有元素的均值
print(arr.mean(axis=1)) # axis=1:按行求均值
print(arr.mean(axis=1).mean())# 行均值的再求均值
print(arr.mean(axis=0)) # axis=0:按列求均值
print(arr.mean(axis=0).mean())# 列均值的再求均值
print(np.mean(arr)) # np.mean:与 arr.mean() 等价
print(np.mean(arr, axis=1)) # np.mean 指定 axis2.6.5 累计计算
cumsum代表累加,可以按照 axis 轴来规定方向累加cumprod代表累乘,可以按照 axis 轴来规定方向累乘- 如果不指定轴 axis,那么数组形状会改变,会让所有元素参加计算
python
# 各种方式计算求和
print(arr.sum()) # 所有元素求和
print(arr.sum(0)) # 按列求和
print(arr.sum(1)) # 按行求和
# 计算累计求和和累计乘积
arr = np.array([[0, 1, 2], # 创建示例数组
[3, 4, 5],
[6, 7, 8]])
print(arr) # 查看数组
print(arr.cumsum(0)) # 按列累计求和
print(arr.cumprod(1)) # 按行累计乘积
print(arr.cumprod(0)) # 按列累计乘积
print(arr.cumsum()) # 所有元素累计求和(展平后)2.6.6 唯一化
np.unique是把数组进行去重,相同元素只保留一份np.unique与set有类似的功能,np.unique返回数组,set返回的是集合
python
# 唯一化以及其他的集合逻辑
names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe']) # 姓名数组
print(np.unique(names)) # 唯一化字符串数组
ints = np.array([3, 3, 3, 2, 2, 1, 1, 4, 4]) # 整数数组
print(np.unique(ints)) # 唯一化整数数组
print(sorted(set(names))) # 使用 set + sorted 获取唯一值并排序2.6.7 np.in1d 函数
- 包含关系常用在数据分析抽取数据中的条件判断
invert参数:是否取反向结果,True和False互为反向结果
python
# 包含关系判断 np.in1d
# np.in1d(a, b, invert=True)
values = np.array([6, 0, 0, 3, 2, 5, 6]) # 原始数组
print(np.in1d(values, [2, 3, 6], invert=0)) # 判断 values 是否在列表中
print(np.in1d(values, [2, 3, 6], invert=1)) # invert=1 表示取反2.6.8 取整函数
np.square计算元素的平方np.sign返回数字的符号,正数为 1,负数为 -1,0 为 0np.ceil为向上取整np.floor为向下取整np.rint为四舍五入取整
python
arr = np.arange(10) # 创建 0~9 的数组
print(arr) # 打印原数组
print(np.square(arr)) # 计算平方
print(np.sign(arr)) # 返回符号
print(np.ceil(np.sqrt(arr))) # sqrt 后向上取整
print(np.floor(np.sqrt(arr))) # sqrt 后向下取整
print(np.rint(np.sqrt(arr))) # sqrt 后四舍五入取整2.6.9 指数/对数函数
np.exp为以 e 为底的指数函数np.log为以 e 为底的对数函数np.log2为以 2 为底的对数函数np.log10为以 10 为底的对数函数
python
print(np.exp(arr)) # e 的指数函数
print(np.log(arr + 1)) # 以 e 为底的对数,加 1 防止 log(0)
print(np.log2(arr + 1)) # 以 2 为底的对数
print(np.log10(arr + 1)) # 以 10 为底的对数2.7 NumPy 数组排序运算
2.7.1 数组排序
np.array.sort()sort()支持参数指定排序方向- 对于二维数组,指定按行还是按列取决于
axis参数
python
# 排序:1维数组
arr = np.random.randn(8) # 生成8个随机数
print(arr) # 打印原数组
arr.sort() # 对1维数组排序(默认升序)
print(arr) # 打印排序后数组
# 排序:2维数组
arr = np.random.randn(5, 3) # 生成5×3数组
print(arr) # 打印原数组
arr.sort(1) # 按行排序(axis=1)
print(arr) # 打印按行排序后数组
arr.sort(0) # 按列排序(axis=0)
print(arr) # 打印按列排序后数组,排序后数组已经改变2.7.2 数组排序性能
10w随机数组与10w随机列表排序速度结果对比,数组要快1-2倍- 在涉及到大数量运算的时候,排序次数多,那么
4-5倍的时间是非常大的差距(相当于15min vs 1H)
python
# 大数组排序耗时
# large_arr = np.random.randn(1000000)
large_arr = np.random.randn(100000) # 生成10w随机数组
%timeit large_arr.sort() # 测试数组排序速度
print(large_arr[int(0.025 * len(large_arr))]) # 取5%分位数
# 大列表排序耗时
# large_arr = np.random.randn(1000000)
large_list = list(np.random.randn(100000)) # 生成10w随机列表
%timeit large_list.sort() # 测试列表排序速度
# print(large_list[int(0.025 * len(large_list))]) # 取5%分位数