【4天快速入门Python数据挖掘之第2天】Numpy——高效的运算工具
🔥一个人走得远了,就会忘记自己为了什么而出发,希望你可以不忘初心,不要随波逐流,一直走下去🎶
🦋 欢迎关注🖱点赞👍收藏🌟留言🐾
🦄 本文由 程序喵正在路上 原创,CSDN首发!
💖 系列专栏:Python数据挖掘
🌠 首发时间:2022年8月19日
✅ 如果觉得博主的文章还不错的话,希望小伙伴们三连支持一下哦
阅读指南
一、Numpy优势
1. Numpy简介
Numpy (Numerical Python) 是一个开源的 Python 科学计算库,用于快速处理任意维度的数组
Numpy 支持常见的数组和矩阵操作,对于同样的数值计算任务,使用 Numpy 比直接使用 Pyhon 要简洁得多
Numpy 使用 ndarray 对象来处理多维数组,该对象是一个快速而灵活的大数据容器
2. ndarray介绍
Numpy 提供了一个 N维数组类型 ndarray (N-dimensional array),它描述了相同类型的 “items” 的集合
▷ 用 ndarray 进行存储:
import numpy as np
# 创建ndarray
score = np.array([[80, 89, 86, 67, 79],
[80, 89, 86, 67, 79],
[80, 89, 86, 67, 79],
[80, 89, 86, 67, 79],
[80, 89, 86, 67, 79],
[80, 89, 86, 67, 79],
[80, 89, 86, 67, 79],
[80, 89, 86, 67, 79]])
score
▷ 运行结果:
使用 Python 列表可以存储一维数组,通过列表的嵌套可以实现多维数组,那么为什么还需要使用 Numpy 的 ndarray 呢?
3. ndarray与Python原生list运算效率对比
在这里我们通过一段代码运行来体会 ndarray 的好处
import random
import time
import numpy as np
a = []
for i in range(10000000):
a.append(random.random())
t1 = time.time()
sum1 = sum(a)
t2 = time.time()
b = np.array(a)
t4 = time.time()
sum3 = np.sum(b)
t5 = time.time()
print(t2-t1, t5-t4)
t2-t1 为使用 Python 自带的求和函数消耗的时间,t5-t4 为使用 Numpy 求和消耗的时间,结果为:
从中我们看到 ndarray 的计算速度要快很多,节约了很多
机器学习的最大特点就是大量的数据运算,那么如果没有一个快速的解决方案,那可能现在 Python 在机器学习领域也达不到好的效果
Numpy 专门针对 ndarray 的操作和运算进行了设计,所以数组的存储效率和输入输出性能远优于 Python 中的嵌套列表,数组越大,Numpy 的优势就越明显
那么,为什么 ndarray 可以这么快呢?
4. ndarray的优势
▷ 1、内存块风格
ndarray 到底跟原生 Python 列表有什么不同呢
ndarray 在存储数据的时候,数据与数据的地址都是连续的,这样就使得批量操作数组元素时速度更快
这是因为 ndarray 中的所有元素的类型都是相同的,而 Python 列表中的元素类型是任意的,所以 ndarray 在存储元素时内存可以连续,而 Python 原生 list 只能通过寻址方式找到下一个元素,这虽然也导致了在通用性能方面 Numpy 的 ndarray 不及 Python 原生 list,但在科学计算中,Numpy 的 ndarray 就可以省略掉很多循环语句,代码使用方面比 Python 原生 list 简单得多
▷ 2、ndarray支持并行化计算(向量化计算)
▷ 3、Numpy底层使用C语言编写,内部解除了GIL(全局解释器锁),其对数组的操作速度不受Python解释器的限制,效率远高于纯Python代码
二、认识N维数组
1. ndarray的属性
属性名字 | 属性解释 |
---|---|
ndarray.shape | 数组维度的元组 |
ndarray.dtype | 数组元素的类型 |
ndarray.ndim | 数组维数 |
ndarray.size | 数组中的元素数量 |
ndarray.itemsize | 一个数组元素的长度(字节) |
2. ndarray的形状
首先创建一些数组
# 创建不同形状的数组
a = np.array([[1, 2, 3], [4, 5, 6]])
b = np.array([1, 2, 3, 4])
c = np.array([[[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]]])
然后分别打印出形状
怎么理解数组的形状?
二维数组:
三维数组:
3. ndarray的类型
dtype 是 numpy.dtype 类型,先看看对于数组来说都有哪些类型
名称 | 描述 | 简写 |
---|---|---|
np.bool | 用一个字节存储的布尔类型(True或False) | ‘b’ |
np.int8 | 一个字节大小,-128 至 127 | ‘i’ |
np.int16 | 整数,-32768 至 32767 | ‘i2’ |
np.int32 | 整数,-231 至 232 – 1 | ‘i4’ |
np.int64 | 整数,-263 至 z63 – 1 | ‘i8’ |
np.uint8 | 无符号整数,0 至 255 | ‘u’ |
np.uint16 | 无符号整数,0 至 65535 | ’u2’ |
np.uint32 | 无符号整数,0 至 232 – 1 | ‘u4’ |
np.uint64 | 无符号整数,0 至 264 – 1 | ‘u8’ |
np.float16 | 单精度浮点数:16位,正负号1位,指数5位,精度10位 | ‘f2’ |
np.float32 | 单精度浮点数:32位,正负号1位,指数8位,精度23位 | ‘f4’ |
np.float64 | 双精度浮点数:64位,正负号1位,指数11位,精度52位 | ‘f8’ |
np.complex64 | 复数,分别用两个32位浮点数表示实部和虚部 | ‘c8’ |
np.complex128 | 复数,分别用两个64位浮点数表示实部和虚部 | ‘c16’ |
np.object | python对象 | ‘O’ |
np.string_ | 字符串 | ‘S’ |
np.unicode | unicode类型 | ‘U’ |
常用的类型有 int32、int64、float32、float64、uint8 等等
创建数组的时候可以指定类型
a = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float32)
a.dtype
b = np.array(['python', 'matplotlib', 'numpy', 'pandas'], dtype=np.string_)
b
三、基本操作
1. 生成数组的方法
▷ 生成0和1的数组
# 生成0的数组
np.zeros(shape=(3, 4), dtype='float32')
# 生成1的数组
np.ones(shape=(2, 3), dtype=np.int32)
▷ 从现有数组生成
array 和 asarray 的不同
np.array 和 np.copy 是深拷贝,原数组的改变不会影响到它们
np.asarray 是浅拷贝,会随着原数组的改变而改变
▷ 生成固定范围的数组
生成等间隔的序列
start:序列的起始值
stop:序列的终止值
如果 endpoint 为 true,该值包含于序列中
num:要生成的等间隔样例数量,默认为50
endpoint:序列中是否包含 stop 值,默认为 true
retstep:如果为 true,返回样例以及连续数字之间的步长
dtype:输出 ndarray 的数据类型
▷ 生成随机数组
np.ramdom模块
均匀分布
返回 [0.0, 1.0] 内的一组均匀分布的数
功能:从一个均匀分布 [low, high] 中随机采样,注意定义域是左闭右开,即包含 low,不包含 high
参数介绍:
low:采样下界,float 类型,默认值为 0
high:采样上界,float 类型,默认值为 1
size:输出样本数目,为 int 或元组类型,例如,size=(m, n, k),则输出 mnk 个样本,,缺省时输出 1 个值
返回值:ndarray 类型,其形状和参数 size 中描述的一致
从一个均匀分布中随机取样,生成一个整数或 N 维整数数组,取数范围:若 high 不为 None 时,取 [low, high] 之间随机整数,否则取值 [0, low] 之间的随机整数
均匀分布(Uniform Distribution)是概率统计中的重要分布之一,顾名思义,均匀,表示可能性相等的含义。均匀分布在自然情况下极为罕见,而人工栽培的有一定株行距的植物群落既是均匀分布
import matplotlib.pyplot as plt
import numpy as np
# 生成均匀分布的随机数
data1 = np.random.uniform(-1, 1, 100000000)
# 创建画布
plt.figure(figsize=(20, 8), dpi=80)
# 绘制直方图
plt.hist(data1, 1000)
# 显示图像
plt.show()
正态分布
功能:从标准正态分布中返回一个或多个样本值
loc:float
此概率分布的均值(对应着整个分布的中心)
scale:float
此概率分布的标准量(对应于分布的宽度,scale 越大越矮胖,越小越瘦高)
size:int or tuple of ints
输出的 shape,默认为 None,只输出一个值
返回指定形状的标准正态分布的数组
正态分布是一种概率分布,正态分布是具有两个参数 μ 和 σ 的连续型随机变量的分布,第一参数 μ 是服从正态分布的随机变量的均值,第二个参数 σ 是此随机变量的标准差,所以正态分布记作 N(μ, σ²)
正态分布的应用
生活、生产于科学实验中很多随机变量的概率分布都可以近似地用正态分布来描述
正态分布的特点
μ 决定了其位置,σ 决定了分布的幅度,当 μ = 0,σ = 1 时的正态分布是标准正态分布
标准差与方差的意义
标准差与方差如何计算便不多说了,属于高中的内容
可以理解成数据的一个离散程度的衡量
下面我们来写一个正态分布
import matplotlib.pyplot as plt
import numpy as np
# 正态分布
data2 = np.random.normal(loc=1.75, scale=0.1, size=1000000)
# 创建画布
plt.figure(figsize=(20, 8), dpi=80)
# 绘制直方图
plt.hist(data2, 1000)
# 显示图像
plt.show()
例如:我们可以模拟生成一组股票的涨跌幅的数据
2. 案例:随机生成股票交易日涨幅数据
比如,8 只股票,在两周内的涨跌幅数据,怎么获取?
股票涨跌幅数据的创建
stock_change = np.random.normal(loc=0, scale=1, size=(8, 10))
生成数据
数据的索引、切片
# 二维数组
stock_change[0, 0:3]

拓展:三维数组如何索引?
arr = np.array([[[1, 2, 3], [4, 5, 6]], [[11, 22, 33], [44, 55, 66]]])
arr[0, 1, 0:3]
形状修改
需求:让刚才的股票行、日期列反过来,变成日期行、股票列
返回新的 ndarray,原始数据没有改变
# 在转换形状的时候,一定要注意数组的元素匹配
stock_change.reshape([10, 8]) # 只是将形状进行了修改,但并没有将行列进行转换
stock_change.reshape([-1, 20]) # 数组的形状被修改为:(4, 20),-1:表示通过待计算
没有返回值,对原始的 ndarray 进行了修改
将数组的行、列进行互换
stock_change.T.shape
类型修改
stock_change.astype(np.int32)
转换成 bytes
arr = np.array([[[1, 2, 3], [4, 5, 6]], [[11, 22, 33], [44, 55, 66]]])
arr.tostring()
数组去重
temp = np.array([[1, 2, 3, 4], [3, 4, 5, 6]])
np.unique(temp)
四、ndarray运算
1. 逻辑运算
如果我们想要操作符合某一条件的数据时,该怎么做呢?
import numpy as np
stock_change = np.random.normal(loc=0, scale=1, size=(8, 10))
# 重新生成8只股票10个交易日的涨跌幅数据
stock_change = np.random.normal(0, 1, (8, 10))
stock_change = stock_change[0:5, 0:5]
# 需求一:逻辑判断,如果涨跌幅大于0.5就标记为True,否则为False
stock_change > 0.5
# 需求二:BOOL赋值,将满足条件的设置为指定的值-布尔索引
stock_change[stock_change > 0.5] = 1
stock_change
通用判断函数
np.all()
# 判断stock_change[0:2, 0:5]是否全部是上涨的
np.all(stock_change[0:2, 0:5] > 0)
False
np.any()
# 判断前5只股票这段期间是否有上涨的
np.any(stock_change[0:5, :] > 0)
True
np.where(三元运算符)
通过使用 np.where 能够进行更加复杂的运算
np.where(布尔值,True的位置的值,False的位置的值)
# 判断前4个股票前4天的涨跌幅,大于0的置为1,否则为0
temp = stock_change[:4, :4]
np.where(temp > 0, 1, 0)
复合逻辑需要结合 np.logical_and 和 np.logical_or 使用
# 判断前4个股票前4天的涨跌幅,大于0.5且小于1的,换为1,否则为0
np.where(np.logical_and(temp > 0.5, temp < 1), 1, 0)
# 判断前4个股票前4天的涨跌幅,大于0.5或者小于-0.5的,换为1,否则为0
np.where(np.logical_or(temp > 0.5, temp < -0.5), 1, 0)
2. 统计计算
如果想要知道涨幅或者跌幅最大的数据,应该怎么做?
(1)统计指标
在数据挖掘 / 机器学习领域,统计指标的值也是我们分析问题的一种方式,常用的指标如下:
Return the minimum of an array or minimum along an axis
返回数组的最小值或沿轴的最小值
Return the maximum of an array or maximum along an axis
返回数组的最大值或沿轴的最大值
Compute the median along the specified axis
沿指定轴计算中值
Compute the arithmetic mean along the specified axis
沿指定轴计算算术平均值
Compute the standard deviation along the specified axis
沿指定轴计算标准偏差
Compute the variance along the specified axis
沿指定轴计算方差
(2)股票涨跌幅统计运算
进行统计的时候,axis 轴的取值并不一定,Numpy 中不同的 API 轴的值都不一样,在这里,axis=0 代表列,axis=1 代表行去进行统计
import numpy as np
# 接下来对于这4只股票的4天数据,进行一些统计运算
temp = stock_change[:4, :4]
# 指定行去统计
print("前4只股票前4天的最大涨幅为{}".format(np.max(temp, axis=1)))
# 使用min,std,mean
print("前4只股票前4天的最大跌幅为{}".format(np.min(temp, axis=1)))
print("前4只股票前4天的波动程度为{}".format(np.std(temp, axis=1)))
print("前4只股票前4天的平均涨跌幅为{}".format(np.mean(temp, axis=1)))
如果需要统计出哪一只股票在某个交易日的涨幅最大或者最小?
# 获取股票指定哪一天的涨幅最大
print("前4只股票前4天内涨幅最大为{}".format(np.argmax(temp, axis=1)))
print("前4天1天内涨幅最大的股票为{}".format(np.argmax(temp, axis=0)))
3. 数组间运算
(1)场景
数据:
[[80, 86],
[82, 80],
[85, 78],
[90, 90],
[86, 82],
[82, 90],
[78, 80],
[92, 94]]
(2)数组与数的运算
arr = np.array([[1, 2, 3, 2, 1, 4], [5, 6, 1, 2, 3, 1]])
arr + 1
arr / 2
# 对比python列表的运算,看出区别
a = [1, 2, 3, 4, 5]
a * 3
(3)数组与数组的运算
arr1 = np.array([[1, 2, 3, 2, 1, 4], [5, 6, 1, 2, 3, 1]])
arr2 = np.array([[1, 2, 3, 4], [3, 4, 5, 6]])
arr1 + arr2 # 两个数组这样是不能进行运算的
(4)广播机制
执行 broadcast 的前提在于,两个 ndarray 执行的是 element-wise 的运算,Broadcast 机制的功能而是为了方便不同形状的 ndarray(numpy 库的核心数据结构)进行数学运算
当操作两个数组时,numpy 会逐个比较它们的 shape (构成的元组 tuple),只有在下述情况下,两个数组才能够进行数组与数组的运算
例如:
维度相等
Image (3d array): 256 x 256 x 3
Scale (1d array): 3
Result (3d array): 256 x 256 x 3
其中相对应的一个地方为 1
A (4d array): 9 x 1 x 7 x 1
B (3d array): 8 x 1 x 5
Result (4d array): 9 x 8 x 7 x 5
其中相对应的一个地方为 1
A (2d array): 5 x 4
B (1d array): 1
Result (2d array): 5 x 4
维度相等,其中相对应的一个地方为 1
A (3d array): 15 x 3 x 5
B (3d array): 15 x 1 x 1
Result (3d array): 15 x 3 x 5
最终结果是取最大维度
如果是下面这样,则不匹配:
A (1d array): 10
B (1d array): 12
A (2d array): 2 x 1
B (3d array): 8 x 4 x 3
思考:下面两个 ndarray 是否能够进行运算?
arr1 = np.array([[1, 2, 3, 2, 1, 4], [5, 6, 1, 2, 3, 1]])
arr2 = np.array([[1], [3]])
arr1 = np.array([[1, 2, 3, 2, 1, 4], [5, 6, 1, 2, 3, 1]]) # 2 x 6
arr2 = np.array([[1], [3]]) # 2 x 1
# 维度相等,其中相对应的一个地方为 1,可以进行运算
arr1 + arr2
4. 矩阵运算
现在再次回到最开始的学生成绩问题:
思考:如何能够直接得出每个学生的成绩呢?
(1)什么是矩阵
矩阵,英文 matrix,和 array 的区别是:矩阵必须是二维的,但是 array 可以是多维的
np.mat()
将数组转换成矩阵类型
a = np.array([[80, 86],
[82, 80],
[85, 78],
[90, 90],
[86, 82],
[82, 90],
[78, 80],
[92, 94]])
np.mat(a)
(2)矩阵乘法运算
矩阵乘法的两个关键:
形状改变:
(M行,N列)x (N行,L列)= (M行,L列)
必须符合上面的式子,否则运算出错!
运算规则:
设 A 为 m x p 的矩阵,B 为 p x n 的矩阵,那么称 m x n 的矩阵 C 为 A 与 B 的乘积,记作 C = AB,其中矩阵 C 中的第 i 行第 j 列元素可以表示为下式:
例如:
上式:(2,3)x(3,2)=(2,2)
左上 —— A第1行 x B第1列
右上 —— A第1行 x B第2列
左下 —— A第2行 x B第1列
右下 —— A第2行 x B第2列
直接得出每个学生的成绩:
a = np.array([[80, 86], # (8 x 2)
[82, 80],
[85, 78],
[90, 90],
[86, 82],
[82, 90],
[78, 80],
[92, 94]])
b = np.array([[0.3], # (2 x 1)
[0.7]])
a_mat = np.mat(a)
b_mat = np.mat(b)
a_mat * b_mat
(3)矩阵乘法API
a = np.array([[80, 86], # (8 x 2)
[82, 80],
[85, 78],
[90, 90],
[86, 82],
[82, 90],
[78, 80],
[92, 94]])
b = np.array([[0.3], # (2 x 1)
[0.7]])
np.matmul(a,b)
np.dot(a,b)
拓展解法:
a @ b
5. 合并、分割
合并、分割的用处:
实现数据的切分和合并,将数据进行切分合并处理
(1)合并
Stack arrays in sequence horizontally (column wise)
Stack arrays in sequence vertically (row wise)
实例:
np.hstack()
a = np.array((1, 2, 3))
b = np.array((2, 3, 4))
np.hstack((a, b))
a = np.array([[1], [2], [3]])
b = np.array([[2], [3], [4]])
np.hstack((a, b))
np.vstack()
a = np.array([1, 2, 3])
b = np.array([2, 3, 4])
np.vstack((a, b))
a = np.array([[1], [2], [3]])
b = np.array([[2], [3], [4]])
np.vstack((a, b))
np.concatenate()
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6]])
np.concatenate((a, b), axis=0)
np.concatenate((a, b.T), axis=1) # 。T 进行转置
比如我们将两部分股票的数据拼接在一起:
stock_change = np.random.normal(loc=0, scale=1, size=(8, 10))
a = stock_change[:2, 0:4]
b = stock_change[4:6, 0:4]
# axis=1时,按照数组的列方向拼接在一起
# axis=0时,按照数组的行方向拼接在一起
np.concatenate([a, b], axis=0)
np.concatenate([a, b], axis=1)
np.vstack((a, b))
np.hstack((a, b))
(2)切割
numpy.split(ary, indices_or_sections, axis=0)
Split an array into mutiple sub-arrays
x = np.arange(9.0)
x
np.split(x, 3)
x = np.arange(8.0)
x
np.split(x, [3, 5, 6, 10])
6. IO操作与数据处理
大多数数据并不是我们自己构造的,而是存在文件当中,需要我们用工具来获取
但是,Numpy 其实并不适合用来读取和处理数据,因此我们这里了解相关 API,以及 Numpy 不方便的地方即可
(1)Numpy读取
Load data from a text file, with missing values handed as specified
准备一个 “test.csv” 文件,内容如下
id,value1,value2,value3
1,123,1.4,23
2,110,,18
3,,2.1,19
data = np.genfromtxt("test.csv",delimiter=",")
data
可以看到,读取结果有缺失值
(2)如何处理缺失值
什么是缺失值?
什么时候 Numpy 中会出现 nan:当我们读取本地的文件为 float 的时候,如果有缺失 (或者为 None),就会出现 nan
缺失值处理
那么,如果我们单纯地把数据中的 nan 替换为 0,合适吗?会带来什么样的影响?
比如,全部替换为 0 后,替换之前的平均值如果大于 0,替换之后的均值肯定会变小,所以更一般的方式是把缺失的数值替换为均值(中值)或者是直接删除有缺失值的一行
所以:
# data中存在nan值,如何操作把其中的nan填充为每一列的均值
data = array([[ nan, nan, nan, nan],
[ 1. , 123. , 1.4, 23. ],
[ 2. , 110. , nan, 18. ],
[ 3. , nan, 2.1, 19. ]])
处理逻辑:
def fill_nan_by_column_mean(t):
for i in range(t.shape[1]):
# 计算nan的个数
nan_num = np.count_nonzero(t[:, i][t[:, i] != t[:, i]])
if nan_num > 0:
now_col = t[:, i]
# 求和
now_col_not_nan = now_col[np.isnan(now_col) == False].sum()
# 和/个数
now_col_mean = now_col_not_nan / (t.shape[0] - nan_num)
# 赋值给now_col
now_col[np.isnan(now_col)] = now_col_mean
# 赋值给t,即更新t的当前列
t[:, i] = now_col
return t
fill_nan_by_column_mean(data)
看了上面的处理过程,你肯定觉得太麻烦了吧,等我们学了 pandas 之后就好处理了
🧸 这次的分享就到这里啦,继续加油哦^^
🐱 我是程序喵,陪你一点点进步
🍭 有出错的地方欢迎在评论区指出来,共同进步,谢谢啦
来源:程序喵正在路上