系列前言
作为一名大二计算机学生,我有幸通过最新的课程学习了Python在数据分析领域的应用。在本系列博客中,我将与你分享我在学习过程中的洞见、技巧和实践经验,希望能够帮助你在这一领域取得进步。
对于阅读中涉及到的部分python语法,我会提供详细的解释和示例,帮助大家温故知新。同时,我也会在每篇博客的末尾推荐我参考的资源和网站,相信对你也同样有益。
一、基本概念
什么是金融量化分析?
金融量化分析是一种运用数学模型和计算机程序来辅助投资决策的方法。它通过分析大量的历史数据,识别出能够带来超额回报的模式,并据此制定投资策略。
这个过程通常包括以下几个步骤:
- 发现规律:通过数据分析,找出市场中能够带来利润的规律或趋势。
- 策略制定:将这些规律转化为具体的可执行策略。
- 程序编写:利用编程语言编写自动化交易程序,让计算机执行这些策略。
- 自动化交易:计算机根据预设的策略自动进行交易,减少人为干预和情绪影响。
金融量化分析的优势在于它能够帮助投资者在复杂的市场中快速做出决策,提高交易效率和盈利能力。它能够:
- 筛选股票:从众多股票中筛选出符合特定条件的投资标的。
- 确定时机:分析市场动态,确定买入、卖出或持仓的最佳时机。
- 风险管理:通过算法控制投资组合的风险水平。
- 客观决策:量化策略减少了人为情绪对投资决策的影响。
什么是数据分析?
- 定义:数据分析是基于特定目的,通过收集、整理、加工和分析数据来提炼有价值信息的过程。
- 过程:明确目的、数据收集、数据处理、数据分析、数据展现、分析报告。
- 目的:改进优化、发现机会、创造新价值、吸引和留住用户、增加收益、高层决策、市场营销、产品运营、客户服务等。
- 困境:缺乏可信性、可用性、高存储成本、信息提取困难。
为什么将python用于数据分析?
得益于其强大的数据分析和科学计算库,如Numpy、Pandas、Matplotlib和Scikit-learn等。
目前Python已经在这一方向大放异彩,加上它强大的调试能力以及工程能力,让我们分析的结果和需要执行的任务可以无缝结合,使得维护变得非常方便。
常用库简介
Numpy 运算方面
矩阵处理:NumPy是用于科学计算的一个开源Python扩充程序库,它为Python提供了高性能的数组与矩阵运算处理能力.
多维数组:NumPy为Python带来了真正的多维数组功能,并且提供了丰富的函数库处理这些数组。
向量处理:它将常用的数学函数都支持向量化运算,使得这些数学函数能够直接对数组进行操作,
运行速度上: 将本来需要在Python级别进行的循环,放到C语言的运算中,明显地提高了程序的运算速度。
Pandas 数据清洗和预处理
Pandas使我们进行数据分析的一个主要工具。它所包含的数据结构和数据处理工具的设计使得Python中进行数据清洗和分析非常快捷。
pandas一般也是和其他数值计算工具一起使用的,支持大部分Numpy语言风格的数组计算。
pandas和numpy最大的区别就是pandas是用来处理表格型或者异质性数据的,
而Numpy则刚好相反,它更适合处理同质型的数值类数组数据matplotlib
matplotlib 数据可视化
是最流行的用于绘制数据图表的python库。
它基本也是可视化这一区域的标杆,在许多情况下,它都是一个可靠、健壮的可视化工具。
对于一些标准的绘图工作,它比较容易理解,进行复杂的绘图还有自定义,它也很灵活。此外,它还与Numpy以及其提供的数据结杉密集成。
二、numpy库
开始之前
请确保您已经安装了NumPy库。如果您尚未安装NumPy,请不要担心,安装过程非常简单。打开您的终端或命令提示符,并输入以下命令:pip install numpy。
等待一会儿即可完成安装。
简介
NumPy是python中的一款高性能科学计算与数据分析的基础包;
NumPy定义了数组和矩阵,提供存储==单一类型==的多维数租(ndarray)和矩阵(Matrix)以及相关运算;
Numpy的数组和Python的基础类型list有什么区别?
Numpy是专门针对数组的操作和运算进行了设计,所以数组的存储效率和输入输出==性能远优于==Python中的嵌套列表,数组越大,Numpy的优势就越明显通常Numpy数组中的所有元素的类型都是相同的,而Python列表中的元素类型是任意的,所以在通用性能方面Numpy数组不及Python列表
在科学计算中,Numpy可以==省掉很多循环==语句,代码使用方面比Python列表简单的多,同时节省了存储和cpu的消耗。
numpy多维数组:
英文全名为N-dimensional array(ndarray)
numpy中提供了2种数据结构,用于支持高效的数据分析:多维数组和矩阵。本节首先学习多维数组ndarray.
ndarray基本特点:
# 它是一个多维数组列表
# 数组对象内的元素类型必须相同
# 数组大小不可修改
# 便于表示矩阵,向量,数组
关于“数组大小不可修改”:
在NumPy中,ndarray
对象的大小指的是数组所占用的内存空间,它是由数组的元素数量和每个元素的数据类型所占用的字节大小决定的。
如果允许用户随意改变数组的大小,就会破坏数据的连续性,从而影响性能,并且可能导致内存管理上的问题。
在后面提到的reshape方法,可以在不改变数据内容的前提下,改变数组的形状。但需要注意的是,新形状的数组必须能够无歧义地从原始数组中得到,即新形状的元素总数必须与原始数组相同。
引入问题
已知若干家跨国公司的市值(美元),将其换算为人民币
# Q:已知若干家跨国公司的市值(美元),将其换算为人民币
dollar_list = [random.uniform(100,200) for _ in range(100)] # 列表推导式 生成10000个随机浮点数
dollar_list_new:float = []
huilv:float = 7.998
# 做法1: for循环迭代出来
def answer_1():
for i in dollar_list:
dollar_list_new.append(i*huilv)
print(dollar_list_new)
# answer_1()
# 做法2:通过map方法和lambda函数映射
# map() 函数是一个内置的高阶函数,它可以接收一个函数和一个可迭代对象作为参数,
# 然后通过该函数对可迭代对象中的每个元素进行处理,返回一个新的迭代器。
# lambda函数是一种匿名函数,通常用于简单的函数操作。
def answer_2():
dollar_list_new = list(map(lambda x:x*huilv,dollar_list))
print(dollar_list_new)
# answer_2()
# 做法3:ndarray对象进行批量计算
def answer_3():
dollar_list_new = np.array(dollar_list) # 创建对象
dollar_list_new * huilv # 进行批量操作
print(dollar_list_new)
# answer_3()
从上述内容中,我们可以看到三种不同的方法来将一个包含跨国公司市值的美元列表转换为人民币。这些方法分别是使用for循环迭代、使用map
函数结合lambda
函数,以及使用NumPy的ndarray
对象进行批量计算。
通过将列表转换为NumPy的ndarray
对象,可以利用NumPy的向量化操作来进行批量计算。在ndarray
对象上执行的运算会自动应用到数组的每个元素上,无需显式地进行迭代。这种方法不仅代码简洁,而且效率非常高,因为NumPy底层使用C语言编写,可以执行快速的数学运算。
ndarray对象的构建
四种常用的构建方法
# 它是一个多维数组列表
# 数组对象内的元素类型必须相同
# 数组大小不可修改
# 构建方法1:使用np.array()方法 传入对应列表作为参数
li = [1,2,3,4]
arr1 = np.array(li)
print(arr1)
# [1 2 3 4]
print(f'type(li):{type(li)} type(arr1):{type(arr1)}')
# type(li):<class 'list'> type(arr):<class 'numpy.ndarray'>
# 构建方法2:可以是多维列表作为参数
li2 = [[1,2,3],[4,5,6],[7,8,9]]
arr2 = np.array(li2)
print(arr2)
'''
[[1 2 3]
[4 5 6]
[7 8 9]]
'''
# 构建方法3:带数据类型
li3 = [1,2,3,4]
arr3 = np.array(li3,dtype=complex)
print(arr3) # [1.+0.j 2.+0.j 3.+0.j 4.+0.j]
# 构建方法4:带最小维度
li4 =[1,2,3,4]
arr4 = np.array(li4,ndmin=3)
print(arr4) # [[[1 2 3 4]]]
关于如何理解 [[[1 2 3 4]]] 这个ndarray:
# 创建一个三维数组
array_3d = np.array([[[1, 2, 3, 4]]])
# 打印数组和它的维度信息
print(array_3d)
print("Array shape:", array_3d.shape)
# 执行这段代码后,你会得到如下输出:
# [[[1 2 3 4]]]
# Array shape: (1, 1, 4)
首先,这个表达式表示的是一个三维数组。在NumPy中,维度(dimension)是指数组的层级,每个层级可以看作是一个数组嵌套。具体来说:
- 第一维:只有一个元素,它本身是一个二维数组。
- 第二维:只有一个元素,它是一个一维数组,包含四个元素
[1, 2, 3, 4]
。 - 第三维:这个一维数组中的每个元素都是一个标量(单个数值),即
1
、2
、3
和4
向量,数组的有关说明
li = [1,2,3,4]
li3 = [[[1,2,3]]]
arr1 = np.array(li) # 从数学定义出发 数组arr1很方便表示一个向量
arr3 = np.array(li3) # 数组arr3则可以表示一个三维数组 延伸来看可以表示更高维的数组
ndarray的常用属性
li = [[1,2,3],
[4,5,6]]
arr = np.array(li)
arr.T # 数组转置2*3->3*2
arr.dtype # 数组元素的数据类型
arr.size # 数组元素的个数6
arr.ndim # 数组的维数2
arr.shape # 数组的维度大小2*3
arr.itemsize # 这一数组属性返回数组中每个元素的字节单位长度。 1byte = 8bit
arr.flags # 关于数据一些属性的设置
## 大家可以自行尝试这些属性对应的输出是什么
关于np.dtype
在NumPy中,可以通过np.dtype()来定义ndarray
的数据类型。dtype()中
指定了数组中每个元素的类型和存储方式,包括字节顺序(端记号)、基本类型和数组中的元素结构。
端记号和基本类型
端记号指定了多字节数据类型的字节顺序。常见的端记号有:
>
:大端(Big-endian),字节序从高位到低位存储。<
:小端(Little-endian),字节序从小位到高位存储。=
:系统默认的字节序。
基本类型是指存储在数组中的单个元素的类型。例如:
i4
:表示一个4字节大小的有符号整数,相当于int32
。np.int8
:表示一个1字节大小的有符号整数。S20
:表示一个20字节大小的字符串。
dt = np.dtype('>i4') # 定义了一种数据类型
print(dt) # >i4
结构化数据类型
下面的例子展示了结构化数据类型的使用
dt1 = np.dtype([('age', np.int8)])
# 定义一个结构化数据类型,包含一个名为'age'的字段,数据类型为8位整数
a = np.array([(10), (20), (30)], dtype=dt1)
# 创建一个结构化数组,包含三个元素,每个元素都是一个包含单个字段'age'的元组
print(a)
# >[(10,) (20,) (30,)] 带一个,表示每个元组一个元素
# 字段的作用在于,它们允许你以一种结构化的方式来访问和操作数组中的数据。例如,你可以使用字段名来访问特定元素的值:
# 访问第一个元素的'age'字段
age1 = a[0]['age']
print(age1) # 输出: 10
student = np.dtype([('name','S20'), ('age', 'i1'), ('marks', 'f4')])
stu_arr = np.array([('jenny', 21, 50),('tom', 18, 75)], dtype = student)
print(stu_arr) # [(b'jenny', 21, 50.) (b'tom', 18, 75.)]
print(stu_arr.shape) # (2,) 本质是个一维数组 2表示这个数组在第一维上有2个元素。
ndarray常用方法
调整数组形状
li = [[1,2,3],[4,5,6]]
arr = np.array(li) # 原ndarray为2行3列
arr_new = arr.reshape(3,2) # 调整为3行2列的ndarray
a = np.arange(1,10)
a1 = a.reshape(3,3)
print(a)
print(a1)
'''
[1 2 3 4 5 6 7 8 9]
[[1 2 3]
[4 5 6]
[7 8 9]]
'''
创建空数组 全零数组 全一数组
empty_00 = np.empty([2,3],dtype=float) # 创建了一个形状为(2,3)的空数组
zeros_00 = np.zeros((2,3),dtype=float) # [ 0. 0. ...]
zeros_01 = np.zeros((2,3),dtype=int) # [0 0 ...]
ones_00 = np.ones(5) # [ 1. 1. 1. 1. 1.] 默认类型基本为float
创建来自数值范围的数组
# numpy.arange(start, stop, step, dtype)
# 左闭右开
x = np.arange(1,4) # [1 2 3]
# numpy.linspace(start, stop, num, dtype)
# 此函数类似于arange()函数。 在此函数中,指定了num表示生成的样本数量 使在范围之内 各样本间隔均匀
x = np.linspace(10,20,5) # [10. 12.5 15. 17.5 20.]
# 很像等差数列
# numpy.logspace(start, stop, num, dtype)
# 此函数返回一个ndarray对象,其中包含在对数刻度上均匀分布的数字。
# 刻度的开始和结束端点是某个底数的幂,通常为 10。
x = np.logspace(1,10,5) # [ 1. 2.26894909 4. 7.07945784 10.]
# 这个数组中的数值是按照10的幂等比递增的,从10^0到10^1,共5个数值。
创建随机数数组 np.random
# 随机整数数组
# np.random.randint(low,high,size,dtype)
a = np.ramdom.randint(1,5,(2,3))
print(a)
# 从标准正态分布(均值为 0,方差为 1)中随机采样的浮点数
a1 = np.random.rand(4,3,1) # 三维
print(a1)
# randn函数返回一个或一组样本,具有==标准正态分布==。
# dn表示每个维度元素的个数;返回值为指定维度的array
ndarray切片和索引
基本概念
索引是指通过指定数据结构中元素的具体位置来访问或选择单个元素的过程。在 NumPy 数组和 pandas DataFrame 中,你可以使用[ ]来访问单个元素。索引可以是整数、标签或布尔数组,用于选择特定的行、列或元素。
切片是指通过指定数据结构中元素的连续范围来访问或选择多个元素的过程。在 NumPy 和 pandas 中,切片使用冒号 :
来表示,可以指定开始、结束和步长,从而选择一个子数组或子 DataFrame。
切片可以看作是索引的扩展,它提供了一种更灵活的方式来访问数据,允许你通过指定范围来提取数据子集。
基本和高级索引
a = np.arange(1,10,1)
## a[start:end:step]
a[1] # 索引从0开始;1代表第2个索引位置
a[1:3] # 1表示索引从1开始 3表示索引结束位置 由于同样遵循“左闭右开”,所以不包括a[3],只有a[1]和a[2]
a[:3] # 未指定起始索引,就是从0开始,所以含义就是a[0],a[1],a[2]
a[-1] # 最后一个元素的索引,就用-1表示,倒数第2个元素的索引,就是-2,依次类推;
a[::-1] # 步长=-1时,起到了倒着索引的作用;
### 更多情况可以以此类推 亲自尝试一下
# 也可以适用于多维
a = np.array([[1,2,3],[3,4,5],[4,5,6]])
print a[1:] # [[3 4 5],[4 5 6]]
# 切片还可以包括省略号(...),来使选择元组的长度与数组的维度相同。 如果在行位置使用省略号,它将返回包含行中元素的ndarray。
print a[...,1] # 第二列的元素(选中所有行,选中第二列 即第二列的元素)
print a[1,...] # 第二行的元素(1选中第二行 ...表示所有列 即第二行的所有元素)
print a[...,1:] # 第二列及其剩余元素
## 高级索引 ->[行索引,列索引]
x = np.array([[1, 2], [3, 4], [5, 6]])
y = x[[0,1,2], [0,1,0]] # 等价于以下代码:y = np.array([x[0, 0], x[1, 1], x[2, 0]])
# >[1 4 5]
x = np.array([[ 0, 1, 2],[ 3, 4, 5],[ 6, 7, 8],[ 9, 10, 11]])
rows = np.array([[0,0],
[3,3]]) # 定义行索引
cols = np.array([[0,2],
[0,2]]) # 定义列索引
y = x[rows,cols]
print y >[(0,0) (0,2)
(3,0) (3,2)]
即[[ 0 2]
[ 9 11]]
# 高级和基本索引可以通过使用切片:或省略号...与索引数组组合。
x = np.array([[ 0, 1, 2],[ 3, 4, 5],[ 6, 7, 8],[ 9, 10, 11]])
z = x[1:4,1:3]
print(z)
[[ 4 5]
[ 7 8]
[10 11]]
y = x[1:4,[1,2]] # 同z一样
布尔索引
~表示取反 &表示与 |表示或 ><大于小于 ==等价于 !=不等价于
x = np.array([[ 0, 1, 2],[ 3, 4, 5],[ 6, 7, 8],[ 9, 10, 11]])
print x[x > 5] # 打印出大于 5 的元素 >[ 6 7 8 9 10 11]
a = np.array([np.nan, 1,2,np.nan,3,4,5])
print a[~np.isnan(a)]
# 1 ~(取补运算符)来过滤NaN
# 2 np.isnan(a) 获取 NaN 值的布尔掩码
# 3 NaN(Not a Number)值
# 过滤掉非复数元素
a = np.array([1, 2+6j, 5, 3.5+5j])
print a[np.iscomplex(a)]
矩阵(Matrix)
矩阵(Matrix)是Numpy提供的另外一种数据类型;前面使用的以及操作的都是数组
(numpy.ndarray)。使用矩阵的好处是:它可以支持各种矩阵运算,而数组就不行。
def matrix_test():
list1 = [
[1,2,3],
[4,5,6]
]
mat1 = np.mat(list1)
list2 = [
[1,2],
[3,4],
[5,6]
]
mat2 = np.mat(list2) # 矩阵的创建
print(mat1)
print(type(mat1))
print(mat1*8) # 矩阵的数乘
print(mat1*mat2) # 矩阵的乘法
print(mat1.T) # 矩阵的转置
## np.diag 是转为对角数组 此时还不是矩阵 不能进行矩阵的相关运算
list3 = [1,4,5]
mat3 = np.diag(list3) # 列表转成对角数组
print(mat3)
print(type(mat3)) # 此时还是数组
real_mat3 = np.mat(mat3)
print(type(real_mat3)) # 芝士对角矩阵
array = np.zeros([2,3])
mat4 = np.mat(array)
print(mat4)
print(type(mat4))# 生成全0矩阵 默认dtype为浮点
array1 = np.zeros([2,3],dtype=int) # 自定义dtype
mat5 = np.mat(array1)
print(mat5)
print(type(mat5))
广播(broadcasting)
术语广播是指 NumPy 在算术运算期间处理不同形状的数组的能力。
对数组的算术运算通常在相应的元素上进行。 如果两个阵列具有完全相同的形状,则这些操作被无缝执行。
广播(broadcasting)是一种机制,它允许不同形状的数组在进行逐元素操作时,自动地进行形状的调整,使得它们可以进行有效的计算。
如果你有一个形状为 (3, 3) 的数组和一个标量(形状为 1),当你将这两个数组相加时,广播机制会自动将标量扩展成一个与 (3, 3) 相同形状的数组,然后再进行元素相加。
这样帮助我们避免繁琐的形状匹配步骤,让代码更易读和高效。
较小的数组会广播到较大数组的大小,以便使它们的形状可兼容。
简单来说,广播规则为:两个数组在某个维度上的 shape 相等,或其中一个数组在某个维度上的 shape 为 1(数组在这个维度上只有一个元素)
def broadcasting_test():
a = np.array([[0.0,0.0,0.0],[10.0,10.0,10.0],[20.0,20.0,20.0],[30.0,30.0,30.0]])
b = np.array([1.0,2.0,3.0])
print(a + b)
arr1 = np.arange(24).reshape(3,4,2)
arr2 = np.arange(8).reshape(4,2)
print(arr1 + arr2)
arr3 = np.array([
[1],
[2],
[3],
[4]
])
print(a+arr3)
Universal functions(ufunc)
是一种能对ndarray的每个元素进行操作的函数。ufunc的输入数据既可以是1个数ndarray,也可以是多个ndarray.
def ufunc_test():
a = np.array([10,10,20,30,45,60,75,90])
# 角度转弧度pi/180x角度;弧度变角度 180/pix弧度
b = a*np.pi/180
c = np.sin(b)
# 这里np.sin是一个ufunc函数,因此它对x中的每个元素求正弦值,然后将结果返回,并且赋值给c。
# 计算之后a、b中的值并没有改变,而是新创建了一个数组c保存结果。
# 如果希望将sin函数所计算的结果直接覆盖到数组b上去的话,可以将要被覆盖的数组作为第二个参数传递给ufunc函数。
print(a)
print(b)
print(c)
b1 = a*np.pi/180
print(b1)
np.sin(b1,b1) # sin(被操作的数组,覆盖的数组)
print(b1)
# numpy 中有众多的ufunc函数提供各式各样的计算。除了sin这种单输入函数之外,还有许多多个输入的函数,比如,add函数。
a = np.arange(1,6)
b = np.arange(2,7)
print(np.add(a,b)) # a,b对应相加
print(type(np.add(a,b)))
## 关于ufunc的性能 numpy 内置的许多ufunc函数都是在C语言级别实现的,因此计算速度非常快。
自定义ufunc(frompyfunc函数)
通过组合标准的ufunc函数的调用,可以实现各种算式的数组计算。不过有些时候这种算式不易编写,
而针对每个元素的计算函数却很容易用Python实现,
这时可以用 frompyfunc函数将一个计算单个元素的函数转换成ufunc函数。这样就可以方便地用所产生的ufunc函数对数组进行计算。
# 现有一计算三角波某点y坐标的函数
def triang1e_wave(x,c,c0,hc):
x = x - int(x) # 三角波的周期为1,因此只取x坐标的小数部分进行计算
if x >= c:
r=0.0
elif x < c0:
r = x / c0 * hc
else:
r =(c-x)/(c-c0)*hc
return r
# triangle_wave函数只能计算单个数值,不能对数组直接进行处理。
# 可以用下面的方法先使用列表解析(List comprehension),计算出一个list,然后用 array函数将列表转换为数组
def ufunc_test1():
# 列表解析式做法
# x = np.linspace(0,2,1000)
# print(type(x))
# y = np.array([triang1e_wave(t,0.6,0.4,1.0) for t in x])
triang1e_wave_ufunc = np.frompyfunc(triang1e_wave,4,1)
x = np.array([0.3,2.7,3])
y = triang1e_wave_ufunc(x,0.6,0.4,1.0)
print(y)
## frompyfunc的调用格式为frompyfunc(func,nin,nout)
# 其中 nin是参数输入参数的个数 nout是返回值个数
结语
以上即是本篇的全部内容,下一篇内容会是关于numpy库的实践。本篇部分内容有参考网站:Egonlin.com和来自课堂的资料文档,如果有机会我会尝试上传。
这是博主的处女作(),如果对你有帮助,欢迎点赞收藏关注!