面向金融的深度学习-提前发布--全-
面向金融的深度学习(提前发布)(全)
原文:Deep Learning for Finance (Early Release)
译者:飞龙
第一章:介绍数据科学和交易
开始学习复杂主题的最佳方法是逐渐建立动力,直到你开始完成拼图。了解金融深度学习需要对基本和中级数据科学主题以及金融市场及其结构有一定的了解。
本章为深入了解数据科学及其用途以及金融市场以及如何从数据科学中受益的构建基础。
在本章结束时,你应该知道数据科学是什么,它的应用是什么,以及你如何在金融领域中利用它来提取价值。
理解数据
在理解数据科学领域之前,首先要了解数据的类型和结构是不可能的。毕竟,这个庞大领域的名称中的第一个词就是“数据”。那么数据是什么?更重要的是,你可以用它做什么?
数据在其最简单和最纯粹的形式中是一组原始信息,可以是任何类型(数值、文本、布尔等)。
收集数据的最终目的是决策。这是通过一个复杂的过程来完成的,从收集和处理数据到解释数据并使用结果做出决策。
让我们举一个使用数据做决策的例子。假设你有一个由五种不同等权重的股息支付股票组成的投资组合,如表 1-1 所示。
表 1-1。股息表
| 股票 | 股息率 |
|---|---|
| A | 5.20% |
| B | 3.99% |
| C | 4.12% |
| D | 6.94% |
| E | 5.55% |
注意
股息是公司利润向股东支付的款项。股息率是以货币单位分配的金额除以公司当前股价。
分析这些数据可以帮助你了解你的投资组合平均股息率。平均值基本上是总和除以数量,它快速展示了投资组合的整体股息率:
因此,你的投资组合的平均股息率为 5.16%。这些信息可以帮助你将你的平均股息率与其他投资组合进行比较,以便知道是否需要进行任何调整。
你可以计算的另一个指标是投资组合中持有的股票数量。这可能是构建分散化墙的第一个信息砖块。尽管这两个信息(平均股息率和投资组合中的股票数量)非常简单,但复杂的数据分析始于简单的指标,有时可能不需要复杂的模型来正确解释正在发生的事件。
你在前面例子中计算的两个指标被称为平均值(或均值)和计数(或元素数量)。它们也是一个称为描述性统计的领域的一部分,这也是数据科学的一部分。
让我们再举一个数据分析的推断性目的的例子。假设你已经计算出两个货币对之间的年度相关性度量,并且你想预测下一个年度的相关性是正面还是负面。表 1-2 中有计算的详细信息。
表 1-2。相关性表
| 年 | 相关性 |
|---|---|
| 2015 | 正面 |
| 2016 | 正面 |
| 2017 | 正面 |
| 2018 | 负面 |
| 2019 | 正面 |
| 2020 | 正面 |
| 2021 | 正面 |
| 2022 | 正面 |
| 2023 | 正面 |
注意
相关性是两个时间序列之间线性依赖的度量。正相关通常意味着两个时间序列平均朝着相同方向移动,而负相关通常意味着两个时间序列平均朝着相反方向移动。
从表 1-2 可以看出,这两种货币对之间的历史相关性大多数情况下是正相关的,占大约 88%的时间。考虑到历史观察,您可以说下一个相关性测量值为正的概率为 88%。这也意味着下一个相关性测量值为负的概率为 12%:
这是另一个简单的例子,说明如何使用数据推断观察结果并做出决策。当然,这里的假设是历史结果确切地反映未来结果,这在现实生活中是不太可能的,但偶尔,为了预测未来,您所拥有的只有过去。
现在,在讨论数据科学之前,让我们回顾一下可能存在的数据类型,并将它们分成不同的组。
数值数据
这种类型的数据由反映在定期或不定期间隔内收集的某种类型信息的数字组成。示例可以包括市场数据(OHLC¹,成交量,价差等)和财务报表数据(资产,收入,成本等)。
分类数据
可以使用名称或标签将数据组织成组或类别的数据。它是定性的而不是定量的。例如,患者的血型是一种分类数据类型。
文本数据
随着自然语言处理(NLP)的发展,文本数据在最近几年中不断增长。机器学习模型使用文本数据来翻译、解释和分析文本的情感。此外,您甚至可以使用这些模型创建一个输出结构化段落的算法。
视觉数据
图像和视频也被视为数据,您可以处理和转换它们为有价值的信息。例如,卷积神经网络(CNN)是一种可以通过标签识别和分类照片的算法(例如,将猫照片标记为猫)。
音频数据
音频数据非常有价值,可以帮助节省转录时间。例如,您可以在音频上使用算法创建字幕和自动字幕。您还可以创建模型,通过说话者的语调和音量来解释说话者的情感。
在处理 Python 时,您可能会遇到以下数据:
整数
这些是整数,可以是正数或负数。例如,−8 和 745。但是,它们的范围限制在−2147483648 和 2147483647 之间。
浮点数
这些是带有小数点的实数,例如 18.54 和 311.52。
字符串
这些是存储在变量中的单词。更科学地说,它们是一组结构化字符(文本)。在 Python 中,您可以在单引号或双引号之间编写字符串。
布尔值
这些是用来评估条件的真假陈述。
数据科学是一门跨学科领域,试图使用不同的技术和模型从数据中提取智能和结论,无论是简单还是复杂。数据科学的过程除了分析数据之外还包括许多阶段。以下总结了数据科学的不同阶段:
-
数据收集:这个过程涉及从可靠和准确的来源获取数据。计算机科学中广为人知的一句话通常归功于乔治·弗舍尔,如下所示:“垃圾进,垃圾出”,这概括了需要有可靠的数据进行正确分析的必要性。基本上,如果您有不准确或有缺陷的数据,那么您的所有过程都将无效,这将是浪费时间。
-
数据预处理:有时,当您获取数据时,它可能是原始形式,需要在数据分析步骤中为数据科学模型做准备。例如,删除一些不必要的数据,处理缺失数据,或消除无效和重复数据都是预处理阶段的一部分。其他更复杂的例子可能包括数据的归一化和去噪。这一步的目的是为了让数据准备好进行分析。
-
数据探索:这是数据分析的第一步,是基本的统计探索,以找到趋势和不同属性,以便您对预期行为有初步了解。一个例子是检查数据的平稳性,这是本书中详细讨论的概念。
-
数据可视化:这是前一步骤的重要补充。它包括创建直方图和热图等可视化,以帮助识别模式和趋势,并使解释更容易。
-
数据分析:这是期待已久的步骤,基本上是数据科学过程的主要焦点。这是您使用不同的学习模型来拟合数据,以便它们根据给定参数解释和预测未来结果的地方。
-
数据解释:这个阶段涉及模型完成工作后的反馈和结论。优化也可能是这个阶段的一部分,然后循环回到第 5 阶段,以便在解释之前再次运行具有更新参数的模型,并评估性能。
注意
总结前面的观点,数据科学包括许多步骤,从获取数据开始,通过解释和优化预测数据未来值的模型。
让我们以 Python 中的一个简单示例来应用前面六个步骤中讨论的数据科学过程。假设您想分析和预测 VIX,这是芝加哥期权交易所每日发布的波动率时间序列指标。
注意
我称之为零步骤的隐藏步骤,这是整个过程的思想和直觉。如果没有首先有动机,您就不会应用这个过程。例如,相信通货膨胀数字可能推动某些大宗商品的回报是一个开始探索数据以寻找证明这一假设的真实数字的想法和动机。
第一步是数据收集,在这种情况下可以使用 Python 自动化。下一个代码块连接到圣路易斯联邦储备银行的网站,并下载 1990 年 1 月至 2023 年 1 月之间的 VIX 历史数据。
注意
VIX代表波动率指数,它代表标准普尔 500 指数的隐含波动率。自 1993 年以来一直可用,由芝加哥期权交易所(CBOE)发行。
由于它旨在衡量股市中的恐惧或不确定性水平,VIX 经常被称为恐惧指数。该指数是一个百分比,是通过对标准普尔 500 指数期权的定价计算得出的。较高的 VIX 值与更大的市场动荡和不确定性相关,而较低的值与平均更大的稳定性相关。
请注意,第三章完全致力于介绍 Python 并利用其功能。目前,您不必理解代码,因为这还不是学习成果:
# Importing the required library
import pandas_datareader as pdr
# Setting the beginning and end of the historical data
start_date = '1990-01-01'
end_date = '2023-01-23'
# Creating a dataframe and downloading the VIX data using its code name and its source
vix = pdr.DataReader('VIXCLS', 'fred', start_date, end_date)
# Printing the latest five observations of the dataframe
print(vix.tail())
该代码使用pandas库导入DataReader函数,该函数从各种来源(如 Yfinance 和 Fred)在线获取历史数据。DataReader函数以数据名称作为第一个参数,然后是来源和日期。print(vix.tail())的输出显示在表 1-3 中:
表 1-3。代码输出
| 日期 VIXCLS |
|---|
| 2023-01-17 19.36 |
| 2023-01-18 20.34 |
| 2023-01-19 20.52 |
| 2023-01-20 19.85 |
| 2023-01-23 19.81 |
让我们继续第二步:数据预处理。我将这部分分为检查无效数据和转换数据,使其准备好使用。在处理时间序列时,特别是下载的时间序列时,有时可能会遇到 NaN 值,这些值不是数字,因为其相应单元格中没有适当的输入。
注意
NaN代表不是数字,它是由于缺失、无效或损坏的数据而发生的。
您可以以许多方式处理 NaN 值。为了本例,让我们看看处理这些无效值的最简单方法,即消除它们。但首先,让我们编写一个简单的代码,输出数据框中 NaN 值的数量,以便您了解要删除多少值:
# Importing the required library
import pandas as pd
# Checking if there are NaN values in the VIX dataframe previously imported
count_nan = vix['VIXCLS'].isnull().sum()
# Printing the result
print('Number of NaN values in the VIX dataframe: ' + str(count_nan))
该代码使用isnull()方法并对其得到的数字求和,从而得出 NaN 值的数量。前面代码片段的输出如下:
Number of NaN values in the VIX dataframe: 292
现在您知道要删除多少行,您可以使用以下代码删除 NaN 行,从而清理数据框中的任何无效输入:
# Dropping the NaN values from the rows
vix = vix.dropna()
第二步的第二部分是转换数据。数据科学模型通常喜欢平稳数据,即具有稳定统计特性(如均值和标准差)的数据。
注意
平稳性的概念和所需的统计指标在第二章中有详细讨论。现在,您需要知道的是,在使用数据科学模型时,您可能需要将原始数据转换为平稳数据。
要将 VIX 数据转换为平稳数据,您可以简单地取相对于前一个值的差异。这类似于将价格数据转换为收益数据。以下代码片段将之前定义的 VIX 数据框转换为理论上暗示的平稳数据:
# Taking the differences in an attempt to make the data stationary
vix = vix.diff(periods = 1, axis = 0)
# Dropping the first value of the data frame
vix = vix.iloc[1: , :]
第三步是数据探索,这完全是关于理解您面前的数据,从统计学角度来说。由于您将在下一章节中详细了解统计指标,我将限制讨论仅计算数据集的均值。
均值就是如果数据集中的值要选举一位领袖,那么可以代表其他值的值。它是值的总和除以它们的数量。均值是描述性统计世界中最简单的统计量,也是最常用的统计量。以下公式显示了一组值的均值的数学表示:
使用pandas,您可以轻松计算数据集的均值如下:
# Calculating the mean of the dataset
mean = vix["VIXCLS"].mean()
# Printing the result
print('The mean of the dataset = ' + str(mean))
前面代码片段的输出如下:
The mean of the dataset = 0.0003
下一步是数据可视化,这在大多数情况下被认为是有趣的步骤。让我们通过时间绘制 VIX 的差异值。以下代码片段绘制了图 1-1 中显示的 VIX 数据:
# Importing the required library
import matplotlib.pyplot as plt
# Plotting the latest 250 observations in black with a label
plt.plot(vix[-250:], color = 'black', linewidth = 1.5, label = 'Change in VIX')
# Plotting a red dashed horizontal line that is equal to the calculated mean
plt.axhline(y = mean, color = 'red', linestyle = 'dashed')
# Calling a grid to facilitate the visual component
plt.grid()
# Calling the legend function so it appears with the chart
plt.legend()
# If you are using a terminal or a script, you might want to add plt.show()

图 1-1。自 2022 年初以来 VIX 的变化
第 5 步和第 6 步,数据分析和数据解释,是您将在本书中深入研究的内容,所以让我们暂时跳过它们,集中在数据科学的介绍部分上。
在继续之前,让我们回到无效或缺失数据的问题。有时,数据是不完整的,有缺失的单元格。尽管这可能会影响算法的预测能力,但不应该阻止您继续分析,因为有快速修复方法可以帮助减轻空单元格的负面影响。例如,考虑表 1-4:
表 1-4。季度 GDP
| 季度 | GDP |
|---|---|
| Q1 2020 | 0.9% |
| Q2 2020 | 1.2% |
| Q3 2020 | 0.5% |
| Q4 2020 | 0.4% |
| Q1 2021 | #N/A |
| Q2 2021 | 1.0% |
| Q3 2021 | 1.1% |
| Q4 2021 | 0.6% |
表格包含了一个假想国家的季度国内生产总值(GDP)。请注意表格中缺少了 2021 年 Q1 的数值。有三种基本方法可以解决这个问题:
-
删除包含缺失值的单元格:这是在数据科学过程的先前示例中使用的技术。它简单地认为时间戳不存在。这是最简单的修复方法。
-
假设缺失单元格等于前一个单元格:这种技术假设当前缺失值等于其之前的值。这也是一个简单的修复方法,旨在平滑数据而不是完全忽略问题。
-
计算空值周围单元格的平均值或中位数: 这种技术将平滑推进一步,假设缺失值等于前一个值和后一个值之间的平均值。
数据科学涵盖了一系列数学和统计概念。它需要对机器学习算法有深入的了解,比如决策树、随机森林和神经网络。这些概念被详细讨论,但也以易于理解的方式呈现,以便技术和非技术读者都能从直觉中受益。许多模型被认为是黑匣子,这其中有一些真相,但数据科学家的工作是首先理解模型,然后解释其结果。这有助于理解这些模型的局限性。
本书使用 Python 作为创建算法的首选编程语言。正如前面提到的,第三章介绍了 Python 以及必要的知识,以了解如何操作和分析数据,同时提供了创建不同模型的基础,这些模型比您想象的要简单。
在继续下一节之前,让我们看看数据存储的概念。毕竟,数据很有价值,但您需要将其存储在一个容易获取和分析的地方。
数据存储指的是用于将数据存储和组织起来以供将来分析的技术和领域。数据以许多格式存储,其中包括常见的格式如 CSV 和 XLSX 文件。其他类型的格式可能包括 XML、JSON,甚至是图片的 JPG 格式。格式的选择取决于数据的结构和组织。
数据也可以存储在云端或本地,具体取决于存储容量和成本。例如,您可能希望选择将历史 1 分钟的苹果股票数据存储在云端,以节省本地计算机上的空间,而不是将其保存在 CSV 文件中。
在 Python 中处理时间序列时,您主要会处理两种类型的数据存储:数组和数据框。让我们看看它们是什么:
数组
数组用于存储相同类型的元素。通常,最好将同质数据集(如数字)存储在数组中。当您进行无需时间戳的回测时,就会发生这种情况。在 Python 中处理数组的最常用库是numpy。
数据框
数据框是一个可以容纳各种类型数据(如浮点数、字符串等)的二维数据结构。它可以类比为具有列和行的表格。当您进行需要时间戳及其相应值的回测时,就会发生这种情况。在 Python 中处理数据框的最常用库是pandas。
总的来说,当需要高效存储同质数据集时,应使用数组。当处理异构数据或需要以表格方式编辑和分析数据时,应使用数据框。
注意
数据科学不断发展。新的存储方法正在不断发展,以使其更高效,提高容量和速度。
理解数据科学
数据科学已迅速成为技术和进步中不可或缺的一部分。算法依赖于数据科学工具提供的信息来执行其任务。但算法是什么呢?
算法是一组有序的程序,旨在完成某项活动或解决特定问题。算法可以简单到抛硬币,也可以复杂到 Risch 算法。
让我们来看一个非常简单的算法,更新图表平台所需的财务数据:
-
连接服务器和在线数据提供商。
-
复制带有最新时间戳的财务数据。
-
将数据粘贴到图表平台。
-
回到第一步,重新执行整个过程。
这就是算法的本质:执行一组有限或无限目标的指令。
注意
您在上一节看到的六个数据科学阶段也可以被视为一种算法。
交易策略也是算法,因为它们有明确的规则来启动和清算头寸。一个交易策略的例子是市场套利。
套利是一种旨在从不同交易所报价的同一资产的价格差异中获利的交易策略。这些价格差异是套利者通过他们的买卖活动消除的异常。考虑一只股票在不同国家的交易所 A 和交易所 B 上交易(为简单起见,这两个国家使用相同的货币)。自然地,股票必须在两个交易所上以相同的价格交易。当这个条件不成立时,套利者就会从他们的巢穴中出来狩猎。
他们在便宜的交易所买入股票,立即在更昂贵的交易所卖出,从而确保几乎无风险的利润。这些操作速度极快,因为差异不会持续很长时间,这是由于套利者的强大力量和速度。这里有一个明显的例子:
-
交易所 A 的股票价格= $10.00
-
交易所 B 的股票价格= $10.50
在这种情况下,套利者的算法将执行以下操作:
-
在交易所 A 以$10.00 买入股票。
-
在交易所 B 立即以$10.50 卖出股票。
-
把差额($0.50)放进口袋,直到差距被消除。
注意
交易和执行算法可能非常复杂,需要专业知识和一定的市场优势。
到目前为止,您应该了解数据科学的主要用途:数据解释和预测:
数据解释
也常被称为商业智能或简单的数据智能。部署算法的目的是了解数据的内容和方式。
数据预测
也常被称为预测分析或简单的预测。部署算法的目的是了解数据的下一步。
在金融市场中使用学习算法的主要目的主要是为了预测数据,以便您可以做出明智的交易决策,以实现资本增值的成功率高于随机。这是通过我在本书中讨论的许多简单和复杂算法来实现的。这些学习算法或模型可以分类如下:
监督学习
监督学习算法是需要标记数据才能运行的模型。这意味着您必须提供数据,以便模型在这些过去的值上进行训练,并了解隐藏的模式,以便在遇到新数据时能够提供未来的输出。监督学习的例子包括线性回归算法和自回归综合移动平均(ARIMA)模型。更复杂的模型包括支持向量回归算法(SVR)和神经网络。将多个层添加到神经网络中将其转变为具有高分析复杂多层数据能力的深度学习模型。所有这些算法将在本书后面更深入地讨论。
无监督学习
无监督学习算法是不需要标记数据就能运行的模型。这意味着它们可以使用未标记的数据来完成工作,因为它们被设计为自行发现隐藏的模式。示例包括聚类算法和主成分分析(PCA)。
强化学习
强化学习算法是一种不需要任何数据的模型,因为它们会自行发现环境并从中学习。与监督和无监督学习模型相反,强化学习模型通过从环境获得的反馈来获取知识,通过奖励系统。由于这通常应用于代理与环境互动并学习采用随时间最大化奖励的行为的情况,它可能不是时间序列回归的首选算法。另一方面,它可以用于开发可应用于时间序列数据以创建预测的策略。
您可能已经注意到,本书的标题是金融深度学习。这意味着除了其他学习模型外,我将花费相当大的篇幅讨论深度学习模型,并使用它们编写交易策略。这也意味着本书的重点主要是讨论神经网络及其不同变体。
深度监督学习模型(如深度神经网络)可以学习数据的分层表示,因为它们包含许多层,每一层在不同抽象级别提取特征。因此,深度模型学习了深层次和复杂的模式,这可能是浅层模型难以学习的。
另一方面,浅层监督学习模型(如线性回归)在学习复杂的非线性关系方面能力有限。但是,它们需要较少的计算工作量,因此速度更快。
数据科学算法如今几乎随处可见,不仅仅在金融领域。例如:
-
商业分析:使用数据分析优化定价、预测客户流失或改进营销举措。
-
医疗保健:通过深入分析患者数据来改善患者结果、寻找创新疗法或降低医疗成本。
-
体育:通过体育数据分析来提高团队表现、球员搜寻或投注。
-
研究:分析数据以支持科学研究、证明理论或获取新知识。
现在,当有人谈论数据科学应用时,了解数据科学家的工作内容会有所帮助。
数据科学家必须评估和理解复杂数据,以获取见解并为决策提供指导。这包括开发统计模型、应用机器学习技术和可视化数据等常见任务。他们支持数据驱动解决方案的实施,并向利益相关者通报结果。
另一方面,数据工程师负责构建和维护支持数据科学项目所需的基础设施和工具。这包括构建和执行数据管道、优化数据存储和检索、构建和维护大数据处理系统等任务。他们还与数据科学家密切合作,确保他们获取所需的研究数据。
换句话说,数据科学家更关注数据的解释和分析,而数据工程师更关注收集、存储和分析数据所需的工具和基础设施。
到目前为止,您应该已经了解了开始数据科学所需的一切。让我们介绍本书的第二个主要主题:金融市场。毕竟,本书旨在展示如何创建数据科学模型和算法,并将它们应用于金融数据,以从中提取预测价值。
金融市场和交易简介
本书的目的是以实践的方式应用不同的学习模型来创建不同的交易策略。因此,了解交易和金融市场运作方式至关重要。
金融市场是人们可以交易金融工具(如股票、债券和货币)的地方。买卖行为被称为交易。购买金融工具的主要目的之一是资本增值。买家相信该工具的价值大于其价格,因此买家购买股票(多头)并在他们认为当前价格等于当前价值时出售。相反,交易者也可以在工具价格下跌时赚钱。这个过程被称为卖空,在期货和外汇(FX)等某些市场中很常见。
卖空的过程包括从第三方借入金融工具,在市场上卖出,然后在价格下跌之后买回,并在市场价格将其归还给第三方。理想情况下,由于你预计工具的价格会下跌,你会以更便宜的价格买回(在价格下降后),并以市场价格将其归还给第三方,从而获得差价。
多头(买入)头寸示例
一名交易者预计微软的股价将在未来几个月内因技术法规的改善而上涨,从而增加收入。因此,他们以 250 美元的价格购买了一些股票,并计划以 500 美元的价格出售。因此,交易者是多头微软股票(也称为看涨)。
空头(卖出)头寸示例
一名交易者预计洛克希德·马丁的股价将在未来几天内下跌,因为技术策略发出了信号。因此,他们以 450 美元的价格卖空了一些股票,并计划以 410 美元的价格买回。因此,交易者是空头洛克希德·马丁股票(称为看跌)。
注意
上涨趋势的市场被称为牛市。源自“牛”的词和其积极的本质,多头与乐观、狂喜和贪婪有关。另一方面,下跌趋势的市场被称为熊市。源自“熊”的词和其防御性的本质,空头与悲观、恐慌和恐惧有关。
金融工具可能以其原始(实物)形式或所谓的现货形式以及衍生品形式出现。衍生品是交易者用来以某种方式交易市场的产品。例如,远期或期货合同是一种衍生合同,买家锁定资产的价格以在以后的某个时间购买。
另一种衍生品是期权。期权是在未来以支付保费(期权价格)购买特定资产的权利但不是义务。当买家想购买基础股票时,他们行使他们的期权;否则,他们可以让期权到期。
交易活动也可能出于对冲目的而发生,因为它不仅仅限于投机。一个例子是法国主要航空公司 AirFrance 通过购买石油期货对其业务运营进行对冲。购买石油期货可以保护 AirFrance 免受可能损害其主要业务(航空)的石油价格上涨。使用燃料来驱动飞机的成本上升被期货收益抵消。这使航空公司可以专注于其主要业务。整个过程称为对冲。
让我们举个例子来让事情更清晰,比如一家航空公司预计在接下来的六个月内消耗一定数量的燃料,但他们担心这段时间内石油价格可能会上涨。为了防范这种价格风险,航空公司可以进入期货合同,在未来日期以固定价格购买石油。
如果在那段时间内石油价格上涨,航空公司仍然可以以较低的固定价格购买石油,这是在期货合同中约定的。另一方面,如果石油价格下跌,航空公司将被迫支付较高的固定价格,但较低的市场价格将抵消这一成本。
这样,航空公司可以减轻石油市场价格波动的风险,并稳定其燃油成本。这可以帮助航空公司更好地管理预算并预测未来的收入。
正如你所看到的,交易操作的目的是从中获得财务收益--它旨在通过锁定已知的石油价格来简单稳定其成本。
通常,根据其类型,金融工具被分为资产类别:
股票市场
股票市场是公司发行股票以筹集业务资金的交易场所(电子或实体)。当人们购买公司股票时,他们成为该公司的部分所有者,并根据公司政策有资格获得股息。根据股票类型的不同,他们还可以获得在董事会会议上投票的权利。
固定收益
政府和企业可以在固定收益市场借款。当一个人购买债券时,实际上是向借款人借钱,后者同意偿还贷款以及利息。根据借款人的信用状况和当前利率,债券的价值可能会增加或减少。
货币
外汇市场,也被称为货币市场,是人们可以购买和出售各种货币的地方。货币的价值可以根据多种变量增加或减少,包括经济、利率和国家的政治稳定性。
商品
农产品、黄金、石油和其他具有工业或其他用途的实物资产被称为商品。它们通常提供了一种从全球经济趋势中获利的方式,同时也是对抗通货膨胀的一种形式。
另类投资
在金融世界中,诸如房地产、私募股权和对冲基金等非传统投资被称为另类资产类别。这些另类资产类别有可能提供比传统资产更好的回报,并提供多样化的好处,但它们也往往不太流动,可能更难评估。重要的是要记住,每种资产类别都有独特的特点和不同程度的风险,因此投资者在投资这些资产之前应该做好功课。
金融市场允许企业和政府筹集他们运营所需的资金。它们还为投资者提供了赚取利润的机会,投机和投资于有趣的机会。交易活动为市场提供了流动性,使价格更有效率且成本更低。换句话说,市场越流动,交易成本就越低,因为订单数量使得在交易时对市场产生重大影响的可能性降低。但市场真正是如何运作的呢?是什么导致价格上涨和下跌?
市场微观结构是研究金融市场中证券交易的研究。它研究交易的运作方式以及交易员、投资者和市场制造商的行为。了解价格形成和影响交易成本的变量是市场微观结构研究的目的。
订单流向、流动性、市场有效性和价格发现只是市场微观结构研究涵盖的许多主题之一。此外,它还研究了各种交易技术,包括限价单、市价单和算法交易,如何影响市场动态。流动性可能是最重要的市场微观结构概念。它描述了资产可以在不实质性改变价格的情况下被买入或卖出的容易程度。流动性可能会因金融工具和时间而异。它可能会受到多种变量的影响,包括交易量和波动性。
此外,市场效率是市场微观结构研究的一个关键组成部分。该领域的研究表明,一些市场比其他市场更有效率,而内幕交易、价格操纵和信息不对称等因素可能会影响价格。市场效率在第四章中重新讨论,该章涉及技术分析,这是交易中的一个关键分析领域。
最后,我想讨论市场微观结构的另一个重要领域:价格发现。这指的是市场中用于设定价格的方法。价格可能会受到订单流向、做市商活动和各种交易方法的影响。
想象一下,你想要购买两只股票的大量股份:A 股和 B 股。A 股非常流动,而 B 股非常不流动。如果你想要在 A 股上执行买单,你很可能会以期望的市场价格得到填充,而且影响很小,如果有的话。然而,对于 B 股,你很可能会得到一个更糟糕的价格,因为没有足够的卖家愿意以你期望的买入价格出售,因此,随着你的订单增加需求,价格上涨以匹配卖家的价格,因此,你将以更高(更糟糕)的价格购买。这就是流动性对你交易的影响。
金融数据科学应用
让我们开始窥探金融数据科学的主要领域。每个领域都有其挑战和需要简单和复杂解决方案的问题。金融也不例外。近年来,数据科学在改善金融世界的应用上取得了巨大进步,从企业世界到市场世界。让我们讨论其中一些领域:
金融欺诈检测
可以使用数据科学模型来检查金融交易中的模式和异常,试图发现可能的欺诈行为。通过检查信用卡的交易数据来发现奇怪或可疑的支出模式,利用数据科学阻止金融欺诈的一种方法是发现异常或可疑的支出模式。这可能包括在短时间内进行多次小额购买,或者从同一商店频繁进行重大或频繁购买。基于这些数据,机器学习算法被训练来发现指向欺诈行为的异常。
风险管理
为了检查金融数据并发现投资组合的潜在风险,可以应用数据科学方法。这可以帮助金融机构管理其风险敞口并做出更明智的决策。在金融风险管理中应用数据科学的一个例子是分析市场数据以预测股票价格和其他金融指标的变化。这可能涉及使用统计建模、机器学习和人工智能等方法分析大量历史数据,以发现可用于预测未来市场状况的模式和趋势。
信用评分
数据科学可用于检查金融数据和信用历史,预测个人或公司的信用价值,并做出贷款决策。利用财务数据,如收入和信用历史,来预测个人的信用价值是应用数据科学进行信用评分研究的一个例子。这可能涉及开发一个预测模型,该模型可以使用多个指标,如先前的信用表现、收入和工作历史,来评估一个人偿还贷款的可能性,使用统计建模和机器学习等技术。
自然语言处理(NLP)
为了做出更好的判断,自然语言处理(NLP)分析并从非结构化的金融数据中提取见解,例如新闻文章、报告和社交媒体帖子。NLP 的一个著名例子是利用文本的情感来提取可能源自市场参与者和专家意图和情感的交易机会。NLP 属于情感分析领域(借助机器学习)。
总结
数据科学每天都在不断发展,新的技术和模型不断出现,旨在改进对数据的解释。本章简要介绍了您需要了解的关于数据科学以及如何在金融领域中使用它的内容。
下一章介绍了在尝试理解数据科学模型时可能需要的统计学、概率论和数学知识。尽管本书的目的是通过 Python 呈现创建和应用不同模型的实践方法,但了解您正在处理的内容而不是盲目地将其应用于数据是有帮助的。
如果您需要 Python 的复习,请参阅第三章,这是一个基本介绍。它为书中接下来要讲的内容奠定了基础。您不需要成为 Python 大师来进行数据科学,但您必须理解代码及其引用的内容,尤其是如何调试和检测代码中的错误。
¹ OHLC 指的是市场数据的四个基本部分:开盘价、最高价、最低价和收盘价。
² 我之所以说是暗示的是因为必须通过统计检查来验证平稳性,您将在下一章中看到。目前,假设是对数据进行差分运算得到平稳时间序列。
³ Rish 算法是一种用于找到反导数的不定积分技术。
第二章:深度学习的基本概率方法
技术的兴起和可访问性使每个人都能够部署机器学习和深度学习算法进行数据分析和优化。但不幸的是,这意味着许多用户不了解不同学习模型的基础和基础知识。这使得机器学习对他们来说简直就是一个黑匣子,这是一场灾难的前兆。
理解和掌握概率、统计和数学的基本概念对于理解和掌握数据以及创建试图解释和预测数据的模型至关重要。本章介绍了理解不同学习算法所需的数值概念的基础知识,或者至少展示了你可以从哪里开始建立知识以掌握这些数学主题。
为简单起见,本书中使用的术语机器学习指的是所有类型的学习模型(如机器学习、深度学习和强化学习)。
概率入门
概率是描述随机变量和随机事件的一切。世界充满了随机性,找到穿越混乱的最佳方法是尝试使用概率方法来解释它。诚然,短语解释混乱可能是一个矛盾修饰语,因为混乱实际上无法解释,但我们人类无法放弃对不确定事件的控制,随着进步,我们已经开发出工具来理解这个可怕的世界。
你可能会想知道在尝试为金融交易开发机器学习算法时理解概率基础的用途是什么。这是一个合理的问题,你必须知道,一个学科的基础不一定与它相似。
例如,要成为一名飞行员,你必须首先学习空气动力学,其中充满了与毕业后获得的最终技能不相似的技术概念。这与本章中所做的类似;通过学习概率基础知识、统计概念和数学主题,你将开始走上成为机器学习开发人员的正确道路。
了解你正在学习的东西的用途应该会给你动力。以下是一些对机器学习重要的关键概率主题:
概率分布函数
概率分布描述了随机变量的各种结果的可能性。对于许多机器学习技术来说,理解典型概率分布的特征和属性是至关重要的。概率分布函数还描述了不同类型的时间序列数据,从而有助于选择正确的算法。
贝叶斯定理用于更新概率
贝叶斯定理是概率论的基石,提供了一种在新数据的光下更新事件概率的方法。它被纳入到各种机器学习技术中,包括贝叶斯网络和分类器。
假设检验
假设检验用于确定基于数据样本的人口断言更可能是真实还是错误的。许多机器学习模型在其过程中使用假设检验。
决策树
决策树是一种借鉴了条件概率等概率概念的机器学习算法,本章涵盖了这个概念。更详细的内容,决策树在第七章中有详细介绍。
信息论
信息论是关于信息如何被量化、存储和传输的复杂研究。它被纳入到许多机器学习技术中,包括决策树。
概率概念简介
概率信息中最基本的部分是随机变量,它是一个不确定的数字或结果。随机变量用于模拟被认为是不确定的事件,例如货币对未来回报。
随机变量要么是离散的,要么是连续的。离散随机变量具有有限的值集,而连续随机变量具有在某个区间内的值。考虑以下两个例子以澄清事情:
-
离散随机变量的一个例子是掷骰子的结果。它们受以下集合的限制{1, 2, 3, 4, 5, 6}。
-
连续随机变量的一个例子是 EURUSD 的每日价格回报(1 欧元兑换美元的汇率)。
随机变量由概率分布描述,这是给出这些随机变量的每个可能值的概率的函数。通常,直方图用于显示概率。直方图绘制将在本章后面讨论。
在任何时刻,某个事件发生的概率在 0 和 1 之间。这意味着概率被分配给随机变量,范围在 0 到 1 之间,其中概率为 0 表示发生的机会为零,概率为 1 表示发生的确定性。
您也可以将其以百分比表示,范围从 0%到 100%。这两个数字之间的值是有效的,这意味着某个事件发生的概率可以是 0.5133(51.33%)。考虑掷一个有六个面的骰子。在不以任何方式操纵骰子的情况下,得到 3 的概率是多少?
由于骰子有六个面,每个结果有六个相等的概率,这意味着对于任何结果,概率如下找到:
用P(x)表示事件x的概率。这给出了问题的答案:
掷骰子时,只能有一个结果。它不能同时给出 3 和 4,因为一面必须占优势。这就是互斥性的概念。互斥事件(例如在掷骰子时得到 3 或得到 4)最终总和为 1。看看以下例子:
将所有这些互斥事件相加得到 1,这意味着六面骰子中可能概率的总和如下:
注
声明随机变量有 0.8 的发生概率等同于声明相同变量有 0.2 的不发生概率。
概率测量可以是条件的或无条件的。条件概率是指事件发生影响另一个事件发生的概率。例如,鉴于积极的就业数据,主权利率上涨的概率是条件概率的一个例子。给定事件 B 发生的情况下事件 A 发生的概率由以下数学符号表示:P(A|B)
相比之下,无条件概率不依赖于其他事件。以条件概率为例,您可以制定一个无条件概率计算,该计算测量利率上涨的概率,而不考虑其他经济事件。
概率具有特定的加法和乘法规则,具有自己的解释。在看例子之前,让我们先看一下公式。两个事件实现的联合概率是它们都发生的概率。它是使用以下公式计算的:
该公式表示的是 A 和 B 同时发生的概率是 A 在 B 发生的情况下发生的概率乘以 B 发生的概率。因此,等式的右侧将一个条件概率乘以一个无条件概率。
加法规则用于确定至少发生两个结果中的一个的概率。这有两种方式:第一种处理互斥事件,第二种处理非互斥事件:
如果事件不是互斥的,则为避免重复计数,公式如下:
如果事件是互斥的,则公式简化为以下形式:
注意,在互斥事件中,只能实现 A 或 B,因此两者都发生的概率为零。要理解为什么需要减去 A 和 B 的联合概率,请看图 2-1。

图 2-1。概率的加法规则
注意,当 A 或 B 发生的概率互斥时,必须不包括它们的联合概率。现在让我们来看看独立事件的概念。
独立事件不相互关联(例如,掷骰子两次)。联合概率计算如下:
因此,独立事件指的是一个事件的发生对其他事件的发生绝对没有影响。现在,让我们看一个例子来验证这些概念。考虑一个简单的抛硬币。得到正面的概率不取决于您在上一个抛硬币中得到的结果。因此,得到正面的概率始终为 0.50(50%)。更进一步,连续五次抛硬币后只得到正面的概率是多少?
由于每个事件的概率与前一个或下一个事件是独立的,因此公式如下:
随机变量的期望值是不同结果的加权平均值。因此,期望值实际上是指均值的另一种方式。在数学上,期望值如下:
看一下表 2-1,尝试计算一年中某个月的下一个就业人数的期望值。
表 2-1。就业表
| 非农就业人数 | 概率 |
|---|---|
| 300,000 | 0.1 |
| 400,000 | 0.3 |
| 500,000 | 0.5 |
| 600,000 | 0.1 |
非农就业人数是指美国劳工部发布的每月报告,提供有关全国有薪雇员总数的信息,不包括农业部门的从业人员以及政府和非营利组织的从业人员。
从表 2-1 中,经济学家假设有 50%的概率总有薪员工人数增加 50 万,有 30%的概率总有薪员工人数增加 40 万。因此期望值为:
因此,代表经济学家共识的数字是 460,000,因为它是大多数预测的最接近加权值。它是代表数据集的值。
在介绍概率的部分结束之前,存在一个称为贝叶斯定理的数学公式,根据先前相关事件的知识估计事件的可能性。贝叶斯定理的公式如下:
其中:
-
P(A|B)是在事件 B 发生的情况下事件 A 发生的概率。
-
P(B|A)是在事件 A 发生的情况下事件 B 发生的概率。
-
P(A)是事件 A 发生的概率。
-
P(B)是事件 B 发生的概率。
换句话说,贝叶斯定理允许您根据新信息更新对事件概率的信念。
注意
本节的主要要点如下:
-
概率描述随机变量和随机事件。它是介于 0 和 1 之间的值。
-
事件的概率可以组合在一起形成更复杂的情景。
-
预期结果是指定宇宙中每个概率的加权平均值。
抽样和假设检验
当人口很大时,会采取代表性样本,使其成为数据的主要描述者。以美国为例。其民主制度意味着人民有权决定自己的命运,但不可能去问每个人关于每个话题的详细意见。这就是为什么要举行选举并选举代表,以便他们代表人民行事。
抽样指的是在更大的人口中选择数据样本并对人口的统计特性做出结论的行为。有几种不同的抽样方法。最常见的是以下几种:
简单随机抽样
在简单随机抽样中,人口中的每个元素被选入样本的机会是相等的。这可以是在一个标记的人口中生成的随机数,其中每个个体被选中的概率相同。
分层抽样
使用分层抽样,人口根据某些特征分成组,然后从每个组中按比例随机抽取一个简单随机样本。
集群抽样
使用集群抽样,人口被分成集群,然后从中选择一个随机样本。然后,所选集群中的所有元素都包括在样本中。
系统抽样
使用系统抽样,通过从人口中每隔n个个体选择一个元素来选择一个元素,其中n是一个固定数字。这意味着它不是随机的,而是事先指定的。
一个经验法则是,你获得的数据越多,度量就越能反映人口。在机器学习领域,抽样非常重要,因为很多时候,你正在抽取数据样本来代表真实的人口。例如,在对策略进行回测时,你需要将整个数据集分成一个训练样本和一个测试样本,其中第一个是算法了解其结构的数据样本,第二个是算法测试其预测能力的数据样本。
类似地,另一个使用抽样的例子是交叉验证,这是一种将数据集分成两个或更多子组的技术。模型使用一个子集进行训练,然后使用其他子集进行测试。对于数据的各种子集,这个过程会重复多次,然后确定模型的平均性能。
这些术语将在接下来的章节中更深入地讨论。现在你应该明白,在机器学习中(甚至在使用优化技术的深度学习中),抽样的概念非常重要。
抽样并不完美,可能会出现错误,就像任何其他估计方法一样。抽样误差指的是样本的统计量与人口的统计量之间的差异(如果已知)。统计量是描述分析数据集的度量(一个例子是均值,在第三章中关于统计学的更详细内容中会看到)。那么,你应该有多少最小样本量才能对人口进行推断呢?经验法则是至少有 30 个观察值,越多越好。这将引出中心极限定理,该定理指出从人口中抽取的随机样本将在样本变大时逼近正态分布(一种对称的钟形概率分布)。
中心极限定理使得推断和结论的应用变得简单,因为假设检验与正态分布相配。在进行假设检验之前,让我们看看置信区间,即预期总体参数的值范围。置信区间通常通过从点估计值中加减一个因子来构建。例如,给定一个样本均值 x̄,可以构建一个置信区间如下:
让我们逐步理解计算过程。样本均值是对总体的估计,并且是因为不可能计算总体均值而计算的,因此,通过进行随机抽样,假设样本均值应该等于总体均值。然而,在现实生活中,事情可能会有所不同,这就是为什么应该使用概率方法构建置信区间的原因。
注意
显著性水平是置信区间的阈值。例如,95%的置信区间意味着有 95%的置信度,估计值应该落在某个范围内。剩下的 5%概率不在这个范围内,称为显著性水平(通常用α符号表示)。
可靠性因子是一个统计量,取决于估计值的分布以及它落入置信区间的概率。为了简单起见,让我们假设总体的方差是正态的,总体是正态分布的。对于 5%的显著性水平(因此,置信区间为 95%),在这种情况下可靠性因子为 1.96(获得这个数字的方式与讨论的内容不太相关)。
标准误差是样本的标准差。标准差在第三章中有更深入的讨论;现在只需知道它代表了围绕均值的不同值的波动。标准误差使用以下公式计算:
值得知道的是,对于 1%的显著性水平,可靠性因子为 2.575,对于 10%的显著性水平,可靠性因子为 1.645。让我们通过一个实际例子来理解所有这些数学。
考虑一个包含 100 种金融工具(债券、货币对、股票、结构化产品等)的总体。这些工具的年平均回报率为 1.4%。假设总体标准差为 4.34%,在 1%显著性水平(99%置信区间)下,均值的置信区间是多少?
答案就是将数值代入以下公式:
这意味着置信区间在(0.29%,2.51%)之间。
让我们看另一个例子。假设贵金属和工业金属(如黄金和铜)的年回报率是正态分布的,均值为 3.5%,已知总体标准差为 5.1%。在 10%显著性水平下,5 种不同商品的年回报率的置信区间是多少?答案如下:
这意味着置信区间在(1.27%,5.8%)之间。
注意
如果样本量较小和/或总体标准差未知,则 t-分布可能比正态分布更好。
t-分布是一种概率分布,用于模拟样本均值的分布,当样本量较小和/或总体标准差未知时。它在形状上类似于正态分布,但尾部更重,这代表了与较小样本量相关的不确定性。
在结束有关抽样和估计的讨论之前,以下列表显示了根据总体特征给出的适当分布:
-
具有已知方差的小型正态分布应使用正态分布的可靠性因子。
-
具有已知方差的大型正态分布应使用正态分布的可靠性因子。
-
具有未知方差的小正态分布应使用 t 分布的可靠性因子。
-
具有未知方差的大正态分布应使用 t 分布的可靠性因子。
-
具有已知方差的大非正态分布应使用正态分布的可靠性因子。
-
具有已知方差的大非正态分布应使用 t 分布的可靠性因子。
请记住,大意味着观察数量大于 30。前一个列表中未涵盖的组合是复杂的,超出了本讨论的范围。
下一步是假设检验,这是一种从数据样本中得出结论的关键概率技术。这部分非常重要,因为它几乎用于所有类型的统计分析和模型中。
在统计学中,假设检验是一种从少量数据样本中得出关于总体的结论的技术。它涉及制定两个竞争性假设,零假设和备择假设,关于总体参数,然后使用样本数据确定哪个更有可能是准确的。
例如,金融分析师正在从风险角度评估两个投资组合。他们制定了两个假设:
-
零假设表明两个投资组合的波动性没有显著差异。
-
备择假设表明两个投资组合的波动性存在显著差异。
然后使用统计分析来测试假设,以确定波动性差异是否具有统计学意义或纯粹是由于偶然因素。
根据零假设和备择假设的定义,使用样本数据计算一个检验统计量。为了评估结果的显著性,然后将检验统计量与从标准分布中抽取的临界值进行比较。如果检验统计量在关键区域内,则拒绝零假设并接受备择假设。如果检验统计量不在关键区域内,则不拒绝零假设,并得出支持备择假设的证据不足的结论。
这些都是花言巧语,说的其实是假设检验基本上是创建两种相反的情景,进行概率检查,然后决定哪种情景更有可能是真实的。假设检验可以采取两种形式:
-
单侧检验:一个例子是测试某些金融工具的回报是否大于零。
-
双侧检验:一个例子是测试某些金融工具的回报是否与零不同(意味着可以大于或小于零)。
注意
假设检验通常是双侧的。
零假设是您希望拒绝的假设,因此希望被拒绝并接受备择方案。双侧检验采用以下一般形式:
由于备择方案允许数值在零上下两侧(这是零假设中规定的水平),应该有两个临界值。因此,双侧检验的规则是,如果检验统计量大于上临界值或小于下临界值,则拒绝零假设。例如,对于正态分布的数据,检验统计量与临界值(在 5%显著性水平下)进行比较,分别为+1.96 和-1.96。如果检验统计量落在+1.96 和-1.96 之间的范围之外,则拒绝零假设。
假设检验的过程包括计算检验统计量。通过将总体参数的点估计与零假设的假设值进行比较来计算它。然后,两者都通过样本的标准误差进行缩放。数学表示如下:
假设检验中的一个重要考虑因素是样本可能不具代表性,这导致对总体的描述出现错误。这导致了两种类型的错误:
-
I 型错误:当拒绝虚无假设,即使它是真的时发生。
-
II 型错误:当未能拒绝虚无假设,即使它是错误的时发生。
直观地说,显著性水平是发生 I 型错误的概率。请记住,如果α = 5%,那么错误地拒绝真实的虚无假设的概率为 5%。举个例子会更清楚。
考虑一位分析师对一个长短投资组合 20 年期间的年度回报进行研究。平均年回报率为 1%,标准偏差为 2%。分析师认为年均回报率不等于零,并希望为此构建一个 95%的置信区间,然后进行假设检验:
-
列出变量。样本大小为 20,标准偏差为 2%,平均值为 1%。
-
根据公式计算标准误差,在这种情况下为 0.44%。
-
定义 95%置信区间的临界值,即+1.96 和-1.96。
-
因此置信区间为(0.13%,1.86%)。
-
指定虚无假设,根据分析师的意见,这是一个双尾检验。虚无假设是年回报等于零。如果测试统计量小于-1.96 或大于+1.96,则应拒绝它。
-
使用公式找到测试统计量为 2.27。因此,虚无假设被拒绝。
还有一个重要的指标要讨论:p 值。p 值是在零假设成立的情况下,看到一个比统计测试中看到的更极端的测试统计量的概率。将 p 值与显著性水平(通常为 0.05)进行比较,可以帮助您理解它。如果 p 值小于或等于显著性水平,则结果被认为具有统计学意义,零假设被拒绝,支持备择假设。
如果 p 值小于 5%的显著性水平,这意味着如果零假设成立,看到一个与当前测试统计量一样极端的测试统计量的概率为 5%。另一种定义 p 值的方式是将其视为可以拒绝零假设的最小显著性水平。
注意
本节的主要要点如下:
-
抽样是指在人口中收集数据,以便对上述人口的统计特性做出结论。
-
在机器学习中广泛使用抽样。一个例子是交叉验证。
-
假设检验是一种从少量数据中得出对总体的结论的技术。
信息论入门
信息论是一个复杂的抽象数学领域,与概率密切相关。它研究信息如何被量化、存储和传输。当涉及到事件发生时,有三种情况:
-
不确定性:如果事件尚未发生。
-
惊喜:如果事件刚刚发生。
-
信息:如果事件已经发生。
信息论中的一个关键概念是熵:消息或信息源中的不确定性或随机性水平。它描述了事件或消息的意外程度。相反,信息增益衡量了在接收新信息时熵(惊喜)的减少。
基本上,信息理论描述了事件的惊喜。当事件发生的概率很低时,它具有更多的惊喜,因此提供更多信息。同样,当事件发生的概率很高时,它具有更少的惊喜,因此提供更少的信息。您应该记住的是,从不太可能事件中学到的信息量大于从可能事件中学到的信息量。
在深入了解信息理论之前,了解什么是对数以及指数是很重要的。一般的指数函数将某个常数或变量提升到某个幂次方:
换句话说,一个数字的指数是您将其乘以自身的次数:
相比之下,对数是指数的相反,其目的是找到指数(从前面的例子中知道 4 和 64,找到 3):
因此,对数是一个数字乘以另一个数字得到另一个数字的次数的答案。由于它们是互为反函数,您可以将它们一起使用来简化甚至解决 x。看下面的例子:
这里的目标是在给定对数函数的情况下找到 x。第一步只是在一侧使用指数函数,因为希望它取消右侧的对数(记住,反函数互相抵消)。这给我们带来了以下结果:
对数可以有不同的底数。然而,最常用的对数的底数是 10。在计算机科学中,基数为 2 的对数表示比特(二进制位)。因此,信息被表示为比特。信息增益的公式如下:
假设有两个变量x和y,其中x的概率为 1(100%,因此确定),而y的概率为 0.5(50%,因此大部分是随机的),在这两种情况下信息是多少?答案如下:
因此,确定事件提供零信息,而有一半机会实现的事件具有信息 1。那么概率为 0.05(5%)的非常不可能事件z的信息是多少?
因此,概率和信息之间存在负相关关系是信息理论的原则之一。熵和信息是相关概念,但它们具有不同的含义和应用。
熵是用来评估系统有多混乱或随机的度量标准。熵描述了信号的不确定性或不可预测性。系统或通信中的无序或不可预测程度随着熵的增加而增加。
信息是接收信号后熵或不确定性减少的结果。信号减少接收者的不确定性或熵的能力随着其信息内容的增加而增加。
注意
每当所有事件等可能时,熵达到最大值。
熵使用以下公式计算:
因此,它是对数乘以各自概率的和的平均值。
现在,让我们讨论本节的最后一个概念,信息增益。通过改变数据集引起的熵的减少是通过信息增益来计算的。
信息增益是第七章决策树中的关键概念之一,因此在了解决策树是什么之后,您可能希望参考本节。
主要通过比较数据集在转换前后的熵来计算信息增益。请记住,当随机事件的所有结果具有相同的概率时,熵达到最大值。这也可以表示为一个分布,其中对称分布(如正态分布)具有高熵,而偏斜分布具有低熵。
注意
最小化熵与最大化信息增益有关。
在结束这个信息论的介绍部分之前(当讨论决策树时,你将更深入地了解它),让我们看看互信息的概念。这个度量是在两个变量之间计算的,因此有互的名称,它衡量了在给定另一个变量的情况下变量不确定性的减少。互信息的公式如下:
互信息因此衡量了变量之间的依赖关系。互信息越大,变量之间的关系就越大(零值表示独立变量)。记住这个概念,因为你将在第三章中的处理相关性的部分中看到它。这是因为互信息也可以衡量变量之间的非线性相关性。
注意
让我们总结一下你需要在信息论中保留的内容,以便对即将到来的内容有基本的了解:
-
信息论使用概率的概念来计算在机器学习模型和其他计算中使用的信息和熵(如相关性)。
-
信息是接收信号导致的熵或不确定性的减少。熵是用来评估系统有多混乱或随机的度量标准。
-
互信息是两个随机变量之间依赖关系的度量。它也可以用来计算两者之间的相关性。
-
信息论中的工具被用于一些机器学习模型,如决策树。
总结
概率在继续更高级主题之前提供了一个基本框架。本章概述了在处理机器学习和深度学习模型时可能遇到的概念。了解如何计算概率以及如何执行假设检验是很重要的(尽管实际上算法会为您执行这些操作)。
下一章非常重要,介绍了你需要的统计知识,不仅适用于机器学习,还适用于金融交易和复杂数据分析。
第三章:描述性统计和数据分析
描述性统计是描述数据并尽可能多地从中提取信息的领域。基本上,描述性统计可以像数据的代表一样,因为它概括了数据的倾向、行为和趋势。
交易和分析从这一领域的指标中借鉴了很多。在本章中,您将看到需要掌握数据分析的主要概念。我发现最好的教育工具是实际示例,因此我将使用一个经济数据集的示例来呈现本章。
让我们以来自美国的通胀数据为例。消费者价格指数(CPI)衡量城市消费者每月支付的一系列产品和服务的价格(这意味着每个月都会向公众发布一个新的观察值,从而形成一个连续的时间序列)。任何两个时间段之间的通胀率是通过价格指数的百分比变化来衡量的。例如,如果去年面包的价格是 1.00 美元,今天的价格是 1.01 美元,那么通胀率为 1.00%。
您可以使用的获取 CPI 数据的代码类似于您在第一章中用来获取 VIX 数据的代码。
# Importing the required library
import pandas_datareader as pdr
# Setting the beginning and end of the historical data
start_date = '1950-01-01'
end_date = '2023-01-23'
# Creating a dataframe and downloading the CPI data using its code name and its source
cpi = pdr.DataReader('CPIAUCSL', 'fred', start_date, end_date)
# Printing the latest five observations of the dataframe
print(cpi.tail())
# Importing the required library
import pandas as pd
# Checking if there are NaN values in the CPI dataframe previously defined
count_nan = cpi['CPIAUCSL'].isnull().sum()
# Printing the result
print('Number of NaN values in the CPI dataframe: ' + str(count_nan))
# Dropping the NaN values from the rows
cpi = cpi.dropna()
# Transforming the CPI into a year-on-year measure
cpi = cpi.pct_change(periods = 12, axis = 0) * 100
cpi = cpi.dropna()
到目前为止,您应该有一个包含 CPI 年度变化的数据框架。年度变化是 CPI 上最常见的转换,因为它清晰简单地衡量了总体价格水平的变化,足够长的时间来考虑短期波动和季节性影响(回想一下面包的例子)。
因此,CPI 的年度变化作为通胀总体趋势的一个标尺。它也简单易懂,可以与其他国家和历史时期进行比较,因此在政策制定者和经济学家中很受欢迎(尽管在不同国家之间的篮子中元素权重存在缺陷)。让我们从统计角度分析数据集。
中心趋势度量
中心趋势指的是将数据集总结为可以代表它们的值的计算。第一个和最为人熟知的中心趋势度量是平均值(平均数)。平均值就是值的总和除以它们的数量。它是数据集的中心点,很可能是最能代表它的值。平均值的数学公式如下:
让我们以两个数据集的简单示例为例。假设您想要计算数据集 A 和数据集 B 的平均值。您会如何做?
-
数据集 A = [1, 2, 3, 4, 5]
-
数据集 B = [1, 1, 1, 1]
数据集 A 包含 5 个值(数量),总和为 15。这意味着平均值等于 3。数据集 B 包含 4 个值,总和为 4。这意味着平均值等于 1。
注意
当数据集中的所有值相同时,平均值与这些值相同。
图 3-1 显示了美国 CPI 过去二十年的年度值。较高的虚线是过去二十年计算的月度平均值。较低的虚线代表零,低于此线的是通货紧缩期。

图 3-1。美国 CPI 过去二十年的年度变化
您可以使用以下代码创建图 3-1:
# Calculating the mean of the CPI over the last 10 years
cpi_last_ten_years = cpi.iloc[-240:]
mean = cpi_last_ten_years["CPIAUCSL"].mean()
# Printing the result
print('The mean of the dataset: ' + str(mean), '%')
# Importing the required library
import matplotlib.pyplot as plt
# Plotting the latest observations in black with a label
plt.plot(cpi_last_ten_years[:], color = 'black', linewidth = 1.5, label = 'Change in CPI Year-on-Year')
# Plotting horizontal lines that represent the mean and the zero threshold
plt.axhline(y = mean, color = 'red', linestyle = 'dashed', label = '10-Year Mean')
plt.axhline(y = 0, color = 'blue', linestyle = 'dashed', linewidth = 1)
# Calling a grid to facilitate the visual component
plt.grid()
# Calling the legend function so it appears with the chart
plt.legend()
平均值的输出应该如下。
The mean of the dataset: 2.4794 %
这意味着 CPI 每年变化的平均观察值约为 2.50%。尽管美联储没有明确的通胀目标,但普遍认为存在一致意见,即维持通胀年度变化在 2.00%左右,因此与历史观察值相差不远。由于 2021 年以来政治和经济动荡导致的最近高通胀数字,有必要回归到平均水平以稳定当前局势。这个例子为过去 10 年所谓的正常水平(~2.50%)提供了一个数值价值。
显然,在 2023 年初高通胀率(约 6.00%)的情况下,情况与正常有些偏离,但是偏离多少?这个问题将在下一节中讨论变异性度量时得到回答。现在,让我们继续讨论中心趋势。
下一个度量是中位数,简单来说就是将数据集分成两个相等的部分的值。换句话说,如果您按升序排列数据集,中间的值就是中位数。当数据分布中有很多离群值或偏斜时(可能会使均值偏差较大并且不够代表性),中位数就会被使用。
通常与计算中位数相关的有两个主题,第一个与包含偶数个值的数据集有关(例如,24 行),第二个与包含奇数个值的数据集有关(例如,47 行):
计算偶数数据集的中位数
如果排列好的数据集中有偶数个值,中位数就是两个中间值的平均值。
计算奇数数据集的中位数
如果排列好的数据集中有一个奇数个值,中位数就是中间的值。
让我们以两个数据集的简单示例为例。假设您想要在数据集 A 和数据集 B 上计算中位数。您会如何做呢?
-
数据集 A = [1, 2, 3, 4, 5]
-
数据集 B = [1, 2, 3, 4]
数据集 A 包含五个值,是奇数个。这意味着中间的值就是中位数。在这种情况下,中位数是 3(注意它也是数据集的均值)。数据集 B 包含四个值,是偶数个。这意味着两个中间值的平均值就是中位数。在这种情况下,中位数是 2.5,即 2 和 3 的平均值。
图 3-2 显示了过去二十年美国 CPI 同比值。较高的虚线是过去二十年计算出的月中位数。较低的虚线代表零。基本上,这就像图 3-1,但是用中位数而不是均值绘制。

图 3-2. 过去二十年美国 CPI 同比变化与中位数
您可以使用以下代码创建图 3-2:
# Calculating the median of the dataset
median = cpi_last_ten_years["CPIAUCSL"].median()
# Printing the result
print('The median of the dataset: ' + str(median), '%')
# Plotting the latest observations in black with a label
plt.plot(cpi_last_ten_years[:], color = 'black', linewidth = 1.5, label = 'Change in CPI Year-on-Year')
plt.axhline(y = median, color = 'red', linestyle = 'dashed', label = '10-Year Median')
plt.axhline(y = 0, color = 'blue', linestyle = 'dashed', linewidth = 1)
# Calling a grid to facilitate the visual component
plt.grid()
# Calling the legend function so it appears with the chart
plt.legend()
中位数的输出应该如下:
The median of the dataset: 2.1143 %
显然,中位数受最近来自异常环境的离群值的影响较小。中位数约为 2.10%,更符合隐含目标 2.00%。
注意
请记住,第六章将为您提供有关本章中看到的 Python 代码片段的所有必要信息,因此如果您对编码概念感到困惑,也不必担心。
本节中最后一个中心趋势度量是众数。众数是最常见的值(但在数据分析中使用最少)。
让我们以两个数据集的简单示例为例。假设您想要在以下数据集上计算众数。您会如何做呢?
-
数据集 A = [1, 2, 2, 4, 5]
-
数据集 B = [1, 2, 3, 4]
-
数据集 C = [1, 1, 2, 2, 3]
数据集 A 包含两次值为 2,这使其成为众数。数据集 B 没有众数,因为每个值只出现一次。数据集 C 是多峰的,因为它包含多个众数(即 1 和 2)。
注意
众数在分类变量(如信用评级)中很有用,而不是连续变量(如价格和收益时间序列)
在分析时间序列时,您不太可能使用众数,因为均值和中位数更有用。以下是一些在金融分析中使用均值和中位数的示例:
-
计算价格数据的移动均值(平均值)以检测潜在趋势。
-
计算价格衍生指标的滚动中位数以了解其中性区域。
-
使用历史均值计算证券的预期收益。
-
通过比较均值和中位数来检查收益分布的正态性。
对于中心趋势指标的讨论非常重要,特别是均值和中位数不仅作为独立指标被广泛使用,而且作为更复杂的测量方法的组成部分。
注意
本节的主要要点如下:
-
主要有三种中心趋势测量:均值、中位数和众数。
-
均值是总和除以数量,而中位数是将数据一分为二的值。众数是数据集中最频繁出现的值。
变异性测量
变异性测量描述了数据集中值相对于中心趋势测量的分散程度。最早也是最为人熟知的变异性测量是方差。方差描述了一组数字相对于它们的均值的变异性。方差公式背后的思想是确定每个数据点离均值有多远,然后平方这些偏差以确保所有数字都是正数(这是因为距离不能为负)。
找到方差的公式如下:
这个公式背后的直觉是计算每个数据点与均值之间的平方偏差的总和,从而给出不同距离的观察结果,然后计算这些距离观察结果的均值。
让我们以两个数据集的简单示例为例。假设您想要计算数据集 A 和数据集 B 的方差。您会如何做?
-
数据集 A = [1, 2, 3, 4, 5]
-
数据集 B = [5, 5, 5, 5]
第一步是计算数据集的均值,因为这是您将计算数据离散度的基准。数据集 A 的均值为 3。下一步是逐步使用方差公式如下:
前面的结果总结如下:
最后,结果被观察数量除以以找到方差:
至于数据集 B,您应该直观地考虑一下。如果观察结果都相等,它们都代表数据集,这也意味着它们是自己的均值。在这种情况下,您对数据的方差会有什么看法,考虑到所有值都等于均值?
如果您的回答是方差为零,那么您是正确的。从数学上讲,您可以按照以下方式计算:
前面的结果总结为零,如果您将零除以 4(数据集的数量),您将得到零。直觉上,没有方差,因为所有值都是恒定的,它们不偏离其均值。
您可以使用以下代码在 Python 中计算方差:
# Calculating the variance of the dataset
variance = cpi_last_ten_years["CPIAUCSL"].var()
# Printing the result
print('The variance of the dataset: ' + str(variance), '%')
方差的输出应该如下:
The variance of the dataset: 3.6248 %
然而,存在一个缺陷,即方差代表平方值,与均值不可比较,因为它们使用不同的单位。通过对方差取平方根来轻松解决这个问题。这样做带来了下一个变异性测量,标准差。它是方差的平方根,是值与均值的平均偏差。
低标准差表示值倾向于接近均值(低波动性),而高标准差表示值相对于均值分布在更广范围内(高波动性)。
注意
标准差和波动性这两个词是可以互换使用的。它们指的是同一件事。
找到标准差的公式如下:
如果您考虑了前面关于方差的例子,那么标准差可以如下找到:
标准差通常与均值一起使用,因为它们使用相同的单位。当我讨论正态分布函数时,您很快就会理解这个统计量的重要性,这是描述性统计中的一个关键概念。
您可以使用以下代码在 Python 中计算标准差:
# Calculating the standard deviation of the dataset
standard_deviation = cpi_last_ten_years["CPIAUCSL"].std()
# Printing the result
print('The standard deviation of the dataset: ' + str(standard_deviation), '%')
标准差的输出应该如下:
The standard deviation of the dataset: 1.9039 %
你应该如何解释标准偏差?平均而言,CPI 同比值往往与同一时期的平均值相差±1.90%,该平均值为 2.48%。
在接下来的部分中,您将看到如何更好地利用标准偏差数字。本节中的最后一个变异性度量是范围。范围是一个非常简单的统计量,显示数据集中最大值和最小值之间的距离。这让你快速了解两个历史极端值。范围在您将在后面章节中看到的归一化公式中使用。查找范围的公式如下:
让我们以相同的例子计算范围。在 Python 中,您可以轻松地做到这一点,因为有内置函数可以显示给定数据集的最大值和最小值:
# Calculating the range of the dataset
range_metric = max(cpi["CPIAUCSL"]) - min(cpi["CPIAUCSL"])
# Printing the result
print('The range of the dataset: ' + str(range_metric), '%')
以下代码的输出应该如下所示:
The range of the dataset: 16.5510 %
图 3-3 显示了自 1950 年以来的 CPI 值。对角虚线代表范围。

图 3-3. 自 1950 年以来的美国 CPI 同比变化,带有代表范围的对角虚线
CPI 的范围显示了通货膨胀测量值在 30 年内从一个时期到另一个时期的变化大小。不同国家的通货膨胀数字的年度变化各不相同。一般来说,发达国家如法国和美国在稳定时期具有稳定的变化,而阿根廷和土耳其等新兴和前沿世界国家的通货膨胀数字更加波动和极端。
注意
在继续下一节时,请牢记以下要点:
-
您应该了解的三个关键变异性度量是方差、标准偏差和范围。
-
标准偏差是方差的平方根。这样做是为了使其可与均值进行比较。
-
范围是数据集中最高值和最低值之间的差异。它是对观察的整体波动性的快速快照。
形状度量
形状度量描述数据集中围绕中心趋势度量的值的分布。
均值和标准偏差是描述正态分布的两个因素。标准偏差描述了数据的传播或分散,而均值反映了分布的中心。
概率分布是描述随机实验中不同结果或事件发生可能性的数学函数。换句话说,它给出了随机变量所有可能值的概率。
有许多类型的概率分布,包括离散和连续分布。离散分布只取有限数量的值。最知名的离散分布是伯努利分布、二项分布和泊松分布。
连续分布用于可以在给定范围内取任意值的随机变量(如股票价格)。最知名的连续分布是正态分布。
正态分布(也称为高斯分布)是一种连续概率分布,围绕均值对称,并具有钟形。它是统计分析中最广泛使用的分布之一,通常用于描述自然现象,如年龄、体重和考试成绩。图 3-4 显示了正态分布的形状。

图 3-4. 均值=0,标准偏差=1 的正态分布图
您可以使用以下代码块生成图 3-4:
# Importing libraries
import matplotlib.pyplot as plt
import numpy as np
import scipy.stats as stats
# Generate data for the plot
data = np.linspace(-3, 3, num = 1000)
# Define the mean and standard deviation of the normal distribution
mean = 0
std = 1
# Generate the function of the normal distribution
pdf = stats.norm.pdf(data, mean, std)
# Plot the normal distribution plot
plt.plot(data, pdf, '-', color = 'black', lw = 2)
plt.axvline(mean, color = 'black', linestyle = '--')
# Calling a grid to facilitate the visual component
plt.grid()
# Show the plot
plt.show()
注意
由于正态分布变量很常见,大多数统计测试和模型假设分析的数据是正态的。对于金融回报,即使它们经历了一种偏斜和峰度形式,也被假定为正态的,这是本节讨论的形状的两个度量。
在正态分布中,数据围绕平均值对称分布,这也意味着平均值等于中位数和众数。此外,约 68%的数据落在平均值的一个标准差范围内,约 95%落在两个标准差范围内,约 99.7%落在三个标准差范围内。这个属性使正态分布成为进行推断的有用工具。
总结一下,从正态分布中你应该记住以下内容:
-
平均值和标准差描述了分布。
-
平均值将分布一分为二,使其等于中位数。由于对称性属性,众数也等于平均值和中位数。
现在,让我们讨论形状的测量。形状的第一个测量是偏斜度。偏斜度描述了分布的不对称性。它分析了分布偏离对称的程度。
正如您可能已经了解的那样,正态分布的偏斜度等于零。这意味着分布在其平均值周围完全对称,平均值两侧的数据点数量相等。
正偏斜表示分布向右有一个长尾,这意味着平均值大于中位数,因为平均值对异常值敏感,这将使其向上推(因此,在 x 轴的右侧)。同样,代表最频繁观察的众数将是三个中心趋势测量值中的最小值。图 3-5 显示了一个正偏斜。

图 3-5。一个正偏斜分布的示例
负偏斜表示分布向左有一个长尾,这意味着平均值低于中位数,原因在于正偏斜时提到的原因。同样,众数将是三个中心趋势测量值中的最大值。图 3-6 显示了一个负偏斜。

图 3-6。一个负偏斜分布的示例
注意
在金融市场中如何解释偏斜?如果分布是正偏斜的,这意味着高于平均值的回报比低于平均值的回报更多(分布的尾部在正侧更长)。
另一方面,如果分布是负偏斜的,这意味着低于平均值的回报比高于平均值的回报更多(分布的尾部在负侧更长)。
回报系列的偏斜度可以提供有关投资的风险和回报的信息。例如,正偏斜的回报系列可能表明投资具有更高的高回报潜力,但也具有更大的大额损失风险。相反,负偏斜的回报系列可能表明投资具有较低的高回报潜力,但也具有较低的大额损失风险。
找到偏斜度的公式如下:
该公式将第三中心矩除以标准差的三次方。让我们检查美国 CPI 年度数据的偏斜度:
# Calculating the skew of the dataset
skew = cpi["CPIAUCSL"].skew()
# Printing the result
print('The skew of the dataset: ' + str(skew))
以下代码的输出应该如下所示:
The skew of the dataset: 1.4639
数据的偏斜度为 1.46,但这意味着什么?让我们绘制数据的分布,以便解释变得更容易。您可以使用以下代码片段来做到这一点:
# Plotting the histogram of the data
fig, ax = plt.subplots()
ax.hist(cpi['CPIAUCSL'], bins = 30, edgecolor = 'black', color = 'white')
# Add vertical lines for better interpretation
ax.axvline(mean, color='black', linestyle='--', label='Mean', linewidth = 2)
ax.axvline(median, color='grey', linestyle='-.', label='Median', linewidth = 2)
# Calling the grid function for better interpretability
plt.grid()
# Calling the legend function to show the labels
plt.legend()
# Showing the plot
plt.show()
图 3-7 显示了前一个代码片段的结果。数据明显是正偏斜的,因为平均值大于中位数,偏斜度为正(大于零)。

图 3-7。美国 CPI 年度数据的数据分布
记住,偏斜度是概率分布不对称性的度量。因此,它衡量分布偏离正态的程度。解释偏斜度的经验法则如下:
-
如果偏斜度在-0.5 和 0.5 之间,则数据被认为是对称的。
-
如果偏度在-1.0 和-0.5 之间或在 0.5 和 1.0 之间,数据被认为是轻微偏斜的。
-
如果偏度小于-1.0 或大于 1.0,则数据被认为是高度偏斜的
什么是正偏态?1.17 是高度偏斜的数据(在正面),这与一项偏向通货膨胀的货币政策相一致,随着经济增长(伴随着一些引起偏斜的通货膨胀性波动)。
注意
有趣的是,对于偏斜分布,中位数是首选的度量标准,因为均值往往会被离群值拉动,从而扭曲其价值。
下一个形状度量是峰度,它是相对于正态分布的分布的尖峰或扁平程度的度量。峰度描述了分布的尾部,特别是尾部是否比正态分布的尾部更厚或更薄。数学上,峰度是第四中心矩除以标准差的第四次幂。
正态分布的峰度为 3,这意味着它是一个中峰分布。如果一个分布的峰度大于 3,则被称为尖峰分布,意味着它比正态分布有更高的峰值和更厚的尾部。如果一个分布的峰度小于 3,则被称为扁峰分布,意味着它比正态分布有更平的峰值和更薄的尾部。
计算峰度的公式如下:
有时,峰度被测量为超额峰度,以使其起始值为零(对于正态分布)。这意味着将峰度测量值减去 3,以计算超额峰度。让我们计算美国 CPI 年度数据的超额峰度:
# Calculating the excess kurtosis of the dataset
excess_kurtosis = cpi["CPIAUCSL"].kurtosis()
# Printing the result
print('The excess kurtosis of the dataset: ' + str(excess_kurtosis))
以下代码的输出应该如下:
The excess kurtosis of the dataset: 2.2338
从pandas获得的超额峰度在正态分布的情况下应为零。在美国 CPI 年度数据的情况下,它为 2.23,这更符合尖峰(峰值更高,尾部更厚)分布。正值表示分布比正常更尖,负峰度表示形状比正常更扁。
注意
独立于统计学,了解你正在分析的术语是很有趣的。通货膨胀是经济主体(如家庭)购买力下降。购买力下降意味着经济主体随着时间的推移用同样的金额购买力购买力减少,也被称为一般价格上涨。经济意义上的通货膨胀有以下形式:
-
通货膨胀:受控通货膨胀与稳定的经济增长和扩张相关。这是一个增长经济体的理想属性。监管机构监测通货膨胀并试图稳定它,以防止社会和经济问题。
-
通货紧缩:每当通货膨胀处于负值领域时,就被称为通货紧缩。通货紧缩对经济非常危险,尽管对于看到价格下降的消费者来说可能很诱人,但通货紧缩是一种增长杀手,可能导致长期的经济过剩,导致失业和熊市股市。
-
滞涨:当通货膨胀要么很高要么上升,而经济增长放缓时发生。同时,失业率保持高位。这是最糟糕的情况之一。
-
通货紧缩:这是通货膨胀下降但仍处于正值领域。例如,如果今年的通货膨胀率为 2%,而去年的通货膨胀率为 3%,你可以说每年都存在通货紧缩情况。
-
超级通货膨胀:这是通货膨胀失控并经历百分比变化如百万年度变化的噩梦般情景(著名案例包括津巴布韦、南斯拉夫和希腊)。
最后,在描述性统计部门中看到的最后一个指标是分位数。分位数是形状和变异性的度量,因为它们提供有关值的分布(形状)的信息,并提供有关这些值的离散程度(变异性)的信息。最常用的分位数类型称为四分位数。
四分位数将数据集分成四个相等的部分。这是通过对数据进行排序然后执行拆分来完成的。以表 3-1 为例:
| 值 |
|---|
| 1 |
| 2 |
| 4 |
| 5 |
| 7 |
| 8 |
| 9 |
四分位数如下:
-
下四分位数(Q1)是第一四分之一,在这种情况下是 2。
-
中位数(Q2)也是中位数,在这种情况下是 5。
-
在这种情况下,上四分位数(Q3)为 8。
从数学上讲,您可以使用以下公式计算 Q1 和 Q3:
请记住,公式的结果给出的是值的排名,而不是值本身:
四分位距(IQR)是 Q3 和 Q1 之间的差异,并提供了数据集中间 50%值的传播度量。IQR 对异常值具有鲁棒性(因为它依赖于中间值),并提供了对大部分值传播的简要摘要。根据以下公式,表 3-1 中的数据的 IQR 为 6:
IQR 是一个有价值的指标,可以用作许多不同模型中的输入或风险度量。它还可以用于检测数据中的异常值,因为它不受其影响。此外,IQR 可以帮助评估所分析资产的当前波动性,进而可以与其他方法一起使用以创建更强大的模型。正如所理解的那样,IQR 在有用性和解释性方面优于范围度量,因为前者容易受到异常值的影响。
在计算四分位数时要小心,因为有许多方法使用不同的计算来处理相同的数据集。最重要的是在整个分析过程中始终使用一致的方法。在表 3-1 中用于计算四分位数的方法称为图基的铰链方法。默认情况下,当您想使用pandas计算四分位数时,默认方法是线性插值方法,这将给出不同的结果。
这些方法之间的主要区别在于有些方法可能更适合较小的数据集或具有不同分布特征的正常大小数据集。
注意
本节的关键要点如下:
-
正态分布是具有钟形曲线的连续概率分布。大多数数据聚集在平均值周围。正态分布曲线的均值、中位数和众数都相等。
-
偏度度量概率分布的不对称性。
-
峰度度量概率分布的尖峰度。过度峰度通常用于描述当前概率分布。
-
分位数将排列的数据集分成相等的部分。最著名的分位数是将数据分成四个相等部分的四分位数。
-
IQR 是第三四分位数和第一四分位数之间的差异。它不受异常值的影响,因此在数据分析中非常有帮助。
可视化数据
如果您还记得前一章,我提出了数据科学中的六个阶段过程。第四阶段涉及数据可视化。本节将向您展示几种以清晰的视觉方式呈现数据的方法,使您能够解释数据。
有许多常用于可视化数据的统计图类型,例如散点图和折线图。让我们讨论这些图并使用相同的通货膨胀数据创建它们。
第一种数据可视化方法是散点图,用于通过对应于变量交点的点来绘制两个变量之间的关系。让我们使用以下代码创建和可视化一个散点图:
# Importing the required library
import pandas_datareader as pdr
import pandas as pd
import matplotlib.pyplot as plt
# Setting the beginning and end of the historical data
start_date = '1950-01-01'
end_date = '2022-12-01'
# Creating a dataframe and downloading the CPI data using its code name and its source
cpi = pdr.DataReader('CPIAUCSL', 'fred', start_date, end_date)
# Transforming the CPI into a year-on-year measure
cpi = cpi.pct_change(periods = 12, axis = 0) * 100
# Dropping the NaN values
cpi = cpi.dropna()
# Resetting the index
cpi = cpi.reset_index()
# Creating the chart
fig, ax = plt.subplots()
ax.scatter(cpi['DATE'], cpi['CPIAUCSL'], color = 'black', s = 8, label = 'Change in CPI Year-on-Year')
# Calling the grid function for better interpretability
plt.grid()
# Calling the legend function to show the labels
plt.legend()
# Showing the plot
plt.show()
图 3-8 显示了时间散点图的结果。这意味着您将 CPI 数据作为第一个变量(y 轴),时间作为第二个变量(x 轴)。然而,散点图更常用于比较变量,因此去除时间变量可以提供更多见解。

图 3-8。美国 CPI 与时间轴的散点图
如果您拿英国 CPI 年同比变化与美国 CPI 年同比变化进行比较,您将得到图 3-9。请注意两者之间的正相关性,因为一个变量的较高值与另一个变量的较高值相关联。相关性是一个关键指标,您将在下一节中详细了解。绘制图 3-9 的代码如下:
# Setting the beginning and end of the historical data
start_date = '1995-01-01'
end_date = '2022-12-01'
# Creating a dataframe and downloading the CPI data using its code name and its source
cpi_us = pdr.DataReader('CPIAUCSL', 'fred', start_date, end_date)
cpi_uk = pdr.DataReader('GBRCPIALLMINMEI', 'fred', start_date, end_date)
# Dropping the NaN values from the rows
cpi_us = cpi_us.dropna()
cpi_uk = cpi_uk.dropna()
# Transforming the CPI into a year-on-year measure
cpi_us = cpi_us.pct_change(periods = 12, axis = 0) * 100
cpi_us = cpi_us.dropna()
cpi_uk = cpi_uk.pct_change(periods = 12, axis = 0) * 100
cpi_uk = cpi_uk.dropna()
# Creating the chart
fig, ax = plt.subplots()
ax.scatter(cpi_us['CPIAUCSL'], cpi_uk['GBRCPIALLMINMEI'], color = 'black', s = 8, label = 'Change in CPI Year-on-Year')
# Adding a few aesthetic elements to the chart
ax.set_xlabel('US CPI')
ax.set_ylabel('UK CPI')
ax.axvline(x = 0, color='black', linestyle = 'dashed', linewidth = 1) # vertical line
ax.axhline(y = 0, color='black', linestyle = 'dashed', linewidth = 1) # horizontal line
ax.set_ylim(-2,)
# Calling the grid function for better interpretability
plt.grid()
# Calling the legend function to show the labels
plt.legend()
# Showing the plot
plt.show()

图 3-9。英国 CPI 与美国 CPI 的散点图
散点图在可视化数据之间的相关性时很有用。它们也易于绘制和解释。通常,当点散布在这样一种方式时,即可以画出一个向上倾斜的对角线来代表它们,就假定相关性是正的,因为每当 x 轴上的变量增加时,y 轴上的变量也会增加。
另一方面,当可以画出向下倾斜的对角线来代表不同变量时,可能存在负相关性。负相关意味着每当 x 轴上的变量移动时,y 轴上的变量可能会以相反的方式移动。图 3-10 绘制了两个通货膨胀数据之间的最佳拟合线(通过代码生成)。请注意它是向上倾斜的:

图 3-10。英国 CPI 与美国 CPI 的散点图及最佳拟合线
现在让我们转向另一种图表方法。线图基本上是散点图,它们被连接在一起,大多数情况下绘制在时间轴(x 轴)上。您已经在之前的图表中看到过线图,比如图 3-1 和图 3-2,因为它是绘图的最基本形式。
线图的优点在于其简单性和易于实现。它们还显示了系列随时间的演变,有助于检测趋势和模式。在第五章中,您将看到一个更复杂的金融时间序列绘图版本,称为蜡烛图。图 3-11 显示了自 1950 年以来美国 CPI 的基本线图:

图 3-11。美国 CPI 与时间轴的线图
要创建图 3-11,您可以使用以下代码片段:
# Setting the beginning and end of the historical data
start_date = '1950-01-01'
end_date = '2022-12-01'
# Creating a dataframe and downloading the CPI data using its code name and its source
cpi = pdr.DataReader('CPIAUCSL', 'fred', start_date, end_date)
# Transforming the CPI into a year-on-year measure
cpi = cpi.pct_change(periods = 12, axis = 0) * 100
# Dropping the NaN values
cpi = cpi.dropna()
# Resetting the index
cpi = cpi.reset_index()
# Creating the chart
plt.plot(cpi['DATE'], cpi['CPIAUCSL'], color = 'black', label = 'Change in CPI Year-on-Year')
# Calling the grid function for better interpretability
plt.grid()
# Calling the legend function to show the labels
plt.legend()
# Showing the plot
plt.show()
接下来是条形图,用于显示变量(通常是分类变量)的分布。图 3-12 显示了自 2022 年初以来美国 CPI 的条形图:

图 3-12。美国 CPI 与时间轴的条形图
要创建图 3-12,您可以使用以下代码片段:
# Taking the values of the previous twelve months
cpi_one_year = cpi.iloc[-12:]
# Creating the chart
plt.bar(cpi_one_year['DATE'], cpi_one_year['CPIAUCSL'], color = 'black', label = 'Change in CPI Year-on-Year', width = 7)
# Calling the grid function for better interpretability
plt.grid()
# Calling the legend function to show the labels
plt.legend()
# Showing the plot
plt.show()
条形图易于实现,功能多样。然而,对于绘制美国 CPI 或股票价格等连续数据,它们可能存在局限性。当比例失调时,它们也可能具有误导性。由于它们会占用空间,因此不建议在大型数据集上使用条形图。基于后一原因,直方图更适合。
直方图是一种特定类型的条形图,用于通过使用条形来表示统计信息来显示连续数据的频率分布。它指示落入值类别或区间的观测数量。直方图的一个示例是图 3-13(以及上一节的图 3-7):

图 3-13。美国 CPI 的直方图
# Creating the chart
fig, ax = plt.subplots()
ax.hist(cpi['CPIAUCSL'], bins = 30, edgecolor = 'black', color = 'white', label = 'Change in CPI Year-on-Year',)
# Add vertical lines for better interpretation
ax.axvline(0, color='black')
# Calling the grid function for better interpretability
plt.grid()
# Calling the legend function to show the labels
plt.legend()
# Showing the plot
plt.show()
请注意,条形图是根据时间轴绘制的,而直方图没有时间范围,因为它是一组值,旨在显示整体分布点。从视觉上,您可以看到分布的正偏度。
注意
分类变量的一个示例是性别,而连续变量的一个示例是商品价格。
统计学中另一种经典绘图技术是著名的箱线图。它用于可视化连续变量的分布,同时包括中位数和四分位数,以及异常值。理解箱线图的方法如下:
-
箱子代表 IQR。箱子在第一四分位数和第三四分位数之间绘制。箱子的高度表示该范围内数据的传播。
-
箱子内部的线代表中位数。
-
须在箱子的顶部和底部延伸到仍在 1.5 倍 IQR 内的最高和最低数据点。这些数据点称为异常值,并在图中表示为单独的点。
图 3-14 显示了自 1950 年以来美国 CPI 的箱线图:

图 3-14。美国 CPI 的箱线图
您还可以绘制没有异常值的图(任何值距离箱子的任一端超过箱子长度的一倍半)。要创建图 3-14,您可以使用以下代码片段:
# Taking the values of the last twenty years
cpi_last_ten_years = cpi.iloc[-240:]
# Creating the chart
fig, ax = plt.subplots()
ax.boxplot(cpi_last_ten_years['CPIAUCSL'])
# Calling the grid function for better interpretability
plt.grid()
# Calling the legend function to show the labels
plt.legend()
# Showing the plot
plt.show()
要从图中去除异常值,您只需使用以下调整:
# Replace the corresponding code line with the following
ax.boxplot(cpi_last_ten_years['CPIAUCSL'], showfliers = False)
这将给您图 3-15:

图 3-15。美国 CPI 的箱线图,没有异常值
存在许多其他数据可视化技术,例如热图(通常与相关数据和温度映射一起使用)和饼图(通常用于预算和分割)。这完全取决于您需要理解的内容以及哪种更适合您的需求。例如,线图更适合只有一个特征的时间序列(例如,只有某种证券的收盘价可用)。直方图图更适合概率分布数据。
注意
让我们总结一下您需要记住的一切:
-
数据可视化取决于您想要进行的分析和解释的类型。某些图适合某些类型的数据。
-
在数值上确认之前,数据可视化有助于对数据进行初步解释。
-
处理金融时间序列时,您更有可能使用线图和蜡烛图。
相关性
相关性是用于计算两个变量之间线性关系程度的度量。它是一个介于-1.0 和 1.0 之间的数字,-1.0 表示变量之间有强烈的负相关关系,而 1.0 表示有强烈的正相关关系。
零值表示变量之间没有线性关联。然而,相关性并不意味着因果关系。如果两个变量朝着同一个方向移动,则它们被认为是相关的,但这并不意味着一个导致另一个移动,或者它们是由相同事件的结果移动的。
大多数人都同意一些资产具有自然的相关性。例如,由于它们都属于同一行业,并受到相同趋势和事件的影响,苹果和微软的股票是正相关的(这意味着它们的总体趋势是相同的)。图 3-16 显示了两只股票之间的图表。注意它们如何一起移动。

图 3-16。自 2021 年以来的苹果和微软股价
两只股票的顶部和底部几乎同时出现。同样,由于美国和英国具有相似的经济驱动因素和影响,它们也很可能有正相关的通货膨胀数字。
通过视觉解释和数学公式来检查相关性。在看一个例子之前,让我们深入了解计算相关性的根源,这样您就知道它来自哪里以及它的局限性是什么。
注意
简而言之,要计算相关性,您需要测量两个变量散点图中的点与一条直线的接近程度。它们看起来越像一条直线,它们就越正相关,因此有了术语“线性相关”。
有两种主要的计算相关性的方法,即使用 Spearman 方法或 Pearson 方法。
皮尔逊相关系数是从两个变量的标准差和协方差计算出来的线性关联度量。但是,什么是协方差?
协方差计算两个变量均值之间的差异的平均值。如果两个变量有一起移动的倾向,协方差是正的,如果两个变量通常朝相反方向移动,协方差是负的。它的范围在无穷大和负无穷大之间,接近零的值表示没有线性相关。
计算变量x和y之间的协方差的公式如下:
因此,协方差是变量之间的平均偏差乘以它们各自的均值的总和(衡量它们之间的关联程度)。取平均值以规范化此计算。皮尔逊相关系数的计算如下:
简化前面的相关性公式得到以下结果:
因此,皮尔逊相关系数简单地是两个变量之间的协方差除以它们标准差的乘积。让我们计算美国 CPI 年度变化和英国 CPI 年度变化之间的相关性。直觉告诉我们,由于经济上,英国和美国有关联,所以相关性大于零。以下代码块计算了这两个时间序列的皮尔逊相关系数:
# Importing the required libraries
import pandas_datareader as pdr
import pandas as pd
# Setting the beginning and end of the historical data
start_date = '1995-01-01'
end_date = '2022-12-01'
# Creating a dataframe and downloading the CPI data using its code name and its source
cpi_us = pdr.DataReader('CPIAUCSL', 'fred', start_date, end_date)
cpi_uk = pdr.DataReader('GBRCPIALLMINMEI', 'fred', start_date, end_date)
# Dropping the NaN values from the rows
cpi_us = cpi_us.dropna()
cpi_uk = cpi_uk.dropna()
# Transforming the US CPI into a year-on-year measure
cpi_us = cpi_us.pct_change(periods = 12, axis = 0) * 100
cpi_us = cpi_us.dropna()
# Transforming the UK CPI into a year-on-year measure
cpi_uk = cpi_uk.pct_change(periods = 12, axis = 0) * 100
cpi_uk = cpi_uk.dropna()
# Joining both CPI data into one dataframe
combined_cpi_data = pd.concat([cpi_us['CPIAUCSL'], cpi_uk['GBRCPIALLMINMEI']], axis = 1)
# Using pandas' correlation function to calculate the measure
combined_cpi_data.corr(method = 'pearson')
输出如下:
CPIAUCSL GBRCPIALLMINMEI
CPIAUCSL 1.000000 0.732164
GBRCPIALLMINMEI 0.732164 1.000000
两者之间的相关性高达 0.73。这符合预期。皮尔逊相关通常用于具有成比例变化且正态分布的变量。这可能是一个问题,因为金融数据不是正态分布的。因此,讨论 Spearman 相关性是有趣的。
Spearman 相关性是一种非参数秩相关,用于衡量变量之间关系的强度。适用于不遵循正态分布的变量。
注意
请记住,金融回报不是正态分布的,但有时为简单起见会这样处理。
与皮尔逊相关不同,Spearman 秩相关考虑的是值的顺序,而不是实际值。要计算 Spearman 相关性,请按照以下步骤进行:
-
对每个变量的值进行排名。这是通过将最小变量替换为 1,将数据集的长度替换为最大数字来完成的。
-
计算排名之间的差异。在数学上,排名之间的差异用数学公式中的字母d表示。然后,计算它们的平方差。
-
将从步骤 2 中计算的平方差相加。
-
使用以下公式计算 Spearman 相关性。
与皮尔逊相关类似,Spearman 相关性也在-1.00 到 1.00 之间,并具有相同的解释。
注意
强正相关通常大于 0.70,而强负相关通常小于-0.70。
以下代码块计算了这两个时间序列的 Spearman 秩相关系数:
# Importing the required libraries
import pandas_datareader as pdr
import pandas as pd
# Setting the beginning and end of the historical data
start_date = '1995-01-01'
end_date = '2022-12-01'
# Creating a dataframe and downloading the CPI data using its code name and its source
cpi_us = pdr.DataReader('CPIAUCSL', 'fred', start_date, end_date)
cpi_uk = pdr.DataReader('GBRCPIALLMINMEI', 'fred', start_date, end_date)
# Dropping the NaN values from the rows
cpi_us = cpi_us.dropna()
cpi_uk = cpi_uk.dropna()
# Transforming the US CPI into a year-on-year measure
cpi_us = cpi_us.pct_change(periods = 12, axis = 0) * 100
cpi_us = cpi_us.dropna()
# Transforming the UK CPI into a year-on-year measure
cpi_uk = cpi_uk.pct_change(periods = 12, axis = 0) * 100
cpi_uk = cpi_uk.dropna()
# Joining both CPI data into one dataframe
combined_cpi_data = pd.concat([cpi_us['CPIAUCSL'], cpi_uk['GBRCPIALLMINMEI']], axis = 1)
# Using pandas' correlation function to calculate the measure
combined_cpi_data.corr(method = 'spearman')
输出如下:
CPIAUCSL GBRCPIALLMINMEI
CPIAUCSL 1.000000 0.472526
GBRCPIALLMINMEI 0.472526 1.000000
在得到这种结果差异后,让我们回答一个非常重要的问题。为什么这两种度量结果如此不同?
首先要记住的是它们测量的内容。皮尔逊相关度量变量之间的线性关系(趋势),而 Spearman 秩相关度量单调趋势。单调一词指的是朝着同一方向移动,但不完全以相同的速率或幅度。此外,Spearman 相关性将数据转换为序数类型(通过排名),而不是使用实际值的皮尔逊相关性。
自相关(也称为串行相关)是一种统计方法,用于研究给定时间序列与滞后版本之间的关系。它通常用于通过数据中的模式(如季节性或趋势)来预测未来值。因此,自相关是值与先前值之间的关系。例如,将每天的微软股价与前一天进行比较,看看是否存在可辨识的相关性。从算法的角度来看,这可以在表 3-2 中表示:
表 3-1。滞后值表
| t | t-1 |
|---|---|
| $ 1.25 | $ 1.65 | |
| $ 1.77 | $ 1.25 | |
| $ 1.78 | $ 1.77 | |
| $ 1.25 | $ 1.78 | |
| $ 1.90 | $ 1.25 |
每行代表一个时间段。列t是当前价格,列t-1是放在代表当前时间的行上的先前价格。这在创建机器学习模型时是为了了解当前值与以往值之间在每个时间步骤(行)的关系。
正自相关经常出现在趋势资产中,并与持续性(趋势跟随)的概念相关联。另一方面,波动市场表现出负自相关,与反持续性(均值回归)的概念相关联。
注意
短期相关性的度量通常使用价格回报而不是实际价格来计算。然而,可以直接利用价格来识别长期趋势。
以下代码块计算了美国 CPI 年同比的自相关性:
# Importing the required libraries
import pandas_datareader as pdr
import pandas as pd
# Setting the beginning and end of the historical data
start_date = '1950-01-01'
end_date = '2022-12-01'
# Creating a dataframe and downloading the CPI data using its code name and its source
cpi = pdr.DataReader('CPIAUCSL', 'fred', start_date, end_date)
# Dropping the NaN values from the rows
cpi = cpi.dropna()
# Transforming the US CPI into a year-on-year measure
cpi = cpi.pct_change(periods = 12, axis = 0) * 100
cpi = cpi.dropna()
# Transforming the data frame to a series structure
cpi = cpi.iloc[:,0]
# Calculating autocorrelation with a lag of 1
print('Correlation with a lag of 1 = ', round(cpi.autocorr(lag = 1), 2))
# Calculating autocorrelation with a lag of 6
print('Correlation with a lag of 6 = ', round(cpi.autocorr(lag = 6), 2))
# Calculating autocorrelation with a lag of 12
print('Correlation with a lag of 12 = ', round(cpi.autocorr(lag = 12), 2))
滞后 12 意味着每个数据与十二个周期前的数据进行比较,然后计算一个计算度量。代码的输出如下:
Correlation with a lag of 1 = 0.99
Correlation with a lag of 6 = 0.89
Correlation with a lag of 12 = 0.73
现在,在继续下一节之前,让我们回到信息论并讨论一个能够捕捉非线性关系的有趣相关系数。其中一种方式是最大信息系数(MIC)。
最大信息系数(MIC)是两个变量之间关联的非参数测量,旨在处理大型和复杂数据。它通常被视为传统相关性测量的更稳健的替代方法,如皮尔逊相关性和斯皮尔曼秩相关性。由Reshef 等人引入,MIC 使用了信息论中的概念,这些概念在第二章中已经介绍过。
MIC 通过计算列联表中的单元格数量来衡量两个变量之间的关联强度,这些单元格对于变量之间的关系是最具信息量的。MIC 值的范围从 0 到 1,数值越高表示关联越强。它可以处理高维数据,并且可以识别变量之间的非线性关系。然而,它是非方向性的,这意味着接近 1 的值仅表明两个变量之间存在强相关性,但并不说明相关性是正向还是负向。
注意
换句话说,在将每个变量的范围划分为一组箱后,计算每个箱内两个变量之间的互信息。
然后,通过跨所有箱子中的最大互信息值来估计两个变量之间的关联强度。
让我们看一个展示 MIC 在检测非线性关系方面的强大性的实际示例。以下示例模拟了正弦和余弦时间序列。直观地看,从图 3-17 可以看出,两者之间存在滞后-领先关系。

图 3-17。显示一种非线性关系形式的两个波浪序列的强度
以下 Python 代码片段创建了两个时间序列并绘制了图 3-17:
# Importing the required libraries
import numpy as np
import matplotlib.pyplot as plt
# Setting the range of the data
data_range = np.arange(0, 30, 0.1)
# Creating the sine and the cosine waves
sine = np.sin(data_range)
cosine = np.cos(data_range)
# Plotting
plt.plot(sine, color = 'black', label = 'Sine Function')
plt.plot(cosine, color = 'grey', linestyle = 'dashed', label = 'Cosine Function')
plt.grid()
plt.legend()
现在,任务是计算三个相关性度量并分析它们的结果。可以使用以下代码完成这项工作:
# Importing the libraries
from scipy.stats import pearsonr
from scipy.stats import spearmanr
from minepy import MINE
# Calculating the linear correlation measures
print('Correlation | Pearson: ', round(pearsonr(sine, cosine)[0], 3))
print('Correlation | Spearman: ', round(spearmanr(sine, cosine)[0], 3))
# Calculating the MIC
mine = MINE(alpha = 0.6, c = 15)
mine.compute_score(sine,cosine)
MIC = mine.mic()
print('Correlation | MIC: ', round(MIC, 3))
请注意,由于代码创建了一个数组(而不是数据框),在计算测量之前必须导入所需的库。这将在下一章中明确说明。以下是代码的输出:
Correlation | Pearson: 0.035
Correlation | Spearman: 0.027
Correlation | MIC: 0.602
让我们解释一下结果:
-
Pearson 相关性:由于缺少非线性关联,这里没有任何类型的相关性。
-
Spearman 相关性:在这里也适用于极弱的相关性。
-
MIC:该测量返回了两者之间 0.60 的强关系,更接近现实。看起来 MIC 表明这两个波浪之间有强关系,尽管是非线性的。
注意
您可能需要更新 Microsoft Visual C++(至少版本 14.0 或更高版本)以避免在尝试运行minepy库时出现任何错误。
如果正确使用,MIC 在经济分析、金融分析甚至寻找交易信号中非常有用。在这些复杂领域中非线性关系很常见,能够检测到这些关系可能会带来可观的优势。
注意
相关性部分的主要要点如下:
-
相关性是用于计算两个变量之间线性关系程度的度量。它是一个介于-1.0 和 1.0 之间的数字,-1.0 表示变量之间强烈的负相关关系,1.0 表示变量之间强烈的正相关关系。
-
有两种主要类型的相关性测量,Spearman 和 Pearson。它们都有各自的优点和局限性。
-
自相关是变量与其自身滞后值的相关性。例如,如果 Nvidia 的股票回报的自相关是正的,则表示趋势配置。
-
当您使用正确的工具时,相关性测量也可以指非线性关系,例如 MIC。
平稳性概念
平稳性是统计分析和机器学习中的关键概念之一。平稳性是指时间序列的统计特征(均值、方差等)随时间保持恒定。换句话说,当沿时间绘制数据时,没有可辨认的趋势。
不同的学习模型依赖于数据的平稳性,因为这是统计建模的基础之一,主要是为了简化。在金融领域,价格时间序列不是平稳的,因为它们显示具有不同方差(波动性)的趋势。看一下图 3-18,看看您能否检测到趋势。您会说这个时间序列是平稳的吗?

图 3-18。模拟数据随时间变化的均值
自然地,答案是否定的,因为一个上升趋势明显正在进行中。这种状态对于统计分析和机器学习是不可取的。幸运的是,您可以对时间序列应用转换使其平稳。但首先,让我们看看如何以数学方式检查平稳性,因为视觉方式并不能证明任何事情。处理数据平稳性问题的正确过程是遵循以下步骤:
-
使用本节中将看到的不同统计测试来检查平稳性。
-
如果测试显示数据平稳性,您就可以准备使用数据进行不同的学习算法。如果测试显示数据不平稳,您必须继续进行下一步。
-
应用您将在本节中看到的价格转换技术。
-
使用相同测试在新转换的数据上重新检查平稳性。
-
如果测试显示数据平稳性,那么您已成功转换了数据。否则,请重新进行转换并再次检查,直到您获得平稳数据。
注意
升序或降序时间序列随时间变化的均值和方差,因此很可能是非平稳的。当然也有例外情况,稍后您将看到原因。
请记住,平稳性的目标是随时间稳定和恒定的均值和方差。因此,当您看图 3-19 时,您能说些什么?

图 3-19。模拟数据在时间上均值围绕零
从视觉上看,数据似乎没有趋势,看起来围绕着一个稳定的均值波动,具有稳定的方差。第一印象是数据是平稳的。当然,这必须通过统计测试来证明。
第一个和最基本的测试是增广迪基-富勒(ADF)测试。ADF 测试使用假设检验来检验平稳性。
ADF 测试在数据中寻找单位根。单位根是非平稳数据的一个特性,在时间序列分析的背景下,指的是随机过程的一个特征,其中系列的根等于 1。简单来说,这意味着其统计特性,如均值和方差,会随时间变化。以下是你需要知道的内容:
-
零假设假定存在单位根。这意味着如果你试图证明数据是平稳的,你要拒绝零假设(如第二章的假设检验部分所示)。
-
因此,备择假设是不存在单位根,数据是平稳的。
-
从测试中获得的 p 值必须小于所选的显著性水平(在大多数情况下,为 5%)。
让我们拿美国 CPI 的年度数据进行平稳性测试。以下代码片段使用 ADF 测试检查平稳性:
# Importing the required libraries
from statsmodels.tsa.stattools import adfuller
import pandas_datareader as pdr
# Setting the beginning and end of the historical data
start_date = '1950-01-01'
end_date = '2022-12-01'
# Creating a dataframe and downloading the CPI data using its code name and its source
cpi = pdr.DataReader('CPIAUCSL', 'fred', start_date, end_date)
# Dropping the NaN values from the rows
cpi = cpi.dropna()
# Transforming the US CPI into a year-on-year measure
cpi = cpi.pct_change(periods = 12, axis = 0) * 100
cpi = cpi.dropna()
# Applying the ADF test on the CPI data
adfuller(cpi)
print('p-value: %f' % adfuller(cpi)[1])
代码的输出如下:
p-value: 0.0152
假设 5%的显著性水平,似乎可以接受年度数据是平稳的(然而,如果你想更严格一些,使用 1%的显著性水平,那么 p 值表明数据是非平稳的)。无论如何,即使看图表也会让你感到困惑。请记住,在图 3-11 中,美国 CPI 的年度变化似乎是稳定的,但并不像是平稳数据。这就是为什么要使用数值和统计测试的原因。
现在,让我们做同样的事情,但省略年度变化。换句话说,对美国 CPI 原始数据应用代码,不考虑年度变化。以下是代码:
# Importing the required libraries
from statsmodels.tsa.stattools import adfuller
import pandas_datareader as pdr
# Setting the beginning and end of the historical data
start_date = '1950-01-01'
end_date = '2022-12-01'
# Creating a dataframe and downloading the CPI data using its code name and its source
cpi = pdr.DataReader('CPIAUCSL', 'fred', start_date, end_date)
# Dropping the NaN values from the rows
cpi = cpi.dropna()
# Applying the ADF test on the CPI data
adfuller(cpi)
print('p-value: %f' % adfuller(cpi)[1])
代码的输出如下:
p-value: 0.999
显然,p 值大于所有显著性水平,这意味着时间序列是非平稳的。让我们总结一下这些结果:
-
似乎在美国 CPI 的年度变化方面,可以使用 5%的显著性水平拒绝零假设。数据集被假定为平稳。
-
似乎在美国 CPI 的原始数值方面,无法使用 5%的显著性水平拒绝零假设。数据集被假定为非平稳。
当你绘制美国 CPI 的原始数值时,这一点变得明显,如图 3-20 所示。

图 3-20。美国 CPI 的绝对数值显示出明显的趋势性
你必须了解的另一个测试被称为Kwiatkowski-Phillips-Schmidt-Shin(KPSS),这也是一个旨在确定时间序列是平稳还是非平稳的统计测试。然而,KPSS 测试可以检测趋势时间序列中的平稳性,使其成为一个强大的工具。
趋势时间序列实际上可以是平稳的,条件是它们具有稳定的均值。
警告
ADF 测试有一个假设检验,主张非平稳性,另一个假设主张平稳性。另一方面,KPSS 测试有一个假设主张平稳性,另一个假设主张非平稳性。
在分析通货膨胀数据之前,让我们看看趋势时间序列如何可以是平稳的。记住,平稳性指的是稳定的均值和标准差,所以如果你有一个逐渐上升或下降的时间序列,具有稳定的统计特性,它可能是平稳的。下一个代码片段模拟了一个正弦波,然后加入了一丝趋势性:
# Importing the required libraries
import numpy as np
import matplotlib.pyplot as plt
# Creating the first time series using sine waves
length = np.pi * 2 * 5
sinewave = np.sin(np.arange(0, length, length / 1000))
# Creating the second time series using trending sine waves
sinewave_ascending = np.sin(np.arange(0, length, length / 1000))
# Defining the trend variable
a = 0.01
# Looping to add a trend factor
for i in range(len(sinewave_ascending)):
sinewave_ascending[i] = a + sinewave_ascending[i]
a = 0.01 + a
如图 3-21 所示绘制两个系列的图表显示了趋势正弦波似乎是稳定的。但让我们通过统计检验来证明这一点。

图 3-21. 具有趋势正弦波的正常正弦波模拟系列
图 3-21 是使用以下代码生成的(确保您已经使用前面的代码块定义了系列):
# Plotting the series
plt.plot(sinewave, label = 'Sine Wave', color = 'black')
plt.plot(sinewave_ascending, label = 'Ascending Sine Wave', color = 'grey')
# Calling the grid function for better interpretability
plt.grid()
# Calling the legend function to show the labels
plt.legend()
# Showing the plot
plt.show()
让我们对两个系列进行 ADF 测试,看看结果如何:
# ADF testing | Normal sine wave
adfuller(sinewave)
print('p-value: %f' % adfuller(sinewave)[1])
# ADF testing | Ascending sine wave
adfuller(sinewave_ascending)
print('p-value: %f' % adfuller(sinewave_ascending)[1])
输出如下:
p-value: 0.000000 # For the sine wave
p-value: 0.898635 # For the ascending sine wave
显然,ADF 测试与趋势市场不能是平稳的观念一致。但 KPSS 测试呢?以下代码使用 KPSS 在相同数据上检查平稳性:
# Importing the KPSS library
from statsmodels.tsa.stattools import kpss
# KPSS testing | Normal sine wave
kpss(sinewave)
print('p-value: %f' % kpss(sinewave)[1])
# KPSS testing | Ascending sine wave
kpss(sinewave_ascending)
print('p-value: %f' % kpss(sinewave_ascending)[1])
# KPSS testing while taking into account the trend | Ascending sine wave
kpss(sinewave_ascending, regression = 'ct')
print('p-value: %f' % kpss(sinewave_ascending, regression = 'ct')[1])
`'''
The 'ct' argument is used to check if the dataset is stationary
around a trend. By default, the argument is 'c' which is is used
to check if the data is stationary around a constant.
'''`
输出如下:
p-value: 0.10 # For the sine wave
p-value: 0.01 # For the ascending sine wave without trend consideration
p-value: 0.10 # For the ascending sine wave with trend consideration
请记住,KPSS 测试的零假设是数据是平稳的,因此如果 p 值大于显著性水平,则数据被认为是平稳的,因为无法拒绝零假设。
考虑趋势时,KPSS 统计量表明上升的正弦波是一个平稳的时间序列。这是一个基本示例,展示了如何在趋势时间序列中找到平稳数据。
让我们拿美国 CPI 年度数据来检验其平稳性。以下代码片段使用 KPSS 测试检查平稳性:
# Importing the required libraries
from statsmodels.tsa.stattools import kpss
import pandas_datareader as pdr
# Setting the beginning and end of the historical data
start_date = '1950-01-01'
end_date = '2022-12-01'
# Creating a dataframe and downloading the CPI data using its code name and its source
cpi = pdr.DataReader('CPIAUCSL', 'fred', start_date, end_date)
# Dropping the NaN values from the rows
cpi = cpi.dropna()
# Transforming the US CPI into a year-on-year measure
cpi = cpi.pct_change(periods = 12, axis = 0) * 100
cpi = cpi.dropna()
# Applying the KPSS (no trend consideration) test on the CPI data
kpss(cpi)
print('p-value: %f' % kpss(cpi)[1])
# Applying the KPSS (with trend consideration) test on the CPI data
kpss(cpi, regression = 'ct')
print('p-value: %f' % kpss(cpi, regression = 'ct')[1])
代码的输出如下:
p-value: 0.036323 # without trend consideration
p-value: 0.010000 # with trend consideration
看起来 KPSS 测试的结果与 ADF 测试的结果相矛盾。这种情况可能会时有发生,差分可能会解决问题(请记住,年度数据已经是绝对 CPI 值的差分时间序列,但有些时间序列可能需要多次差分才能变得平稳,这也取决于差分的周期)。在矛盾情况下,最安全的解决方案是再次转换数据。
在结束关于平稳性的本节之前,让我们讨论一个复杂的主题,您将在第七章中看到其实际应用。转换数据可能会导致一个不寻常的问题,即内存丢失。在他的书中,Marco Lopez de Prado提出了一种称为分数差分的技术,旨在使数据保持平稳同时保留一些记忆。
当对非平稳时间序列进行差分以使其平稳时,会发生内存丢失,这另一种说法是值之间的自相关大大减少,从而消除了趋势成分和基础资产的 DNA。差分的程度和原始系列中自相关结构的持久性决定了有多少内存丢失。
注意
本节介绍了许多复杂的概念。您应该记住以下内容:
-
平稳性是指随着时间稳定的均值和方差的概念。大多数机器学习模型都依赖于这一特性。
-
金融价格时间序列很可能是非平稳的,需要进行一阶转换才能变得平稳并准备好进行统计建模。有些甚至可能需要进行二阶转换才能变得平稳。
-
ADF 和 KPSS 测试检查数据中的平稳性,后者能够检查趋势数据中的平稳性,因此更加彻底。
-
趋势数据可能是平稳的。尽管这种特性很少见,但 KPSS 能够检测到平稳性,与 ADF 测试相反。
回归分析和统计推断
推断,如牛津语言所定义,是基于证据和推理得出的结论。因此,与描述性统计相反,推断统计使用数据或数据样本进行推断(预测)。统计推断中的主要工具是线性回归。
线性回归是一种基本的机器学习算法,在本书中您将在第七章与其他机器学习算法中看到。因此,让我们在本节中介绍回归分析的直觉。
线性回归方程的最基本形式如下:
基本线性回归方程说明,一个因变量(您想要预测的内容)由一个常数、一个敏感度调整的变量和一个残差(用于解释未解释变化的误差项)来解释。考虑表 3-3:
表 3-2. 预测表
| y | x |
|---|---|
| 100 | 49 |
| 200 | 99 |
| 300 | 149 |
| 400 | 199 |
| ? | 249 |
预测y给定x的线性方程如下:
因此,给定x = 249 时,最新的y应该是 500:
注意线性回归如何完美捕捉两个变量之间的线性关系,因为没有残差(未解释的变化)。当线性回归完美捕捉两个变量之间的关系时,这意味着它们的坐标点完美对齐在沿 x 轴的一条线上。
多元线性回归可以采用以下形式:
这基本上意味着因变量y可能受到多个变量的影响。例如,如果您想要估计房价,您可能需要考虑房间数量、面积、社区和可能影响价格的任何其他变量。同样,如果您想要预测商品价格,您可能需要考虑不同的宏观经济因素、货币价值和任何其他替代数据。
重要的是要理解每个变量所指的内容。确保记住上述公式。线性回归有一些假设:
线性关系
因变量和自变量之间的关系应该是线性的,意味着平面上的一条直线可以描述这种关系。在处理复杂的金融变量时,这在现实生活中是罕见的。
变量的独立性
观察结果应该彼此独立,意味着一个观察结果的值不会影响另一个观察结果的值。
同方差性
残差的方差(因变量的预测值与实际值之间的差异)应该在自变量的所有水平上保持恒定。
残差的正态性
残差应该呈正态分布,意味着大多数残差接近零,分布是对称的。
在多元线性回归的情况下,您可以添加一个新的假设,即多重共线性的缺失。自变量之间不应高度相关,否则可能难以确定每个自变量对因变量的独特影响。换句话说,这可以防止冗余。在第七章中,您将看到更详细的线性回归示例,本节仅将其作为统计学领域的一部分进行介绍。
注意
现在,您应该对统计学的关键概念有扎实的理解。让我们总结一下您需要记住的一切:
-
线性回归是推断统计学领域的一部分,它是描述变量之间关系的线性方程。
-
线性回归根据您训练过去数据并期望关系在未来保持的方程来解释和预测数据。
总结
能够进行数据分析是部署正确算法以预测时间序列未来值的关键。通过来自统计学领域的各种工具来理解数据。确保您了解什么是平稳性和相关性,因为它们在建模中提供极其有价值的见解。
¹ 其他方式也有,但这两种方式是最流行的表示形式。
第四章:深度学习的线性代数和微积分
代数和微积分是数据科学的支柱,特别是基于这两个数学领域的概念的学习算法。本章以一种所有人都能理解的方式介绍了一些关键的代数和微积分主题。
了解为什么要学习某样东西是有帮助的。这样,你就会获得继续学习的动力,也知道应该把注意力放在哪里。
代数是研究运算和关系规则以及由此产生的构造和思想。代数涵盖了诸如线性方程和矩阵等主题。你可以将代数看作通往微积分的第一步。
微积分是研究曲线斜率和变化率的学科。微积分涵盖了诸如导数和积分等主题。它在许多领域如经济学和工程学中被广泛使用。不同的学习算法依赖微积分的概念来执行其复杂操作。
两者之间的区别在于,微积分处理变化、运动和积累的概念,而代数处理数学符号以及操纵这些符号的规则。微积分关注变化函数的特性和行为,而代数为解方程和理解函数提供了基础。
[待补充标题]
向量和矩阵
向量是一个具有大小(长度)和方向(箭头)的对象。向量的基本表示是带有坐标的箭头。但首先,让我们看看什么是坐标轴。
x 轴和 y 轴是垂直线,指定了平面的边界以及二维笛卡尔坐标系中不同点的位置。x 轴是水平的,y 轴是垂直的。
这些坐标轴可以表示向量,其中 x 轴表示向量的水平分量,y 轴表示其垂直分量。
图 4-1 显示了一个简单的二维笛卡尔坐标系,带有两个坐标轴。

图 4-1. 二维笛卡尔坐标系
二维笛卡尔坐标系使用简单的括号来显示不同点的位置,遵循以下顺序:
因此,如果你想绘制坐标为(2,3)的点 A,你可能会从零点开始查看图表,向右移动两个点,然后向上移动三个点。点的结果应该看起来像图 4-2。

图 4-2. A 在坐标系上的位置
现在让我们添加另一个点并在它们之间绘制一个向量。假设你还有坐标为(4,5)的点 B。自然地,由于 B 的坐标都高于 A 的坐标,你会期望向量 AB 是向上倾斜的。图 4-3 显示了新点 B 和向量 AB。

图 4-3. 向量 AB 连接 A 和 B 点的大小和方向
然而,绘制了使用两点坐标的向量后,你如何引用这个向量呢?简单来说,向量 AB 有自己的坐标来表示它。记住,向量是从点 A 到点 B 的移动的表示。这意味着沿着 X 轴和 Y 轴的两点移动就是向量。在数学上,要找到向量,你应该将两个坐标点相减,同时保持方向。具体操作如下:
- 向量 AB意味着你从 A 到 B,因此,你需要从点 B 的坐标中减去点 A 的坐标:
- 向量 BA表示从 B 到 A,因此,您需要从点 A 的坐标中减去点 B 的坐标:
解释 AB 和 BA 向量时,你要考虑移动。AB 向量表示从点 A 到点 B 的移动,水平和垂直方向分别为两个正点(向右和向上)。BA 向量表示从点 B 到点 A 的移动,水平和垂直方向分别为两个负点(向左和向下)。
注意
向量 AB 和 BA 虽然具有相同的斜率,但它们并不是同一物体。但是斜率到底是什么?
斜率是线上两点之间的垂直变化与水平变化之间的比率。您可以使用以下数学公式计算斜率:
如果这两个向量只是线(没有方向),那么它们将是相同的对象。然而,添加方向性组件使它们成为两个可区分的数学对象。
图 4-4 更详细地解释了斜率的概念,因为 x 向右移动了两个点,y 向左移动了两个点。

图 4-4。向量 AB 的 x 和 y 的变化
图 4-5 展示了向量 BA 的 x 和 y 的变化。

图 4-5。向量 BA 的 x 和 y 的变化
注意
具有大小为 1 的向量称为单位向量。
研究人员通常使用向量作为速度的表示,尤其在工程领域。导航是一个严重依赖向量的领域。它允许导航员确定他们的位置并规划他们的目的地。自然地,大小表示速度,方向表示目的地。
您可以将向量相互相加和相减,也可以与标量相加和相减。这允许方向和大小的变化。您应该从前面的讨论中记住的是,向量表示轴上不同点之间的方向。
注意
标量是具有大小但没有方向的值。与向量相反,标量用于表示温度和价格等元素。基本上,标量是数字。
在机器学习中,x 轴和 y 轴分别代表数据和模型的结果。在散点图中,独立(预测)变量由 x 轴表示,而依赖(预测)变量由 y 轴表示。
矩阵是一个按行和列组织的包含数字的矩形数组。矩阵在计算机图形和其他领域中很有用,用于定义和操作线性方程组。矩阵与向量有何不同?最简单的答案是,向量是具有单列或单行的矩阵。这是一个 3 x 3 矩阵的基本示例:
矩阵的大小分别使用它们的行和列来表示。行是水平线,列是垂直线。以下表示是一个 2 x 4 矩阵:
以下表示是另一个矩阵的示例。这次是一个 4 x 2 矩阵:
注意
矩阵在机器学习中被广泛使用。行通常表示时间,列表示特征。
不同矩阵的求和很简单,但只有在矩阵大小相匹配时才能使用(即它们具有相同的列数和行数)。例如,让我们将以下两个矩阵相加:
您可以看到,要添加两个矩阵,只需将相同位置的数字相加。现在,如果您尝试添加以下两个矩阵,您将无法做到,因为要添加的内容不匹配:
矩阵的减法也很简单,遵循与矩阵加法相同的规则。让我们看下面的例子:
显然,矩阵的减法也是矩阵的加法,其中一个矩阵中的信号发生变化。
矩阵乘以标量是非常简单的。让我们看下面的例子:
基本上,您正在将矩阵中的每个元素乘以标量。矩阵乘以另一个矩阵稍微复杂,因为它们使用点乘方法。首先,为了将两个矩阵相乘,它们必须满足这个条件:
这意味着第一个矩阵的列数必须等于第二个矩阵的行数,点乘的结果矩阵是第一个矩阵的行数和第二个矩阵的列数。点乘在这个 1 x 3 和 3 x 1 矩阵乘法的示例表示中解释(注意相同的列数和行数):
让我们再看一个 2 x 2 矩阵乘法的例子:
有一种特殊类型的矩阵称为单位矩阵,基本上是矩阵的数字 1。对于 2 x 2 维度,它定义如下:
对于 3 x 3 维度,如下所示:
将任何矩阵乘以单位矩阵会产生相同的原始矩阵。这就是为什么它可以被称为矩阵的 1(将任何数字乘以 1 会得到相同的数字)。值得注意的是,矩阵乘法不是可交换的,这意味着乘法的顺序会改变结果,例如:
矩阵转置是一个过程,涉及将行变为列,反之亦然。矩阵的转置是通过沿其主对角线反射矩阵获得的:
转置在一些机器学习算法中使用,并且在处理这些模型时不是一个罕见的操作。如果您想了解数据科学和机器学习中矩阵的作用,可以参考这个非详尽的列表:
数据表示
矩阵通常表示具有行表示样本和列表示特征的数据。例如,矩阵中的一行可以表示一个时间步长中的 OHLC 数据。
线性代数
矩阵和线性代数是相互交织的,许多学习算法在其操作中使用矩阵的概念。
数据关系矩阵
如果您还记得第三章,协方差和相关性度量通常表示为矩阵。这些关系计算是机器学习中重要的概念。
注意
您应该从本节中保留以下关键概念:
-
向量是一个具有大小(长度)和方向(箭头头部)的对象。多个向量组合在一起形成一个矩阵。
-
矩阵可用于存储数据。它有其特殊的操作方式。
-
矩阵乘法使用点乘方法。
-
转置矩阵意味着交换其行和列。
线性方程简介
您已经在讨论线性回归和第三章统计推断中看到了线性方程的一个例子。线性方程基本上是呈现不同变量和常数之间的等式关系的公式。在机器学习的情况下,它通常是依赖变量(输出)和自变量(输入)之间的关系。理解线性方程的最佳方法是通过示例。
注意
线性方程的目的是找到一个未知变量,通常用字母x表示。
让我们看一个非常基本的例子,您可以将其视为以后将看到的更高级微积分概念的第一个基本构建块。以下例子需要找到x的值:
您应该将方程理解为“10 乘以哪个数字等于 20?”当一个常数直接附加到变量(如x)时,它指的是一个乘法运算。现在,要解出x(即找到使方程相等的x的值),您有一个明显的解决方案,即摆脱 10,这样您就可以在方程的一边得到x,而在另一边得到其余部分。
自然地,为了消除 10,您应该除以 10,这样剩下的是 1,如果乘以变量x则不会有任何变化。但是,请记住两个重要的事情:
-
如果在方程的一边进行数学运算,则必须在另一边进行相同的运算。这就是它们被称为方程的原因。
-
为简单起见,您应该将常数乘以其倒数而不是除以它来消除它。
一个数字的倒数是该数字的倒数。这是它的数学表示:
现在,回到例子,要找到x,您可以这样做:
进行乘法并简化得到以下结果:
这意味着方程的解是 2。要验证这一点,您只需要将其代入原方程如下:
因此,需要两个 10 来得到 20。
注意
将数字除以自身或乘以其倒数是相同的。
让我们看另一个通过线性技术解决x的例子。考虑以下问题:
进行乘法并简化得到以下结果:
这意味着方程的解是 18。要验证这一点,您只需要将其代入原方程如下:
让我们深入一点,因为通常,线性方程并不是这么简单。有时它们包含更多的变量和常数,需要更详细的解决方案,但让我们一步一步地继续。考虑以下例子:
解出x需要稍微重新排列方程。记住,目标是让x在一边,其余在另一边。在处理 3 之前,您必须摆脱常数 6。解的第一部分如下:
注意您必须在方程的两边都加上 6。左边的部分将自行取消,而右边的部分将加起来得到 18:
最后,您可以通过乘以附加到变量x的常数的倒数来解决:
简化并解出x得到以下解:
这意味着方程的解是 6。要验证这一点,只需将其代入原方程如下:
到目前为止,您应该开始注意到线性代数是关于使用快捷方式和快速技巧简化方程并找到未知变量的。下一个例子展示了有时变量x可能出现在多个地方:
记住,主要重点是让x在方程的一边,其余在另一边:
添加x的常数得到以下结果:
最后一步是除以 9,这样您只剩下x:
现在您可以通过在原方程中将 3 代入x来验证这一点。您会注意到方程的两边是相等的。
注意
尽管这一部分非常简单,但它包含了您在代数和微积分中开始进阶所需的基本基础。在继续之前需要记住的主要要点如下:
-
线性方程是一种表示,其中任何变量的最高指数为一。这意味着没有变量被提高到二次及以上的幂。
-
线性方程在图表上绘制时是直线。
-
线性方程在建模各种现实世界事件中的应用使它们在许多数学和研究领域至关重要。它们也广泛应用于机器学习。
-
解决x的过程是找到使等式两边相等的值。
-
在方程的一侧执行操作(如加上一个常数或乘以一个常数)时,你必须在另一侧也这样做。
-
倒数在简化方程时很有用。
方程组
方程组是指两个或更多个方程共同解决一个或多个变量的情况。因此,与通常的单个方程不同,如下所示:
方程组类似于以下内容:
有解决它们的方法和特殊情况,在本节中进行讨论。方程组在机器学习中很有用,并在其许多方面中使用。
让我们从本节开头的前一个方程组开始,通过图形方式解决它。绘制两个函数实际上可以直接给出解。线性方程的交点是解。因此,交点的坐标(x, y)分别指代x和y的解。
从图 4-6 可以看出,x=10,y=-10。将这些值代入各自的变量中可以得到正确答案:

图 4-6。显示两个函数及其交点(解)的图形
由于函数是线性的,解它们有三种情况:
-
每个变量只有一个解。
-
没有解。当函数是平行时发生(这意味着它们永远不会相交)。
-
有无限多个解。当它是相同的函数时发生(因为所有点都落在直线上)。
在使用代数解方程组之前,让我们直观地看看如何可能没有解以及如何可能有无限多个解。考虑以下系统:
图 4-7 将两者绘制在一起。由于它们完全相同,它们落在同一条线上。实际上,图 4-7 中有两条线,但由于它们相同,它们是无法区分的。对于线上的每个x,都有一个对应的y。

图 4-7。显示两个函数及其无限交点的图形
现在,考虑以下系统:
图 4-8 显示它们永远不会相交,这是直观的,因为你不能用不同的数字(由变量x表示)乘以相同的数字并期望得到相同的结果。

图 4-8。显示两个函数及其不可能的交点的图形
当有两个以上的变量时,代数方法用于解决它们,因为它们不能通过图形解决。这主要涉及两种方法,替换和消元。
替换是当你可以用一个方程中的变量值替换另一个方程中的变量值时使用的。考虑以下示例:
最简单的方法是重新排列第一个方程,使得你可以用x表示y:
在第二个方程中解x变得简单:
现在你已经找到了x的值,你可以通过将x的值代入第一个方程中轻松找到y:
要检查你的解是否正确,你可以将x和y的值分别代入两个公式中:
从图形上看,这意味着两个方程在(0.8889, 1.111)处相交。这种技术可以用于多于两个变量。按照相同的过程,直到方程简化到足以给出答案。替换的问题在于当你处理多于两个变量时可能需要一些时间。
消元是一种更快的替代方法。它是通过消除变量直到只剩下一个的方法。考虑以下示例:
注意到有 4y和 2y,可以将第二个方程乘以 2,然后将两个方程相减(这将消除y变量):
将两个方程相减得到以下结果:
因此,x=0。从图形上看,这意味着它们在x=0 时相交(正好在垂直的y线上)。将x的值代入第一个公式得到y=5:
同样,消元法也可以解决具有三个变量的方程。选择替换和消元之间取决于方程的类型。
注意
本节的要点可以总结如下:
-
方程组解决变量。它们在机器学习中非常有用,并且在一些算法中使用。
-
对于简单的方程组,图形解法是首选。
-
通过代数解方程组需要使用替换和消元方法。
-
当系统比较简单时,首选替换法,但当系统稍微复杂时,消元法是更好的选择。
三角学
三角学探讨了所谓的三角函数*的行为,这些函数将三角形的角度与其边长联系起来。最常用的三角形是直角三角形,其中一个角为 90°。图 4-9 展示了一个直角三角形的例子。
图 4-9。直角三角形
让我们定义直角三角形的主要特征:
-
三角形的最长边称为斜边。
-
斜边前面的角度是直角(90°的角度)。
-
根据您选择的另一个角度(θ)(从剩下的两个中选择),连接这个角度和斜边之间的线称为邻边,另一条线称为对边。
三角函数简单地是一条线除以另一条线。记住三角形中有三条线(斜边、对边和邻边)。三角函数的计算如下:
从前述三个三角函数中,可以提取一个三角恒等式,通过基本的线性代数从sin和cos得到tan:
双曲函数类似于三角函数,但是使用指数函数定义。
注意
关于双曲函数的部分非常重要,因为它构成了所谓的激活函数的基础,这是神经网络中的关键概念,是深度学习模型的主角。您将在第八章中详细了解它们。
欧拉数(表示为e)是数学中最重要的数字之一。它是一个无理数,即不能表示为分数的实数。 无理 一词源于无法用比例表达它的事实;这与其个性无关。欧拉数e也是自然对数ln的底数,其前几位数字为 2.71828。获得e的最佳近似值的一个公式如下:
通过增加前述公式中的n,您将接近e的值。它有许多有趣的性质,特别是其斜率等于其自身的值。让我们以以下函数为例(也称为自然指数函数):
在任何点,函数的斜率都是相同的值。看一下图 4-10。
图 4-10。自然指数函数的图形
注意
您可能会想知道为什么这本书要解释指数和对数。主要有两个原因:
-
指数和更重要的是欧拉数在双曲函数中使用,其中tanh(x)是神经网络的主要激活函数之一,这是一种机器学习和深度学习模型。
-
对数在数据归一化以及损失函数中非常有用,这些概念将在后面的章节中看到。
因此,在建立对后续模型的专业知识时,深入理解它们所指的内容是至关重要的。
双曲函数使用自然指数函数,并定义如下:
tanh(x)的关键特征之一是非线性,限制在[-1, 1]之间,并且它以零为中心。图 4-11 显示了tanh(x)的图形。

图 4-11. tanh(x)的图形显示它在-1 和 1 之间的限制
注意
从本节中保留的关键概念总结如下:
-
三角学是一门探索三角函数行为的领域,它将三角形的角与其边长联系起来。
-
三角恒等式是一种将三角函数相互关联的快捷方式。
-
欧拉数e是无理数,是自然对数的底。它在指数增长和双曲函数中有许多应用。
-
双曲函数类似于三角函数,但并不相同。虽然三角函数涉及三角形和圆,双曲函数涉及双曲线。
-
双曲正切函数在神经网络中使用,这是一种深度学习算法。
极限与连续性
微积分通过使微小量可见来工作。
- Keith Devlin
在看完线性代数的主要主题后,让我们现在转向微积分。极限不必是噩梦。我一直发现它们被误解了。实际上,它们很容易理解。但首先,你需要动力,这来自于了解学习极限的附加价值。
对于许多原因,深入了解极限在机器学习模型中非常重要:
优化
在像梯度下降这样的优化方法中,极限可以用来调节步长并确保收敛到局部最小值(这是您将在第八章中学到的概念)。
特征选择
极限可用于排列各种模型特征的重要性并执行特征选择,这可以使模型更简单且性能更好。
敏感性分析
机器学习模型对输入数据变化的敏感性以及泛化到新数据的能力可以用来检查模型的行为。
此外,极限在您将在接下来的页面中遇到的更高级的微积分概念中使用。
极限的主要目的是在函数未定义时知道函数的值。但什么是未定义的函数?当你有一个给出不可能解决方案的函数(例如除以零)时,极限帮助你绕过这个问题,以便知道该点的函数值。因此,极限的目的是解决函数,即使它们是未定义的。
请记住,将x作为输入的函数的解是y轴上的一个值。图 4-12 显示了以下函数的线性图:

图 4-12. 函数 f(x) = x + 2 的图形
图中函数的解是考虑到每次x的值的线性线上的值。
当x = 4 时,函数的解(y的值)是多少?显然,答案是 6,因为将x的值替换为 4 会得到 6。
从极限的角度来看这个解,就是说,当x从两侧(负侧和正侧)趋近于 4 时,函数的解是什么?表 4-1 简化了这个困境:
表 4-1. 寻找 x
| f(x) | * x* |
|---|---|
| 5.998 | 3.998 |
| 5.999 | 3.999 |
| 6.000 | 4 |
| 6.001 | 4.001 |
| 6.002 | 4.002 |
从负侧接近相当于在 4 以下添加一个数字的一部分并分析结果。同样,从正侧接近相当于在 4 以上减去一个数字的一部分并分析结果。当 x 趋近于 4 时,解似乎收敛到 6。这就是极限的解。
一般形式的极限按照以下约定书写:
极限的一般形式读作:当你沿着x轴接近a时(无论是从正侧还是负侧),函数f(x)趋近于值L。
注
极限的概念表明,当你从任一侧(负数或正数)锁定并接近一个数字时,方程的解趋近于某个数字,极限的解就是那个数字。
如前所述,当使用传统的代入方法无法定义解的确切点时,极限是有用的。
单侧极限与一般极限不同。左侧极限是你从负侧到正侧寻找极限,右侧极限是你从正侧到负侧寻找极限。当两个单侧极限存在且相等时,一般极限存在。因此,前述陈述总结如下:
-
左侧极限存在。
-
右侧极限存在。
-
左侧极限等于右侧极限。
左侧极限定义如下:
右侧极限定义如下:
让我们看下面的方程:
当 x = 3 时函数的解是什么?代入会导致以下问题:
然而,从表 4-2 所示的极限的角度来看,当你接近x = 3时,无论是从左侧还是右侧,解都趋向于 27。
表 4-2。找到 x
| f(x) | x |
|---|---|
| 2.9998 | 26.9982 |
| 2.9999 | 26.9991 |
| 3.0000 | 未定义 |
| 3.0001 | 27.0009 |
| 3.0002 | 27.0018 |
从图形上看,这可以看作是图表中沿着两个轴的不连续性。不连续性存在于坐标(3,27)附近的线上。
有些函数没有极限。例如,当x趋近于 5 时,以下函数的极限是多少?
看看表 4-3,当x趋近于 5 时,结果在从两侧接近时高度发散。例如,从负侧接近,4.9999 的极限是-10,000,从正侧接近,5.0001 的极限是 10,000。
| f(x) | x |
|---|---|
| 4.9998 | -5000 |
| 4.9999 | -10000 |
| 5.0000 | 未定义 |
| 5.0001 | 10000 |
| 5.0002 | 5000 |
请记住,一般极限存在时,两个单侧极限必须存在且相等,而这里并非如此。绘制图表得到图 4-13,这可能有助于理解为什么极限不存在。

图 4-13。证明极限不存在的函数图
但是如果你想分析的函数看起来像这样呢:
看看表 4-3,似乎当 x 趋近于 5 时,结果迅速加速,趋向于一个非常大的数字,称为无穷大(∞)。看看表 4-4:
表 4-3。找到 x
| f(x) | x |
|---|---|
| 4.99997 | 33333.33 |
| 4.99998 | 50000 |
| 4.99999 | 100000 |
| 4.9999999 | 10000000 |
| 5 | 未定义 |
| 5.0000001 | 10000000 |
| 5.00001 | 100000 |
| 5.00002 | 50000 |
| 5.00003 | 33333.33 |
看看每一个微小步骤中x趋近于 5 时,y趋近于正无穷大。因此,极限问题的答案是正无穷大(+∞)。图 4-14 显示了函数的图形。注意当 x 趋近于 5 时它们的值都在上升。

图 4-14。证明当 x 趋近于 5 时极限存在的函数图
连续函数是在图表中没有间隙或空洞的函数,而不连续函数包含这样的间隙和空洞。这通常意味着后者包含函数解未定义的点,可能需要通过极限来近似。因此,连续性和极限是两个相关的概念。
让我们继续解决极限问题;毕竟,你不会每次都创建一个表格并主观分析结果来找到极限。解决极限有三种方法:
-
替换:这是最简单的规则,通常首先使用。
-
因子分解:这是在替换无效后进行的。
-
共轭方法:这是在前两种方法不起作用后的解决方案。
替换方法就是简单地插入x趋近的值。基本上,这些是具有解决方案的函数,其中使用了极限。看下面的例子:
使用替换方法,函数的极限如下找到:
因此,极限的答案是 5。
因子分解方法是替换无效时的下一个选择(例如,在函数中插入x的值后极限未定义)。因子分解是通过使用因子改变方程的形式的方式,使得在使用替换方法时不再是未定义的。看下面的例子:
如果尝试替换方法,将得到一个未定义的值,如下所示:
在这种情况下,因子可能有所帮助。例如,分子乘以(x+6),然后除以(x+6)。通过取消两个项来简化这个过程可能会得到一个解决方案:
现在因子分解已完成,你可以再次尝试替换:
因此,当x趋向于-6 时,函数的极限为 43。
共轭方法是替换和因子分解无效时的下一个选择。共轭简单地是两个变量之间符号的改变。例如,x + y的共轭是x - y。在分数的情况下,通过将分子和分母乘以其中一个的共轭(最好使用具有平方根的项的共轭,因为它将被取消)来执行此操作。看下面的例子:
通过将两个项都乘以分母的共轭,你已经开始使用共轭方法来解决问题:
考虑乘法和简化后得到以下结果:
你将留下以下熟悉的情况:
现在,函数已经准备好进行替换:
因此,函数的解决方案是 6。正如你所看到的,有时需要对方程进行准备工作,以便进行替换。
注意
关于极限的这一部分的主要关键点如下:
-
极限有助于找到在某些点可能未定义的函数的解决方案。
-
为了一般极限存在,两个单侧极限必须存在且必须相等。
-
有几种方法可以找到函数的极限,尤其是替换、因子分解和共轭方法。
-
极限在机器学习中非常有用,比如敏感性分析和优化。
导数
导数测量函数在一个或多个输入变化时的变化。换句话说,它是给定点的函数变化率。
对导数有扎实的理解对于构建多种原因的机器学习模型很重要:
优化
为了最小化损失函数(你将在第八章中看到的概念),优化方法使用导数来确定最陡下降的方向并修改模型参数。梯度下降是机器学习中最常用的优化技术之一。
反向传播
在深度学习中执行梯度下降时,反向传播技术使用导数来计算损失函数相对于模型参数的梯度。
超参数调整
为了提高模型的性能,导数用于灵敏度分析和调整超参数(这是第八章中你将完全掌握的另一个概念)。
不要忘记你从前一节关于极限学到的东西,因为你在本节中也会需要它们。微积分主要涉及导数和积分。本节讨论导数及其用途。
你可以将导数看作是代表(或模拟)另一个函数在某一点的斜率的函数。斜率是一条线相对于水平线的位置的度量。正斜率表示线向上移动,负斜率表示线向下移动。
导数和斜率是相关概念,但它们并不是同一回事。这里是两者之间的主要区别:
-
斜率度量线的陡峭程度。它是 y 轴变化与x轴变化的比率。你已经在讨论线性代数的部分中看到过这一点。
-
导数描述了给定函数的变化率。当函数上两点之间的距离趋近于零时,该函数在该点的导数是切线斜率的极限。
在用通俗的语言解释导数并看一些例子之前,让我们看一下它们的正式定义(即它们在默认形式下的数学表示):
这个方程构成了解决导数的基础,尽管有许多快捷方式,你将学会并理解它们的来历。让我们尝试使用正式定义找到一个函数的导数。考虑以下方程:
要找到导数,将f(x)放入正式定义中,然后解决极限:
为了简化问题,让我们找到f(x + h),这样将其代入正式定义会更容易:
现在,让我们将 f(x + h)代入定义中:
注意有许多项可以简化,使公式变得更清晰。记住,你正在尝试找到极限,解决极限后才能找到导数:
由于可以将分子中的所有项除以分母h,因此分母h的除法为进一步简化提供了潜力:
现在是解决极限的时候了。因为方程很简单,第一次尝试是通过代入,你已经猜到,这是可能的。通过代入变量h并使其为零(根据极限),你得到以下结果:
这就是原始函数f(x)的导数。如果你想找到函数在x=2 时的导数,只需将其代入导数函数中:
看一下你刚刚解决的函数的图形。图 4-15 显示了原始函数的图形及其导数(直线)。注意f'(2)恰好在 8 处。当x=2 时,f(x)的斜率为 8。

图 4-15。原始的 f(x)及其导数 f'(x)
注
注意当f(x)触底并开始上升时,f'(x)在-2 处穿过零线。这是本章稍后你会了解的一个概念。
你不太可能每次想要找到导数时都使用正式定义(可以用在每个函数上)。有导数规则可以让你通过快捷方式节省大量时间。第一个规则被称为幂规则,这是一种找到具有指数的函数的导数的方法。
通常也可以用这种符号来表示导数(与f'(x)相同):
找到导数的幂规则如下:
基本上,这意味着通过将常数乘以指数然后从指数中减去 1 来找到导数。这里有一个例子:
请记住,如果变量没有附加常数,这意味着该常数等于 1。这里有一个更复杂的例子,但原则相同:
值得注意的是,即使常数不符合幂规则的一般形式,该规则也适用于常数。常数的导数为零。然而,了解为什么很有帮助,但首先,您必须了解这个数学概念:
有了这个说法,您可以想象常数始终乘以x的零次方(因为它不会改变它们的值)。现在,如果您想找到 17 的导数,下面是如何进行的:
正如您所知,任何与零相乘的数都会返回零作为结果。这给出了导数的常数规则如下:
当遇到分数或负数指数时,您可以按照相同的逻辑进行计算。
导数的乘积规则在两个函数相乘时非常有用。乘积规则如下:
让我们举个例子,使用乘积规则找到导数:
方程可以清楚地分成两个项,f(x)和g(x),如下:
在应用乘积规则之前,让我们找到这两个项的导数。请注意,一旦您理解了幂规则,找到f(x)和g(x)的导数就很容易了:
在应用乘积规则时,您应该得到以下结果:
图 4-16 显示了h(x)和h'(x)的图形。

图 4-16。原始h(x)及其导数h'(x)
下一步是看商规则,它处理两个函数的除法。正式定义如下:
让我们将其应用到以下函数中:
通常情况下,最好先找到f(x)和g(x)的导数,这种情况下它们明显是分开的,f(x)是分子,g(x)是分母。在应用商规则时,您应该得到以下结果:
指数导数处理应用于常数的幂规则。看看以下方程--您如何找到它的导数?
与通常的变量-基数-常数-指数不同,它是常数-基数-变量-指数。在尝试计算导数时,这种情况会有所不同。正式定义如下:
以下示例展示了如何完成这个过程:
之前提到的欧拉数有一个特殊的导数。当要找到e的导数时,答案很有趣:
这是因为自然对数函数和指数函数彼此是互逆的,所以,术语ln e等于 1。因此,指数函数* e*的导数就是它本身。
同时,让我们讨论对数导数。到目前为止,您应该知道什么是指数和对数。两种类型对数的一般定义如下:
请注意,在自然对数的第二个导数函数中,再次遇到了术语ln e,因此简化变得非常容易,因为它等于 1。
以下是一个例子:
使用正式定义,这个对数函数的导数如下:
注意
请记住,对数log的底数是 10,但自然对数ln的底数是e(~2.7182)
自然对数和对数函数实际上通过简单的乘法是线性相关的。如果您知道常数a的对数,您可以通过将a的对数乘以 2.303 来找到其自然对数ln。
导数中的一个重要概念是链式法则。让我们回到幂规则,它处理变量上的指数。记住以下公式来找到导数:
这是一个简化版本,因为只有x,但实际情况是您必须乘以指数下的项的导数。到目前为止,您只看到x作为指数下的变量。x的导数是 1,这就是为什么它被简化并隐藏的原因。然而,对于更复杂的函数,比如这个:
通过以下两个步骤找到函数的导数:
-
找到外部函数的导数,而不触及内部函数。
-
找到内部函数的导数并将其乘以剩余的函数。
因此,解决方案如下(知道4x + 1的导数只是 4):
指数函数也适用相同的规则。看下面的例子:
链式法则实际上可以被视为一个主要规则,因为它适用于任何地方,甚至在乘积法则和商法则中。
导数中还有更多的概念需要掌握,但由于本书不是一个完整的微积分大师课程,你至少应该知道导数的含义,如何找到它,它代表什么,以及如何在机器学习和深度学习中使用它。
注
这一部分关于导数的关键点如下:
-
导数测量函数在一个或多个输入变化时的变化。
-
幂法则用于找到函数的导数的幂。
-
乘积法则用于找到两个相乘的函数的导数。
-
商法则用于找到两个相除的函数的导数。
-
链式法则是用于微分(即找到导数的过程)的主要规则。由于简单,它经常被忽视。
-
导数在机器学习中起着至关重要的作用,例如启用优化技术,帮助模型训练,并增强模型的可解释性。
积分和微积分基本定理
积分是一个操作,表示给定区间内函数曲线下的面积。它是导数的反函数,这就是为什么它也被称为反导数。
找到积分的过程称为积分。积分可用于找到曲线下的面积,它们在金融领域中也被广泛使用,如风险管理、投资组合管理、概率方法,甚至期权定价。
理解积分的最基本方式是考虑计算函数曲线下的面积。这也可以通过手动计算x轴上的不同变化来完成,但这不会很准确(随着你增加更小的切片,准确性会增加)。因此,随着切片大小接近零,面积的准确性会更好。由于这是一个繁琐的过程,积分就在这里拯救了。
请记住,积分是导数的反函数。这很重要,因为它暗示了两者之间的直接关系。积分的基本定义如下:
前面的方程意味着f(x)的积分是一般函数F(x)再加上一个常数C,这个常数在最初的微分过程中被遗失了。以下是一个例子,更好地解释为什么需要加入常数:
考虑以下函数:
计算其导数,你会得到以下结果:
现在,如果你想要对其进行积分,以便回到原始函数(在这种情况下由大写字母F(x)代表,而不是f(x))?
通常,看到微分过程(即求导数),你会返回 2 作为指数,这会给出以下答案:
这看起来不像原始函数。缺少常数 5。但你无法知道这一点,甚至如果你知道有一个常数,那是什么?1?2?677?这就是为什么在积分过程中添加一个常数C,以便它代表丢失的常数。因此,积分问题的答案如下:
注
到目前为止,讨论仅限于不定积分,其中积分符号是裸露的(这意味着没有边界)。在定义完成积分所需的规则之后,你将看到这意味着什么。
对于幂函数(就像前面的函数一样),积分的一般规则如下:
这比看起来要简单得多。您只是反转了之前看到的幂规则。考虑以下示例:
要验证您的答案,您可以找到结果的导数(使用幂规则):
让我们再举一个例子。考虑以下积分问题:
自然地,使用规则,您应该找到以下结果:
让我们继续讨论定积分,这是在函数曲线下方的区域具有上下限的积分。因此,不定积分在曲线下方找到面积,而定积分在由点a和点b给定的区间内被限制。不定积分的一般定义如下:
这就是最简单的方法。您将解决积分,然后插入两个数字并将两个函数相减。考虑以下积分的评估(积分求解通常被称为评估积分):
第一步是理解被要求的内容。从积分的定义来看,似乎需要使用给定函数计算x-轴上[0, 2]之间的面积:
要在给定点处评估积分,只需按照以下方式插入值:
注意
在定积分中,常数C将始终抵消,因此您可以在这类问题中将其略去。
因此,f(x)图形下方和x-轴上方以及x-轴上[0, 6]之间的面积等于 60 平方单位。以下显示了积分的一些经验法则(毕竟,本章旨在刷新您的知识或让您对一些关键数学概念有基本的理解):
- 要找到一个常数的积分:
- 要找到一个变量的积分:
- 要找到倒数的积分:
- 要找到指数的积分:
微积分基本定理将导数与积分联系起来。这意味着它以积分的形式定义导数,反之亦然。微积分基本定理实际上由两部分组成:
第一部分
微积分基本定理的第一部分规定,如果您有一个连续函数f(x),那么原始函数F(x)定义为f(x)的不定积分,从固定起点a到x,是一个从a到x处处可微的函数,其导数就是在x处评估的f(x)。
第二部分
微积分基本定理的第二部分规定,如果您有一个在某个区间[a, b]上连续的函数f(x),并且您定义一个新函数F(x)为f(x)从a到x的积分,那么f(x)在该区间[a, b]上的定积分可以计算为F(b) - F(a)。
该定理在许多领域中都很有用,包括物理学和工程学,但优化和其他数学模型也受益于它。在不同学习算法中使用积分的一些示例可以总结如下:
密度估计
积分在密度估计中使用,这是许多机器学习算法的一部分,用于计算概率密度函数。
强化学习
积分在强化学习中用于计算奖励函数的期望值。强化学习在第十章中介绍。
贝叶斯模型
积分在贝叶斯推断中使用,这是一种用于建模不确定性的统计框架。
注意
本节关于积分的关键点如下:
-
积分也被称为反导数,它们是导数的相反。
-
不定积分在曲线下方找到面积,而定积分在由点a和点b给定的区间内被限制。
-
微积分基本定理是导数和积分之间的桥梁。
-
积分在机器学习中用于建模不确定性,进行预测和估计期望值。
优化
几种机器和深度学习算法依赖于优化技术来减少误差函数。本节讨论了不同学习算法中的一个基本概念。
优化是在可能解的宇宙中找到最佳解的过程。优化就是找到函数的最高点和最低点。图 4-17 显示了以下公式的图形:

图 4-17。函数的图形
当 x 轴右侧的值减少直到达到一个点开始增加时,存在局部最小值。该点不一定是函数中的最低点,因此称为局部。在图 4-18 中,函数在点 A 处有一个局部最小值。
当 x 轴右侧的值增加直到达到一个点开始减少时,存在局部最大值。该点不一定是函数中的最高点。在图 4-18 中,函数在点 B 处有一个局部最大值。
当 x 轴右侧的值减少直到达到一个点开始增加时,存在全局最小值。该点必须是函数中的最低点,因此称为全局。在图 4-18 中,函数在点 C 处有一个全局最小值。
当 x 轴右侧的值增加直到达到一个点开始减少时,存在全局最大值。该点必须是函数中的最高点。在图 4-18 中,没有全局最大值,因为函数将无限地继续而不形成顶点。您可以清楚地看到函数如何加速向上。
在处理机器和深度学习模型时,目标是找到最小化所谓的损失函数(给出预测误差的函数)的模型参数(或输入)。如果损失函数是凸的,优化技术应该找到趋向于最小化损失函数的全局最小值的参数。此外,如果损失函数是非凸的,则收敛不能保证,优化可能只会导致接近局部最小值,这是目标的一部分,但这会留下全局最小值,这是最终目标。
但是这些最小值和最大值是如何找到的呢?让我们一步一步来看:
-
第一步是执行第一阶导数测试(简单地计算函数的导数)。然后,将函数设置为零并解出x将给出所谓的临界点。临界点是函数改变方向的点(值停止朝一个方向前进并开始朝另一个方向前进)。因此,这些点是极大值和极小值。
-
第二步是执行第二阶导数测试(简单地计算导数的导数)。然后,将函数设置为零并解出x将给出所谓的拐点。拐点给出函数凹向上和凹向下的地方。
换句话说,临界点是函数改变方向的地方,拐点是函数改变凹凸性的地方。图 4-19 显示了凹向上函数和凹向下函数之间的区别。

图 4-18。凹向上与凹向下函数
找到极值的步骤如下:
-
找到第一阶导数并将其设为零。
-
解第一阶导数以找到x。这些值称为临界点,它们代表函数改变方向的点。
-
将值代入公式中,要么低于要么高于临界点。如果第一阶导数的结果为正,则意味着在该点周围增加,如果为负,则意味着在该点周围减少。
-
找到第二阶导数并将其设为零。
-
解二阶导数以找到x。这些值,称为拐点,代表凹凸性从上到下变化的点,反之亦然。
-
将值插入公式中,这些值要么低于要么高于拐点。如果二阶导数的结果为正,意味着该点处存在最小值,如果为负,则意味着该点处存在最大值。
重要的是要理解,第一阶导数和二阶导数测试与临界点有关,而不是与拐点有关的二阶导数测试。以下示例找到函数的极值。
第一步是对第一阶导数进行求导,将其设为零,并解出x:
因此,在该值处存在一个临界点。现在,下一步是找到二阶导数:
接下来,必须将临界点插入到二阶导数公式中:
在临界点处,二阶导数为正。这意味着该点处存在局部最小值。
在接下来的章节中,您将看到更复杂的优化技术,如梯度下降和随机梯度下降,这在机器学习算法中非常常见。
注意
关于优化的本节的关键点如下:
-
优化是找到函数的极值的过程
-
临界点是函数改变方向的点(值停止朝一个方向前进并开始朝另一个方向前进)
-
拐点指出函数是凹向上还是凹向下的地方。
-
损失函数是衡量预测机器学习中预测误差的函数。为了提高模型的准确性,需要将其最小化。损失函数的优化可以通过讨论的方式或称为梯度的方式进行,这是本书讨论范围之外的技术。
总结
第 2、3 和 4 章介绍了您需要开始理解基本机器学习和深度学习模型的主要数值概念。我已经尽最大努力尽可能简化技术要求,我鼓励您至少阅读这三章两次,以便您学到的一切变得自然而然。
自然地,这样一个复杂的领域需要更深入的数学知识,但我相信通过本章中所见的概念,您可以开始在 Python 中发现和构建模型。毕竟,它们来自软件包和库的预构建,本章的目的是理解您正在处理的内容,不太可能使用过时的工具从头构建模型。
到目前为止,您应该已经对数据科学和数学要求有一定的了解,这将让您舒适地开始。
¹ 矩阵也可以包含符号和表达式,但为了简单起见,让我们坚持使用数字。
第五章:介绍技术分析
技术分析提供了许多类型的输入,可以用于您的深度学习模型。本章介绍了这一广阔领域,以便您具备在接下来的章节中创建基于技术的学习模型所需的知识。
技术分析依赖于对价格行动历史的视觉解释,以确定市场可能的总体方向。它依赖于过去是未来最好的预测者的想法。技术分析领域内有几种技术,尤其是以下几种:
图表分析
这是你将主观视觉解释技术应用于图表的地方。通常使用绘制支撑和阻力线以及回撤等方法来找到旋转点,以确定下一步的走势。
指标分析
这是你使用数学公式创建客观指标的地方,这些指标可以是趋势跟踪或逆势。已知的指标包括移动平均线和相对强弱指数(RSI),这两者在本章中都有更详细的讨论。
模式识别
这是你监视某些经常出现的配置并采取行动的地方。模式通常是不时出现并呈现某种理论或经验结果的事件。在金融领域,情况更为复杂,但已经证明某些模式随着时间的推移增加了价值,这可能部分归因于所谓的自我实现预言现象(一种初始期望导致其确认的过程)。
让我们快速浏览一下技术分析的历史,这样你就能更好地了解可以期待什么。技术分析依赖于三个原则:
历史总是在重复。
在趋势和区间中,您可能会看到集群。此外,某些配置大部分时间可能会产生类似的结果。
市场折现一切。
假设一切(所有基本、技术和定量信息)都包含在当前价格中。
市场波动。
由于不同的时间框架和需求,交易者以不同的频率买卖,因此形成趋势和波浪,而不是一条直线。
不幸的是,技术分析被零售交易社区过度炒作和滥用,这使得它在专业行业中的声誉略显不佳。每种分析方法都有其优势和劣势,有成功的基本、技术和定量投资者,但也有失败的投资者来自这三个领域。
基本分析依赖于经济和金融数据,以长期投资视角对特定证券或货币进行评估,而定量分析更加灵活,更常应用于短期数据。它使用数学和统计概念进行预测。
技术分析等假设之一是市场不是有效的,但这是什么意思?市场效率表明信息已经嵌入到当前价格中,价格和价值是相同的。当你购买资产时,你希望它是被低估的(在基本分析术语中)或被卖过头的(在技术分析术语中),这就是为什么你相信价格应该上涨以满足价值。因此,你假设价值大于价格。
市场效率否认了价格不等于价值的任何说法,因此建议任何 Alpha 交易不应导致高于平均水平的回报(Alpha 交易是进行投机操作以表现优于基准的行为,通常是指数或加权指标)。
市场效率假说是技术分析师的最大敌人,因为其原则之一是在弱式效率中,您无法从技术分析中获得超额回报。因此,技术分析在一开始就被否定,然后基本分析也受到打击。
可以合理地假设,将来市场将不得不变得高效,因为参与者数量和获取信息的便利性。然而,正如政治和异常事件所展示的,市场往往远非高效。
注
触发市场恐慌和非理性的政治事件的一个例子是俄罗斯入侵乌克兰。同样,一个异常经济事件的例子是央行意外加息。
图表分析
在了解图表分析是什么之前,您需要知道打开图表时看到的内容,更具体地说是蜡烛图。
假设市场开盘价为 100 美元。发生了一些交易活动。让我们也记录在每小时周期内打印的最高价(102 美元)和最低价(98 美元)。还要记录每小时的收盘价(101 美元)。请记住,这四个数据被称为开盘、最高、最低和收盘(OHLC)。它们代表创建蜡烛图所必需的四个基本价格。
蜡烛图非常简单直观。它们是沿着时间轴的盒状元素,包含 OHLC 数据。图 5-1 展示了关于蜡烛图如何工作的一切。

图 5-1。左边是一个看涨的蜡烛图;右边是一个看跌的蜡烛图
一个看涨蜡烛图的收盘价高于开盘价,而一个看跌蜡烛图的收盘价低于开盘价。
蜡烛图是分析金融时间序列的最著名方法之一。它们比简单的线图包含更多信息,比条形图提供更多的视觉可解释性。
注
线图是通过按时间顺序连接收盘价创建的。这是绘制资产的最简单方式。它在三种图表类型中包含的信息最少,因为它只显示收盘价。
图表分析是通过主观绘制来找到支撑和阻力线的任务。线,无论是水平的还是对角线的,是预测市场反应的关键:
-
支撑水平是市场应该反弹的水平,因为暗示着周围的需求应该高于供应。
-
阻力水平是市场应该退回的水平,因为暗示着周围的供应应该高于需求。
资产在时间轴上的方向可以分为三种:上升趋势,价格创造更高的高点;下降趋势,价格创造更低的低点;横向(或区间),价格在同一水平周围波动一段时间。
图 5-2 显示了 EURUSD 接近 0.9850 的支撑水平。一般来说,当价格接近支撑时,交易者开始考虑买入。这是因为他们预期应该会出现向上的反应,因为力量平衡应该更多地倾向于需求(正面)方,交易者愿意支付更高的价格,因为他们预期未来价格会更高(记住之前讨论过的价格与价值的论点)。这里的含义是大多数交易者看到的价格低于价值。

图 5-2。EURUSD 的蜡烛图显示支撑位于 0.9850 附近
图 5-3 显示了 EURUSD 接近 0.9960 的阻力水平。一般来说,当市场接近阻力时,交易者开始考虑做空市场。这是因为他们预期应该会出现向下的反应,因为力量平衡应该更多地倾向于供应方。

图 5-3. EURUSD 蜡烛图显示 0.9960 的阻力
横盘(横向)市场更有信心,水平支撑和阻力线将起作用。这是因为已经暗示了供需之间的一般平衡。因此,如果供应过剩,市场将迅速调整,因为需求应该足够上升以稳定价格。
图 5-4 显示了一个被困在两个水平线之间的横盘市场;这是 EURUSD 的情况。每当市场接近横盘市场的阻力线时,您应该更有信心地认为会发生下跌,而不是在上升市场中,每当它接近支撑时,您应该更有信心地认为会发生反弹,而不是在下跌市场中。
此外,图表分析也适用于趋势市场。这以上升和下降通道的形式出现。它们与水平水平具有相同的倾向,但有一个偏见(稍后讨论)。

图 5-4. EURUSD 蜡烛图显示 0.9850 的支撑和 0.9960 的阻力
图 5-5 显示了一个上升通道,其中支撑和阻力点随着时间的推移而上升,以反映源自稳定上升需求力量的多头压力。

图 5-5. AUDUSD 蜡烛图显示一个上升通道
看到这一点的交易员会预期每当市场接近上升通道的下部时会有多头反应,并且会预期每当市场接近通道的上部时会有空头反应。
这没有可靠的科学依据,因为没有什么规定市场必须平行移动,但自我实现的预言可能是为什么这样的通道被认为具有预测性质的原因。
图 5-6 显示了一个下降通道,其中支撑和阻力点随着时间的推移而下降,以反映源自稳定上升供应力量的空头压力。一般来说,空头通道往往更具侵略性,因为恐惧主导贪婪,卖方比买方更恐慌。

图 5-6. EURUSD 蜡烛图显示一个下降通道
我提到了处理上升和下降通道时的偏见。我将这种偏见称为隐形之手。原因如下:
“趋势是你的朋友。”这句话是由马丁·兹威格创造的,意思是在上升通道中,您需要更多地关注每当市场回归到支撑区域时进行买入。这是因为您希望多头压力的隐形之手增加您获胜交易的概率。同样,在下降通道的情况下,更多地关注每当市场达到上限时进行卖空。兹威格的格言的完整版本如下:“趋势是你的朋友,直到它弯曲结束。”这意味着在任何时候,市场可能改变其制度,与趋势的任何友谊都将终止。最终,图表分析是主观的,更依赖于交易员或分析师的经验。
值得一提的是,除了通过视觉估计绘制支撑和阻力水平之外,还有许多其他找到支撑和阻力水平的方法:
斐波那契回撤
这是你使用斐波那契比率来确定反应水平的地方。斐波那契回撤通常是在上升或下降趋势中计算的,这样你就知道市场如果触及这些水平将会反转。这种方法的问题在于它非常主观,就像任何其他技术一样,并不完美。优点在于它提供了许多有趣的水平。
枢轴点
使用枢轴点,您可以使用简单的数学公式找到水平。基于昨天的交易活动,您使用公式来预测今天的未来支撑和阻力水平。然后每当市场接近这些水平时,您尝试通过朝相反方向交易来淡化动作。
移动平均线
这些将在下一节中讨论。它们是动态的,跟随价格。您还可以使用它们来检测当前的市场制度。
提示
找到支撑和阻力水平的最佳方法是尽可能结合多种技术,以便您有一定的方法融合,从而增加对初始想法的信心。交易是一个数字游戏,尽可能多地将赔率堆叠在您这一边应该最终增加您的系统表现的机会。
指标分析
指标分析是第二常用的技术分析工具。它通常与图表一起使用,以确认您的初始想法。您可以将指标视为助手。它们可以分为两种类型:
趋势跟踪指标
用于检测和交易预期继续的趋势市场。因此,它们与移动的持续性有关。
反向指标
用于淡化移动²,最好在横向市场中使用³,因为它们通常标志着初始移动的结束。因此,它们与移动的预期逆转有关(因此也与移动的反持续性有关)。
接下来的章节介绍了技术分析的两个支柱:移动平均线(趋势跟踪)和相对强弱指数(反向)。
注意
指标很重要,因为您将在后续章节中的不同学习算法中将它们用作输入。
移动平均线
最著名的趋势跟踪叠加指标是移动平均线。它的简单性使其无疑成为最受欢迎的工具之一。移动平均线有助于确认和跟随趋势。您还可以使用它们找到支撑和阻力水平、止损和目标,以及了解潜在趋势。
有许多类型的移动平均线,但最常见的是简单移动平均线,其中您取收盘价的滚动平均值,如下式所示:
图 5-7 显示了应用在 USDCAD 上的 30 小时简单移动平均线。术语30 小时意味着在小时柱的情况下,我计算最新 30 个周期的移动平均值。

图 5-7. USDCAD 的蜡烛图表,带有 30 小时简单移动平均线
移动平均线的经验法则包括以下内容:
-
每当市场在其移动平均线之上时,牛市动量正在进行中,您最好寻找做多机会。
-
每当市场在其移动平均线之下时,熊市动量正在进行中,您最好寻找做空机会。
-
每当市场穿过或低于其移动平均线时,您可以说动量已经改变,市场可能正在进入新的制度(趋势)。
您还可以结合移动平均线,以便它们发出信号。例如,每当短期移动平均线穿过长期移动平均线时,就会发生牛市交叉,并且市场可能会继续上涨。这也被称为金叉。
相反,每当短期移动平均线穿过长期移动平均线时,就会发生熊市交叉,并且市场可能会继续下跌。这也被称为死亡交叉。
图 5-8 显示了 USDCAD 的 10 小时(接近市场价格)和 30 小时移动平均线(远离市场价格)。请注意,在开始时,出现了一个金叉,标志着牛市趋势的开始。

图 5-8. USDCAD 的蜡烛图表,带有 30 小时和 10 小时简单移动平均线
相对强弱指数
现在让我们看看反向指标。由 J. Welles Wilder Jr.首次引入,相对强弱指数(RSI)是最受欢迎和多功能的有界指标之一。它主要用作反向指标,极端值表明可以利用的反应。使用以下步骤计算默认的 14 周期 RSI:
-
计算前一次收盘价格的变化。
-
将正净变化与负净变化分开。
-
计算正净变化的平滑移动平均和负净变化的绝对值的平滑移动平均。
-
将正净变化除以负净变化的绝对值。将这个计算称为相对强度(RS)。
-
对每个时间步骤应用这个归一化公式来得到 RSI:
注意
平滑移动平均线是由 RSI 的创造者开发的一种特殊类型的移动平均线。它比简单移动平均线更平滑和稳定。
通常,RSI 默认使用 14 个回望期,尽管每个交易员可能对此有自己的偏好。以下是如何使用这个指标:
-
每当 RSI 显示 30 或更低的读数时,市场被认为是超卖的,可能会发生向上的修正。
-
每当 RSI 显示 70 或更高的读数时,市场被认为是超买的,可能会发生向下的修正。
-
每当 RSI 超过或突破 50 水平时,可能会出现新的趋势,但这通常是一个薄弱的假设,更多是理论性的而不是实际性的。
图 5-9 显示了 EURUSD 与其 14 周期 RSI 的对比。指标应该用来确认长期或短期偏向,并且在时机和分析当前市场状态方面非常有帮助。

图 5-9。在顶部面板显示每小时 EURUSD 值,底部面板显示 14 周期 RSI
总结一下,指标可以用许多方式计算。最常用的两种是移动平均线和 RSI。
模式识别
模式是一种重复出现的配置,显示了随后移动的具体预测。模式可以分为以下类型:
经典价格模式
这些是已知的技术反转价格模式,非常主观,由于难以在不考虑主观条件的情况下进行回测,因此可能不可靠。然而,它们仍然被许多交易员和分析师使用。
时间模式
基于时机和价格的组合,这些模式不太为人所知,但在正确使用时可能会很强大和具有预测性。
蜡烛图模式
这是使用 OHLC 数据来预测市场未来反应的地方。蜡烛图是可视化图表的最佳方式之一,因为它们包含许多可能预示反转或确认移动的模式。
经典价格模式指的是理论配置,如双顶和矩形。它们通常是反转或持续模式:
持续价格模式
这些是确认总体持续移动的配置。例如矩形和三角形。
反转价格模式
这些是淡化总体持续移动的配置。例如头肩和双底。
老派的图表分析师熟悉双顶和双底,它们预示着反转并给出了这种反转的潜力。尽管它们很简单,但它们是主观的,有些不像其他的那么明显。
这阻碍了了解它们是否具有价值的能力。图 5-10 显示了一个双顶的图示,当验证模式后给出了一个看跌的偏向,通常是打破连接两个顶部之间低点的线。这条线被称为颈线。

图 5-10。双顶图示
注意双顶中的这三个重要元素:
颈线
这是连接两个峰值之间的最低点和图案的开始/结束的线。它用于确定回撤水平。
回撤
打破颈线后,市场应该朝着颈线做出绝望的尝试,但由于卖方利用这一水平重新进场继续做空,未能继续上涨。因此,回撤水平是验证双顶后的理论最佳卖出点。
潜力
这是双顶的目标。它被测量为图案顶部和下降的颈线之间的中点,并从相同的颈线点开始。
双顶或双底可以有任何大小,但最好是对大多数市场参与者可见,以便其影响更大。从心理上讲,该模式的解释是,随着第二个顶部或底部,市场未能将价格推高超过第一个峰值,因此显示出弱势,这可能会被卖方利用。
还有其他更客观的模式;即它们具有明确的检测和启动规则。所有这些都基于明确的客观条件,不受分析师自行决定的影响。这有助于它们的回测和评估。
在结束本章之前,我想指出技术分析的一些误解和最佳实践,以便您正确开始。
技术分析的常见陷阱
技术分析可能被滥用,这不幸地引发了关于其效用和与基本分析相对位置的永恒辩论。重要的是设定正确的期望,并保持在逻辑思维的范围内。本节讨论了技术分析的已知陷阱,您必须确保避免这些陷阱,以最大限度地提高您在金融丛林中的生存率。
想要快速致富
这个陷阱主要是心理上的,因为它涉及缺乏纪律和糟糕的管理。为了尽快赚钱以打动社会,推动交易员在交易和与交易相关的活动中做出情绪化和糟糕的决策。
这也与需要赚钱的事实有关,您可能会不断更改交易计划,因为您相信新计划是通往财富的更快途径。
当您对自己没有足够的信心,并认为其他人比您更擅长赚钱时,您更有可能跟随他们,尤其是因为他们提供的信息非常丰富。除了您之外,没有人能改变您的未来。
强迫模式
一种常见的心理缺陷,称为确认偏见,阻止交易员看到与他们已经建立的观点相矛盾的信号。
有时,您对某些市场有初步看法,因此开始寻找任何与该看法一致的东西,这也可能导致即使没有有效性也会强行出现模式。
警告
您必须始终保持中立的分析态度,并谨慎对待各个元素,同时保持最大的客观性。当然,这说起来容易做起来难,绝对中立的最佳替代方案是算法方法,这会以人类智力因素为代价。
事后偏见,梦想粉碎者
技术分析在过去看起来不错。一切都显而易见且易于预测,即使使用非常基本的策略;然而,当您应用后者时,您会发现由于严酷的现实,您的大脑被编程为欺骗您相信过去是完全可预测的。
这也是为什么回测结果几乎总是优于前向测试的原因。当你看过去的模式并相信它们应该很容易被发现时,你展现了“事后诸葛亮”的偏见。为了解决这个问题,你必须使用无偏算法进行回测。只有这样,你才能确定模式是否有价值。许多零售交易者陷入了这样一个陷阱,他们对过去进行了一般和简单的审视,以确定他们的策略的有效性,结果却表现不佳。
假设过去的事件会有相同的未来结果
你听过这句谚语“历史不会重复,但往往会押韵。”这句话是理解市场运作方式的关键。当你应用技术分析时,不要期望从过去的信号和模式中得到确切的结果。相反,你必须将它们作为指导和概率元素,表明市场可能会有类似的反应,可以与过去的反应相关联。
交易是一个数字游戏,你必须让胜算倾向于你这边。当人类面对类似事件时,有时会表现出相同的行为。然而,随着不同的参与者不断进入和离开买卖活动,他们的动机也在不断变化,你可以肯定,在遇到类似过去的模式时,市场的未来反应不会完全相同,尽管它可能与过去“押韵”,意味着它可能在平均水平上与过去有相关的反应。
说到这一点,不要期望每次看到明显模式时都能完美地把握市场的时机。
把事情搞得比必要的更复杂
另一句谚语是“一切都应该尽可能简单,但不要过于简单。”这是对你应该如何进行研究和交易的完美描述。
金融市场是高度复杂的、半随机的环境,需要的不仅仅是简单的策略,但策略也不能太复杂,以至于陷入“过拟合”的陷阱,这是一个常见问题,交易者完美地预测过去,并假设未来会完全相同,从而导致过去有巨大的纸面收益,但未来却有巨大的实际损失。
总结
技术分析提供了一系列工具来对市场进行数学、图形或甚至心理(通过模式)分析。这一章标志着在开始本书的真正目的之前进行热身练习的结束,即用于交易和预测目的的机器和深度学习应用。这一章的学习成果应该是对技术分析是什么以及其局限性的深刻理解。同样,你还应该对我提出的两个主要技术指标有深刻的理解:移动平均线和相对强弱指标,因为它们构成了即将出现的模型的关键特征。
¹ 这假设了一个长期显示确定性特征的非随机概率。
² 反向操作是一种交易技术,你在与正在进行的趋势相反的方向进行交易,希望能够把握其结束的时机。
³ 横盘市场通常处于均衡状态,没有特定的趋势描述它们。它们往往在彼此接近的高点和低点之间摇摆。
⁴ 参见 J.韦尔斯·怀尔德(1978)的《技术交易系统中的新概念》,由趋势研究出版。
第六章:数据科学的 Python 入门
在深入机器和深度学习领域之前还有一个停留。对于有经验的 Python 开发人员来说,本章是可选的,但对于没有扎实编程背景的任何人来说都是至关重要的。了解算法背后的直觉是一个很大的优势,但如果未能正确实现它们,这将无法让您走得更远。毕竟,这些算法需要代码来工作,而不是手动操作。确保了解基本语法以及如何操作数据和转换数据。
由于本书不是 Python 编程的 A-Z 指南,本章只是简要介绍了一些基本知识和一些技术,这些应该有助于您顺利地浏览后续章节。
下载 Python
编码被定义为一组旨在由计算机执行的指令。通常需要特定的语法,以便计算机应用这组指令而不出现错误。有许多编码语言,分为两大类:
低级编码语言
这些通常是用于操作系统和固件的机器语言。它们很难阅读。这些语言对硬件有相当大的控制。低级语言的一个例子是汇编语言(及其各种类型)。
高级编码语言
这些是用户友好的语言(具有高度抽象)。它们通常用于编写程序和软件。高级语言的一个例子是 Python 和 Julia。
本书使用的编程语言是Python,这是一种流行且多功能的语言,具有许多优势,并在研究和专业社区中被广泛采用。正如您从章节名称中看到的那样,您将看到 Python 的简介以及开始构建自己脚本所需的必要工具。但在此之前,让我们看看如何实际下载 Python。
Python 解释器是一种用于编写和执行使用 Python 语法编写的代码的软件。我使用一个名为Spyder的软件。有些人可能更熟悉其他解释器,如Jupyter和PyCharm,但过程是相同的。您可以从官方网站下载Spyder,或者更好地,将其作为一个更大的软件包的一部分下载,该软件包称为Anaconda,它简化了安装并提供了更多工具。请注意,这是一个开源且免费使用的软件。
Spyder的界面分为三个窗口,如图 6-1 所示。左侧的窗口用于编写稍后执行的代码(告诉算法运行并应用代码)。通常,您会在该区域看到多行代码。
右上角的窗口是变量资源管理器。每次存储(定义)一个变量,您都可以在那里看到它。右下角的窗口是控制台,显示代码的结果,无论是错误还是输出。

图 6-1。Spyder 的界面
Python 文件的扩展名为name.py,它们允许您保存代码并在以后引用它。您还可以打开多个代码文件并在它们之间导航。本章的大纲如下:
-
了解 Python 语言以及如何编写无错误的代码。
-
了解如何使用控制流以及在时间序列分析中的重要性。
-
了解库和函数是什么以及它们在促进编码中的作用。
-
了解如何处理错误及其不同类型。
-
了解如何使用数据操作库,如
numpy和pandas。 -
最后,看看如何将历史财务时间序列数据导入 Python,以便使用您在之前章节和即将到来的章节中看到的适当工具进行分析。
基本操作和语法
语法是编写无错误代码的正确方式,它是编写功能代码所需的语句结构。当您与计算机交流时,必须确保它理解您,因此,对语法有扎实的理解很重要。
理解代码伴随着一个有用的操作,称为注释。注释是用于解释紧随其后的可执行代码的非可执行代码。这是为了让其他程序员理解代码。注释前面有一个井号#:
# This is a comment. Comments are ignored by the interpreter
# Comments explain the code or give more details about its use
# Comments are written in one line, otherwise, you have to re-write '#'
注意
确保您理解注释是不可执行的。这意味着当您运行(执行)代码时,解释器将忽略它们,不会返回错误。
有时您需要为代码编写文档,这可能需要多行代码(甚至在某些情况下是段落)。在每一行写井号符号可能会很繁琐和混乱。这就是为什么有一种方法可以编写长注释。为此,您必须在每个末尾之间写三个引号:
'''
Python was created in the late 1980s by Guido van Rossum.
The name "Python" was inspired by the comedy group Monty Python.
'''
值得注意的是,三引号被认为是文档字符串,而不是真正的注释(根据官方 Python 文档)。
让我们讨论变量和常量。常量是一个固定值,不会改变,而变量在事件发生时会取不同的值。常量可以是数字 6,而变量可以是字母x,根据一组条件或状态取任何数字。使用'='运算符定义变量:
# Defining a variable
x = 10
y = 5
# Writing a constant
6
运行上述代码将在变量资源管理器中存储变量x和y及其相应的值。同时,代码的输出将是 6。变量是区分大小写的,因此:
# Declaring my_variable
my_variable = 1
# Declaring My_variable
My_variable = 2
# The variable my_variable is different from My_variable
变量声明不能以数字开头,但可以在名称中间或结尾包含一个数字:
# Returns a SyntaxError
1x = 5
# Valid declaration
x1 = 5
# Valid declaration
x1x = 5
变量也可以包含下划线,但不能包含其他内容:
# Returns a SyntaxError
x-y = 5
# Valid declaration
x_y = 5
强烈建议变量简短而直接。例如,考虑创建一个保存某个移动平均值的回溯期的变量(在第五章中介绍的概念):
# Recommended name
ma_lookback = 10
# Not recommended name
the_lookback_on_that_moving_average = 10
有几种不同特征的数据类型:
数值数据类型
这是最简单的数据类型。它完全由数字组成。数值数据类型进一步分为整数、浮点数和复数。整数是简单的整数(正数或负数)。整数的一个例子是 6 和-19。浮点数比整数更精确,因为它们包含逗号后的值。浮点数的一个例子是 2.7 和-8.09。复数包括虚数,比其他两种类型不太相关。
字符串
正如您之前看到的注释和文档字符串一样,可以在代码旁边写文本而不会干扰执行过程。字符串是表示字符序列的文本结构。字符串可以是函数的输入和参数,而不仅仅是注释。
布尔值
这是一个二进制(真或假)数据类型,用于评估给定表达式或条件的真值。例如,您可以使用布尔值来评估市场价格是否高于或低于 100 周期移动平均线。
数据收集
这些是包含多个具有不同和独特用途的数据的序列。数组是相同类型元素的序列(主要是数字)。数组将在本书中经常使用(与本章讨论的名为numpy的 Python 库一起使用)。数据框是结构化数据的二维表,也经常在本书中使用(与本章讨论的名为pandas的 Python 库一起使用)。集合是无序元素的序列。列表是可以是不同数据类型的有序元素集合。元组是有序的、不可变的元素集合,可以是不同数据类型。它用于存储一系列固定值。范围是一个内置的 Python 函数,返回一系列数字。范围函数在循环中经常使用。字典表示一组键值对组合。
以下代码片段显示了一些关于数值数据类型的示例:
# Creating a variable that holds an integer
my_integer = 1
# Creating a variable that holds a float number
my_float_number = 1.2
# Using the built-in Python function type() to verify the variables
type(my_integer)
type(my_float_number)
输出应该如下所示(请记住井号符号后的任何内容都是注释,不会被执行):
int # The output of type(my_integer)
float # The output of type(my_float_number)
字符串只是文本。解释字符串的最著名例子是"Hello World"短语,如下面的代码片段所解释的:
# Outputting the phrase "Hello World"
print('Hello World')
输出应该如下所示:
Hello World
字符串也可以作为函数中的参数使用,这两个概念您将在本章后面看到。
如前面列表中提到的,布尔值要么为真,要么为假。以下代码片段显示了使用它们的示例:
# Make a statement that the type of my_integer is integer
type(my_integer) is int
# Make a statement that the type of my_float_number is float
type(my_float_number) is float
# Make a statement that the type of my_integer is float
type(my_integer) is float
'''
Intuitively, the two first statements will return True as they are
indeed true. The third statement is False as the variable my_integer
is an integer and not a float number
'''
前面代码的输出如下:
True
True
False
让我们讨论运算符的工作原理。您实际上已经看到了一个运算符的示例,即用于定义变量的赋值运算符'='。运算符在变量、常量甚至数据结构之间执行特殊的数学和其他任务。有不同类型的运算符。让我们从算术运算符开始,如下面的代码片段所示:
# Arithmetic operator - Addition
1 + 1 # The line outputs 2
# Arithmetic operator - Subtraction
1 - 1 # The line outputs 0
# Arithmetic operator - Multiplication
2 * 2 # The line outputs 4
# Arithmetic operator - Division
4 / 2 # The line outputs 2.0 as a float number
# Arithmetic operator - Exponents
2 ** 4 # The line outputs 16
比较运算符是下一种运算符,用于比较不同的元素。它们主要用于控制流事件,如本章的下一部分所解释的那样。以下代码片段显示了一些比较运算符:
# Comparison operator - Equality
2 == 2 # The line outputs True
# Comparison operator - Non equality
2 != 3 # The line outputs True
# Comparison operator - Greater than
2 > 3 # The line outputs False
# Comparison operator - Greater than or equal to
2 >= 2 # The line outputs True
# Comparison operator - Less than
2 < 3 # The line outputs True
# Comparison operator - Less than or equal to
2 <= 2 # The line outputs True
逻辑运算符结合两个或多个稍后进行评估的条件。有三个逻辑运算符:and、or和not。以下代码块显示了逻辑运算符的示例:
# Logical operator - and
2 and 1 < 4 # The line outputs True
2 and 5 < 4 # The line outputs False
# Logical operator - or
2 or 5 < 4 # The line outputs 2 which is the integer less than 4
数据收集结构(数组和数据框)将在后面的部分中讨论,因为它们由于其复杂性和独特工具而需要深入介绍。让我们用一个代码来结束这一部分,总结到目前为止讨论的内容:
# Declaring two variables x and y and assigning them values
x = 10
y = 2.5
# Checking the types of the variables
type(x) # Returns int
type(y) # Returns float
# Taking x to the power of y and storing it in a variable z
z = x ** y # Returns 316.22
# Checking if the result is greater than or equal to 100
z >= 100 # Returns True as 316.22 >= 100
控制流
条件语句构成了所谓控制流的第一部分(第二部分是循环)。条件语句是今天人工智能的祖先,因为它们只在满足某些条件时执行代码。
条件语句使用if、elif和else进行管理。以以下代码片段为例来澄清事情:
# Declaring the variables
a = 9
b = 2
# First condition (specific)
if a > b:
print('a is greater than b')
# Second condition (specific)
elif a < b:
print('a is lower than b')
# Third condition (general)
else:
print('a is equal to b')
因此,条件语句以if开头,然后对于每个新的唯一和特定条件,使用elif,直到有意义使用概率宇宙的其余部分作为独立的条件,这是由else语句使用的。请注意,else语句不需要条件,因为它存在以覆盖未覆盖的宇宙的其余部分。
循环用于重复执行代码块,直到满足预定义条件。循环在时间序列中广泛使用,用于计算指标、验证状态和回测交易策略。
循环使用for(用于迭代有限和定义的序列或一系列元素)和while(用于在满足条件之前继续迭代)语句进行管理。以打印值{1, 2, 3, 4}的循环为例:
# Using a for loop
for i in range(1, 5):
print(i)
# Using a while loop
i = 1
while i < 5:
print(i)
i = i + 1
当被翻译时,for循环简单地表示对于每个称为i(或者根据编码者而定的其他字母)的元素,在从 1 开始到 5 结束的范围内(不包括 5),在每次循环中打印i的值(因此,在第一个循环中,i的值等于 1,在第二个循环中,它等于 2)。
当被翻译时,while循环表示从i = 1的值开始,循环时,打印其值,然后在完成第一个循环之前将其加 1。当i变大于 4 时结束循环。
注意
理论上,while循环是无限的,直到另有说明为止。
值得注意的是i = i + 1也可以表示为i += 1。算法的目标是能够以客观的方式递归地应用许多操作,这使得循环特别有用,尤其是与条件语句结合时。让我们以一个金融时间序列的例子来说明:
-
创建一个值范围来模拟假设的每日收盘价。
-
在创建条件时循环遍历数据范围,如果价格从上一期上涨,则打印 1。同样,如果价格从上一期下跌,则打印-1。最后,如果价格与上一期没有变化,则打印 0。
这可以在以下代码块中完成:
# Creating the time series
time_series = [1, 3, 5, 2, 4, 1, 6, 4, 2, 4, 4, 4]
for i in range(len(time_series)):
# The condition where the current price rose
if time_series[i] > time_series[i - 1]:
print(1)
# The condition where the current price fell
elif time_series[i] < time_series[i - 1]:
print(-1)
# The condition where the current price hasn't changed
else:
print(0)
该代码定义了一个值列表(在这种情况下,一个名为time_series的时间序列),然后使用len()函数循环遍历其长度以应用条件。请注意,在每次循环中,当前时间步被称为i,因此上一个时间步为i - 1。
库和函数
在 Python 中,库是一组预先编写的代码,提供功能以使应用程序的创建更容易。模块是单独的 Python 文件,具有可重复使用的代码和数据,可以被导入和在其他 Python 代码中使用,通常在库中找到。因此,模块是一个包含函数和其他类型代码的单个 Python 文件,可以被其他 Python 程序使用和导入。通过使用模块将相似的代码分成不同的文件,通常更容易管理和维护大型代码库。
编码是简化任务并使其更清晰的过程。当你有一个重复的任务,比如计算时间序列的移动平均值时,你可以使用一个函数,这样你就不必每次想使用它时都重新编写移动平均代码。相反,你定义一个带有原始代码的函数,然后在需要计算移动平均值时调用它。但是什么是函数?它是一个可重复使用的代码块,当被调用时执行特定任务。它需要被定义一次。
多个函数形成一个模块,多个模块形成一个库。一个库通常是以主题为导向的。例如,在这本书中,将使用sklearn库与机器学习模型。类似地,数据操作和导入是使用numpy和pandas完成的,这两个库在本章的后面部分讨论。绘图和图表是使用matplotlib库完成的。
在使用之前,必须先将库导入到 Python 解释器中。这样做的语法如下:
# The import statement must be followed by the name of the library
import numpy
# Optionally, you can give the library a shortcut for easier references
import numpy as np
有时,你需要从库中导入一个函数或模块。为此,你不需要导入整个库:
# Importing one function from a library
from math import sqrt
因此,已经确定math是一个 Python 库,其中包含许多数学函数,即sqrt函数,用于找到给定数字的平方根。让我们看看如何定义一个函数。函数使用def后跟函数的名称和任何可选参数来定义。考虑以下示例,创建一个对任意两个给定变量求和的函数:
# Defining the function sum_operation and giving it two arguments
def sum_operation(first_variable, second_variable):
# Outputing the sum of the two variables
print(first_variable + second_variable)
# Calling the function with 1 and 3 as arguments
sum_operation(1, 3) # The output of this line is 4
注意
调用一个函数意味着执行它应该做的事情。换句话说,调用一个函数就是使用它。函数的时间线是被定义然后被调用。
让我们看看如何从库中导入函数并使用它的函数:
# Importing the library
import math
# Using the natural logarithm function
math.log(10)
# Using the exponential function (e)
math.exp(3)
# Using the factorial function
math.factorial(50)
作为一则旁注,阶乘操作是一种数学操作,用于计算从 1 到某个数字(在math.factorial()中请求的参数)的所有正整数的乘积。
库可能不像 1 加 1 那么简单。有时,外部库在能够导入 Python 解释器之前需要先安装。安装可以通过提示符使用以下语法完成:
pip install library_name
让我们回到第三章讨论过的 MIC。在 MIC 的以下已经看到的代码之前:
# Importing the library
from minepy import MINE
# Calculating the MIC
mine = MINE(alpha = 0.6, c = 15)
mine.compute_score(sine,cosine)
MIC = mine.mic()
print('Correlation | MIC: ', round(MIC, 3))
直接导入库可能会导致错误,因为它尚未通过 pip 安装。因此,您必须首先使用提示符上的以下语法进行安装(而不是 Python 解释器):
pip install minepy
重要的是阅读随库提供的文档,以便正确使用它们。文档有助于了解函数的目的以及应该放入哪些参数。此外,它告诉您函数可以接受什么类型的参数(例如,字符串或数字)。现在让我们回到函数(注意它们是如何交织在一起的,讨论一个可能导致讨论另一个)。
函数可以有一个return语句,允许将结果存储在一个变量中,以便在代码的其他部分中使用。让我们举两个简单的例子,然后逐步讨论它们:
# Defining a function to sum two variables and return the result
def sum_operation(first_variable, second_variable):
# The summing operation is stored in a variable called final_sum
final_sum = first_variable + second_variable
# The result is returned
return final_sum
# Create a new variable that holds the result of the function
summed_value = sum_operation(1, 2)
# Use the new variable in a new mathematical operation and store the result
double_summed_value = summed_value * 2
前面的代码定义了sum_operation函数,带有两个参数,然后将操作存储在一个名为final_sum的变量中,然后返回它,以便可以将其存储在外部。然后,定义一个名为summed_value的新变量,作为函数的输出。最后,另一个名为double_summed_value的变量被创建,其结果是summed_value乘以 2。这是一个关于如何在外部操作中使用函数结果作为变量的示例。现在,让我们考虑另一个示例(同时记住先前定义的sum_operation函数):
# Defining a function to square the result gotten from the sum_operation function
def square_summed_value(first_variable, second_variable):
# Calling the nested sum_operation function and storing its result
final_sum = sum_operation(first_variable, second_variable)
# Creating a variable that stores the square of final_sum
squared_sum = final_sum ** 2
# The result is returned
return squared_sum
# Create a new variable that holds the result of the function
squared_summed_value = square_summed_value(1, 2)
最新的代码片段定义了一个名为square_summed_value的函数,它接受两个参数。此外,它在内部使用了一个嵌套函数,这种情况下是sum_operation。嵌套函数的结果再次存储在一个名为final_sum的变量中,该变量用作查找squared_sum variable的输入。该变量被找到为final_sum的平方。这是一个关于如何在函数内部创建其他函数的示例(换句话说,嵌套函数)。
让我们以 Python 和机器学习中常见的库结束本节(除了numpy和pandas):
matplotlib # For plotting and visualizing data
sklearn # For machine learning models
scipy # For scientific computing and optimization
keras # For neural networks
math # For using mathematical tools such as square roots
random # For generating random variables
requests # For making HTTP requests used in web scraping
异常处理和错误
经常会因为代码在执行过程中出现问题而遇到错误。换句话说,当代码执行时,解释器发现阻止其继续进行的障碍时,就会发生错误。最基本的错误是SyntaxError,当有拼写错误或缺少元素使代码无法理解时会发生:
# Will not output a SyntaxError if executed
my_range = range(1, 10)
# Will output a SyntaxError is executed
my_range = range(1, 10
从前面的代码中可以看到,第二行代码末尾缺少一个括号,这个错误解释器无法理解。这种类型的错误可能是最常见的。另一个常见的错误是NameError,当在执行包含它的代码之前未定义变量时会发生。考虑以下示例:
x + y
前面的代码会给出一个NameError,因为解释器不知道x和y的值,因为它们没有被定义。
ModuleNotFoundError发生在解释器无法找到您尝试导入的库或模块时。这通常发生在它安装在错误的目录或未正确安装时。解决此问题的常见方法包括:
-
验证模块的名称是否已正确书写。
-
验证模块是否已正确通过 pip 安装。
-
验证模块是否安装在正确的位置。
另一种常见错误类型是TypeError,当你在不兼容的元素上应用某种操作时会发生,比如将整数与字符串相加。以下操作会引发TypeError:
# Defining variable x
x = 1
# Defining variable y
y = 'Hello
# Summing the two variables which will raise a TypeError
x + y
在时间序列分析中,你可能会遇到这四种错误:
-
IndexError: 当涉及到当前数组或数据框范围之外的索引时,会引发此错误。想象一下有一个包含 300 个值(行)的数组。如果你想循环遍历它们,并在每次循环时将数字 1 输入到下一个单元格(时间步长+1),你很可能会遇到IndexError,因为在最后一个循环中,没有下一个单元格,解释器会引发此错误。
-
ValueError: 当你尝试使用无效参数调用函数时会引发此错误。例如,尝试在调用函数时将整数元素传递为字符串。
-
KeyError: 当尝试访问数据框中不存在的元素时会发生此错误。例如,如果数据框中有三列,而你引用了一个不存在的列(可能是由于语法问题),你很可能会遇到KeyError。
-
ZeroDivisionError: 这个错误很直观,当尝试将一个数字除以零时会发生。
你可能会遇到其他类型的错误。重要的是要理解它们指的是什么,这样你才能够修复它们。
另一方面,Exceptions可能对代码并不致命,因为它们只是显示一个警告,但不一定终止代码。因此,异常发生在执行过程中(与错误不同,错误是因为解释器无法执行)。
注意
理解错误将有助于你修复它并使代码重新运行。
为了忽略某些异常,使用try和except关键字。当你确信处理异常不会改变代码的输出时,这是很有用的。让我们举一个例子,创建一个函数,将时间序列的第一列除以第二列的下一个值。第一步是将时间序列定义为数据框或数组(或任何其他数据集合结构):
# Importing the required library to create an array
import numpy as np
# Creating a two-column list with 8 rows
my_time_series = [(1, 3),
(1, 4),
(1, 4),
(1, 6),
(1, 4),
(0, 2),
(1, 1),
(0, 6)]
# Transforming the list into an array
my_time_series = np.array(my_time_series)
现在,让我们编写一个除以第二列中的下一个值的除法函数,该函数将接受第一列中的任何值:
# Defining the function
def division(first_column, second_column):
# Looping through the length of the created array
for i in range(len(my_time_series)):
# Division operation and storing it in the variable x
x = my_time_series[i, first_column] / my_time_series[i + 1, second_column]
# Outputting the result
print(x)
# Calling the function
division(0, 1)
运行前两个代码块将会产生IndexError,因为在最后一个循环中,函数找不到第二列的下一个值,因为它根本不存在:
IndexError: index 8 is out of bounds for axis 0 with size 8
通过try和except来修复这个问题将忽略导致问题的最后一个计算,并返回预期的结果:
# Defining the function
def division(first_column, second_column):
# Looping through the length of the created array
for i in range(len(my_time_series)):
# First part of the exception handling
try:
# Division operation and storing it in the variable x
x = my_time_series[i, first_column] / my_time_series[i + 1, second_column]
# Outputting the result
print(x)
# Exception handling of a specific error
except IndexError:
# Ignoring (passing) the error
pass
# Calling the function
division(0, 1)
输出如下:
0.25
0.25
0.16666666666666666
0.25
0.5
0.0
0.16666666666666666
本节讨论了错误和异常以及如何处理这些问题。在接下来的章节中,由于其简单性和有效性,这些问题将使用try和except块来处理。
Numpy 和 Pandas 中的数据结构
numpy 和 pandas 这两个词可能对你来说很熟悉,因为我在前几章的大部分代码片段中都用到了它们。此外,你现在了解了什么是库,因此你知道这两个库是用来在 Python 中操作、处理和导入数据的首选库。本节展示了这两者之间的区别以及它们的关键功能,这绝对是你数据分析的重要补充。但首先,让我们定义这两个库:
numpy
NumPy(Numerical Python 的缩写)是一个允许处理多维数组和矩阵的 Python 库。NumPy 提供了一个强大的接口,用于对数组和矩阵执行各种操作。
pandas
Pandas(Panel Data 的缩写)是一个允许处理数据框(一种表格数据类型)的 Python 库。Pandas 提供了两种主要的数据结构:series 和 data frames。series 是一个类似于一维数组的对象,可以保存任何数据类型。data frame 是一个类似于二维表格的结构,由行和列组成(类似于电子表格)。
这两个库在分析时间序列数据方面非常有用。数组仅包含数值类型数据,因此与数据框不同,它们实际上不包含日期类型数据。这可能是使用pandas相对于numpy的优势之一,但两者都有各自的优势和相对弱点。最终,这是一个选择的问题。本书将优先使用 numpy,因为其简单性以及下一章中看到的机器学习模型使用sklearn库,该库适用于数组。这不应阻止您在准备应用模型之前使用数据框。
注意
在numpy和pandas之间切换需要转换时间序列类型。这是一个相对容易的任务,但有时可能会导致某些类型数据的丢失(例如日期数据)。
让我们在开始之前导入这两个库,看看它们的一些潜力:
import numpy as np
import pandas as pd
以下代码创建了两个具有两列和三行的时间序列。第一个时间序列称为my_data_frame,使用pandas的pd.DataFrame函数创建。第二个时间序列称为my_array,使用numpy的np.array函数创建:
# Creating a data frame
my_data_frame = pd.DataFrame({'first_column' : [1, 2, 3],
'second_column' : [4, 5, 6]})
# Creating an array
my_array = np.array([[1, 4], [2, 5], [3, 6]])
如图 6-2 所示,数据框具有真实的索引并且可以有列名。数组是纯数值的,不允许这样做:
图 6-2。左侧是数据框(pandas),右侧是数组(numpy)
在两种数据类型之间切换是非常直观的,因为您将使用前一个代码块中使用的相同两个函数:
# To transform my_data_frame into my_new_array
my_new_array = np.array(my_data_frame)
# To transform my_array into my_new_data_frame
my_new_data_frame = pd.DataFrame(my_array)
注意
您可以注意到my_new_data_frame没有列名。
现在让我们看一些在处理模型时会派上用场的有用函数。切片、连接和其他工具是您必须掌握的东西,以便顺利地在数据分析部分中导航。考虑以下数组:
first_array = np.array([ 1, 2, 3, 5, 8, 13])
second_array = np.array([21, 34, 55, 89, 144, 233])
连接是将两个数据集通过行(axis = 0)或列(axis = 1)融合在一起的行为。让我们都做一遍:
# Reshaping the arrays so they become compatible in multidimensional manipulation
first_array = np.reshape(first_array, (-1, 1))
second_array = np.reshape(second_array, (-1, 1))
# Concatenating both arrays by columns
combined_array = np.concatenate((first_array, second_array), axis = 1)
# Concatenating both arrays by rows
combined_array = np.concatenate((first_array, second_array), axis = 0)
现在,让我们为数据框做同样的事情。考虑以下数据框:
first_data_frame = pd.DataFrame({'first_column' : [ 1, 2, 3],
'second_column' : [ 4, 5, 6]})
second_data_frame = pd.DataFrame({'first_column' : [ 7, 8, 9],
'second_column' : [10, 11, 12]})
当您想要将数据合并到一个结构中时,连接是很有用的。这是如何在数据框中完成的(注意它只是一种语法和函数来源的变化):
# Concatenating both data frames by columns
combined_data_frame = pd.concat([first_data_frame, second_data_frame], axis = 1)
# Concatenating both data frames by rows
combined_data_frame = pd.concat([first_data_frame, second_data_frame], axis = 0)
请记住,对于时间序列,行(水平单元格)代表一个时间步长(例如,每小时),其中包含所有数据,而列代表不同类型的数据(例如,金融工具的开盘价和收盘价)。现在让我们看一下数组的切片技术:
# Defining a one-dimensional array
my_array = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
# Referring to the first value of the array
my_array[0] # Outputs 1
# Referring to the last value of the array
my_array[-1] # Outputs 10
# Referring to the fifth value of the array
my_array[6] # Outputs 7
# Referring to the first three values of the array
my_array[0:3] # Outputs array([1, 2, 3])
my_array[:3] # Outputs array([1, 2, 3])
# Referring to the last three values of the array
my_array[-3:] # Outputs array([8, 9, 10])
# Referring to all the values as of the second value
my_array[1:] # Outputs array([2, 3, 4, 5, 6, 7, 8, 9, 10])
# Defining a multi-dimensional array
my_array = np.array([[ 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10],
[11, 12, 13, 14, 15]])
# Referring to the first value and second column of the array
my_array[0, 1] # Outputs 2
# Referring to the last value and last column of the array
my_array[-1, -1] # Outputs 15
# Referring to the third value and second to last column of the array
my_array[2, -2] # Outputs 14
# Referring to the first three and fourth column values of the array
my_array[:, 2:4] # Outputs array([[3, 4], [8, 9], [13, 14]])
# Referring to the last two values and fifth column of the array
my_array[-2:, 4] # Outputs array([10, 15])
# Referring to all the values and all the columns up until the second row
my_array[:2, ] # Outputs array([[ 1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])
# `Referring to the last row with all the columns`
my_array[-1:, :] # Outputs array([[11, 12, 13, 14, 15]])
注意
重要的是要知道 Python 的索引从零开始。这意味着要引用数据结构中的第一个元素,您需要将其索引称为 index = 0。另外,值得注意的是,在范围中,最后一个元素是排除在外的,这意味着数据结构中的前三个元素被称为[0, 3],这将给出索引为 0、1 和 2 的元素。
让我们看看数据框的情况,以便在您想要操作数据结构时,本节成为一种小百科全书:
# Defining a one-dimensional data frame
my_df= pd.DataFrame({'first_column': [1, 2, 3, 4, 5,
6, 7, 8, 9, 10]})
# Referring to the first value of the data frame
my_df.iloc[0]['first_column'] # Outputs 1
# Referring to the last value of the data frame
my_df.iloc[-1]['first_column'] # Outputs 10
# Referring to the fifth value of the data frame
my_df.iloc[6]['first_column'] # Outputs 7
# Referring to the first three values of the data frame
my_df.iloc[0:3]['first_column'] # Outputs ([1, 2, 3])
# Referring to the last three values of the data frame
my_df.iloc[-3:]['first_column'] # Outputs ([8, 9, 10])
# Referring to all the values as of the second value
my_df.iloc[1:]['first_column'] # Outputs ([2, 3, 4, 5, 6, 7, 8, 9, 10])
# Defining a multi-dimensional data frame
my_df = pd.DataFrame({'first_column' : [ 1, 6, 11],
'second_column' : [ 2, 7, 12],
'third_column' : [ 3, 8, 13],
'fourth_column' : [ 4, 9, 14],
'fifth_column' : [ 5, 10, 15]})
# Referring to the first value and second column of the data frame
my_df.iloc[0]['second_column'] # Outputs 2
# Referring to the last value and last column of the data frame
my_df.iloc[-1]['fifth_column'] # Outputs 15
# Referring to the third value and second to last column of the data frame
my_df.iloc[2]['fourth_column'] # Outputs 14
# Referring to the first three and fourth column values of the data frame
my_df.iloc[:][['third_column', 'fourth_column']]
# Referring to the last two values and fifth column of the data frame
my_df.iloc[-2:]['fifth_column'] # Outputs` `([10, 15])
# Referring to all the values and all the columns up until the second row
my_df.iloc[:2,] # Outputs ([[ 1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])
# Referring to the last row with all the columns
my_df.iloc[-1:,] # Outputs ([[11, 12, 13, 14, 15]])
注意
尝试回到之前的章节执行那里给出的代码。到现在为止,您应该有了更牢固的理解。
在 Python 中导入金融时间序列
本节介绍了部署机器和深度学习算法的关键部分。它涉及到运行模型和评估其性能所需的历史 OHLC 数据。
第一步是准备环境和算法成功所需的一切。为此,您需要两个程序:
-
您用于编写和执行代码的 Python 解释器。您已经完成了这一步。
-
您使用作为数据库的图表和金融软件。这部分内容在本节中涵盖。
在整本书中,我使用MetaTrader 5,这是许多全球交易员使用的基准图表程序。按照以下步骤操作:
-
下载 SPYDER 并熟悉其工作方式。
-
下载 MetaTrader 5 软件。
-
使用 SPYDER 从 MetaTrader 5 导入历史价格。
从官方网站下载并安装 MetaTrader 5。您需要创建一个演示账户,这只是一个带有虚拟货币的虚拟账户。单词demo并不是指使用的时间有限,而是指它不使用真实货币。
要开设账户,选择File > Open an Account,选择 MetaQuotes Software Corp,然后单击Next。然后,选择第一个选项以开设演示账户;这将让您交易虚拟货币。最后,输入一些基本信息,如姓名、电子邮件和账户类型。您将不会收到验证请求或任何类型的确认,因为演示应该直接启动,让您看到图表。
图 6-3 显示了平台的界面。默认情况下,MetaTrader 5 不显示其涵盖的所有市场,因此您需要使它们可供导入和可视化。单击View,单击Market Watch,然后右键单击新标签中显示的任何符号,并选择Show All。这样您就可以看到更多市场的扩展列表。

图 6-3. MetaTrader 5 界面
在进行编码之前,您需要安装 MetaTrader 5 Python 集成库,以便以后在 Spyder 中使用。这很容易,只需一步。打开 Anaconda 提示符并键入:
pip install MetaTrader5
安装是一个桥梁,它允许您在解释器中使用为 MetaTrader 5 设计的 Python 模块和函数。
以下代码块使用了import内置语句,该语句调用内部(自己创建的)或外部(由第三方创建的)库。库是函数的存储库,因此,您需要导入与您要做的事情相关的库。为了演示目的,导入以下模块、包和库:
import datetime # Gives tools for manipulating dates and time
import pytz # Offers cross-platform time zone calculations
import MetaTrader5 as mt5 # Importing the software's library
import pandas as pd
import numpy as np
下一步是创建您可以导入的时间框架的范围。即使我将向您展示如何分析和回测每小时数据,您也可以定义一个更广泛的时间框架,如下面的代码片段所示:
frame_M15 = mt5.TIMEFRAME_M15 # 15-minute time
frameframe_M30 = mt5.TIMEFRAME_M30 # 30-minute time frame
frame_H1 = mt5.TIMEFRAME_H1 # Hourly time frame
frame_H4 = mt5.TIMEFRAME_H4 # 4-hour time frame
frame_D1 = mt5.TIMEFRAME_D1 # Daily time frame
frame_W1 = mt5.TIMEFRAME_W1 # Weekly time frame
frame_M1 = mt5.TIMEFRAME_MN1 # Monthly time frame
注意
完整的代码可以在 GitHub 存储库中找到,名称为Master_Function.py
time frame是您记录价格的频率。使用每小时数据,您将每小时记录最后一个价格。这意味着在一天内,您可以有多达 24 个每小时的价格。这使您能够看到价格的日内演变。目标是记录特定时间段内的 OHLC 数据的全部内容。
以下代码定义了当前时间,用于算法在导入数据时有一个参考点。基本上,您正在创建一个存储当前时间和日期的变量:
now = datetime.datetime.now()
现在让我们继续定义您想要进行回测的金融工具的范围。在本书中,回测将专门在外汇市场上进行。因此,让我们创建一个存储一些关键货币对的变量:
assets = ['EURUSD', 'USDCHF', 'GBPUSD', 'USDCAD']
现在您已经准备好了时间和资产变量,您所需要做的就是创建导入算法的结构。get_quotes()函数完成了这个任务:
def get_quotes(time_frame, year = 2005, month = 1, day = 1,
asset = "EURUSD"):
if not mt5.initialize():
print("initialize() failed, error code =", mt5.last_error())
quit()
timezone = pytz.timezone("Europe/Paris")
time_from = datetime.datetime(year, month, day, tzinfo = timezone)
time_to = datetime.datetime.now(timezone) + datetime.timedelta(days=1)
rates = mt5.copy_rates_range(asset, time_frame, time_from, time_to)
rates_frame = pd.DataFrame(rates)
return rates_frame
请注意,在get_quotes()函数中,您使用了pytz和pandas库。该函数首先通过定义Olson时区开始,您可以自行设置。以下是一个简要的、非详尽的列表,根据您的时区,您可以输入以下内容:
America/New_York
Europe/London
Europe/Paris
Asia/Tokyo
Australia/Sydney
随后,我定义了两个变量time_from和time_to:
-
time_from变量包含了引用导入日期开始的日期时间(例如,2020 年 01 月 01 日)。 -
time_to变量包含了引用导入日期结束的日期时间,使用now变量表示当前时间和日期。
下一步是创建一个变量,使用您指定的时间段导入金融数据。这是通过使用mt5.copy_rates_range()函数的rates变量完成的。最后,使用pandas将数据转换为数据框。导入过程所需的最终函数是mass_import()函数。它允许您使用变量选择时间范围,然后使用get_quotes()函数导入数据并将其格式化为数组。以下代码片段定义了mass_import()函数:
def mass_import(asset, time_frame):
if time_frame == 'H1':
data = get_quotes(frame_H1, 2013, 1, 1, asset = assets[asset])
data = data.iloc[:, 1:5].values
data = data.round(decimals = 5)
return data
mass_import()函数会自动将数据框转换为数组,因此在使用自动导入时无需担心转换问题。
注意
该算法导入了一定数量的历史数据,受到 MetaTrader 5 的限制。尽管这个数字很高,但随着时间的推移,您可能需要调整年份参数以获取数据。例如,如果使用mass_import()函数时得到一个空数组,尝试在get_quotes()函数中放入一个更新的年份(比如“2014”而不是“2013”)。
要导入自 2014 年初以来的历史每小时 EURUSD 数据,您可以输入以下内容(假设get_quotes()、now、数据框和库已经定义):
# Defining the universe of currency pairs
assets = ['EURUSD', 'USDCHF', 'GBPUSD', 'USDCAD']
# Re-defining the mass_import function to switch to a default 2014
def mass_import(asset, time_frame):
if time_frame == 'H1':
data = get_quotes(frame_H1, 2014, 1, 1, asset = assets[asset])
data = data.iloc[:, 1:5].values
data = data.round(decimals = 5)
# Calling the mass_import function and storing it into a variable
eurusd_data = mass_import(0, 'H1')
注意
注意return语句如何在mass_import函数中用于将历史数据存储在选择的变量中。
尽管 MetaTrader 5 有 MAC 版本,但 Python 库只能在 Windows 上运行。在 Mac 上需要使用 Windows 模拟器。对于 Mac 用户,您可能想尝试手动导入方法。
自动导入是一个巨大的时间节省,但 MAC 用户甚至 Windows 用户可能会遇到令人沮丧的错误。因此,我将向您展示手动导入的方法,您可以将其用作修复。在 GitHub 链接中,您会找到一个名为 Historical Data 的文件夹。文件夹中有一些历史金融时间序列的 excel 形式,您可以下载。
手动方式是使用从第三方下载的包含 OHLC 数据的 Excel 文件(例如 GitHub 存储库中提供的 excel 文件)的方法。在这种情况下,您可以使用pandas库导入并将其转换为数组。
让我们以eurusd_data为例。下载文件并将其存储在桌面上。现在,您必须确保 Spyder 的目录与文件在同一位置(即桌面)。通俗地说,Spyder 必须在桌面上搜索 Excel 文件。要选择正确的目录,您必须点击箭头旁边的文件夹按钮,如图 6-4 所示:

图 6-4. 目录选项卡
您应该会得到一个单独的窗口,您可以在其中选择桌面位置,然后验证选择。完成这些步骤后,选项卡应该看起来像图 6-5:

图 6-5. 目录选项卡
您必须使用read_excel()函数(在导入后可访问的pandas内置函数)来获取 Excel 文件中的值。按照以下语法:
# Importing the excel file into the Python interpreter
my_data = pd.read_excel('eurusd_data.xlsx')
现在,您有一个名为eurusd_data的数据框,其中有四列,分别代表开盘价、最高价、最低价和收盘价。通常在使用属于库的函数之前,您必须输入库的名称;这就是为什么read_excel()前面有pd的原因。
注意
我建议 Windows 用户使用自动方式,而 macOS 用户使用手动方式,因为兼容性问题。
您可以通过将.values添加到pd.read_excel('eurusd_data.xlsx')来直接在一行中获取数组,从而变为pd.read_excel('my_data.xlsx')``.values,从而得到一个数组而不是数据框。
总结
Python,编程语言之一的明星。它曾经受到并仍然受到开发者社区的广泛采用。掌握它是解锁数据科学世界巨大潜力的关键。
下一章介绍机器学习和不同的预测算法。主要目的是理解直觉,并能够编写算法并在金融数据上运行回测。一旦您开始理解这个过程,就变成了删除一个算法并插入另一个算法的问题(如果它们有相同的假设)。热身章节已经结束,现在是开始编码的时候了。
虚数是一种代表负数的平方根的复数类型。
关于作者
Sofien Kaabar 是一位金融作家、交易顾问和机构市场策略师,专门从事货币市场,重点关注技术和量化主题。Sofien 的目标是通过结合可以分析和创建的清晰条件,使技术分析客观化,并使用可以与现有技术指标匹敌的技术指标。
Sofien 已经详细阐述了许多成功的交易算法,现在他正在分享多年来所获得的知识,使其对每个人都可以获得。


浙公网安备 33010602011771号