Python 金融编程第二版(GPT 重译)(四)(4)https://developer.aliyun.com/article/1559369
基于 HDF5 的数据存储
当涉及到结构化的数值和金融数据时,HDF5
分层数据库(文件)格式是一个强大的替代方案,例如,关系数据库。无论是在直接使用PyTables
还是与pandas
的功能结合使用时,您都可以期望获得几乎达到可用硬件允许的最大 I/O 性能。
内存外计算
PyTables
支持内存外操作,这使得可以实现不适合内存的基于数组的计算。为此,请考虑以下基于EArray
类的代码。这种类型的对象允许在一维(按行)中扩展,而列数(每行的元素)需要固定。
In [182]: filename = path + 'earray.h5' In [183]: h5 = tb.open_file(filename, 'w') In [184]: n = 500 ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) In [185]: ear = h5.create_earray('/', 'ear', ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) atom=tb.Float64Atom(), ![3](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/3.png) shape=(0, n)) ![4](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/4.png) In [186]: type(ear) Out[186]: tables.earray.EArray In [187]: rand = np.random.standard_normal((n, n)) ![5](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/5.png) rand[:4, :4] Out[187]: array([[-1.25983231, 1.11420699, 0.1667485 , 0.7345676 ], [-0.13785424, 1.22232417, 1.36303097, 0.13521042], [ 1.45487119, -1.47784078, 0.15027672, 0.86755989], [-0.63519366, 0.1516327 , -0.64939447, -0.45010975]]) In [188]: %%time for _ in range(750): ear.append(rand) ![6](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/6.png) ear.flush() CPU times: user 728 ms, sys: 1.11 s, total: 1.84 s Wall time: 2.03 s In [189]: ear Out[189]: /ear (EArray(375000, 500)) '' atom := Float64Atom(shape=(), dflt=0.0) maindim := 0 flavor := 'numpy' byteorder := 'little' chunkshape := (16, 500) In [190]: ear.size_on_disk Out[190]: 1500032000
这定义了固定的列数。
EArray
对象的路径和技术名称。
单个值的原子dtype
对象。
用于实例化的形状(没有行,n
列)。
具有随机数的ndarray
对象…
… 多次附加。
对于不会导致聚合的内存外计算,需要另一个相同形状(大小)的EArray
对象。 PyTables+有一个特殊模块可以高效处理数值表达式。它称为Expr
,基于数值表达式库numexpr
。接下来的代码使用Expr
计算之前整个EArray
对象中的方程式 9-1 的数学表达式。
方程式 9-1. 示例数学表达式
y = 3 sin ( x ) + | x |
结果存储在out
EArray
对象中,表达式评估以块方式进行。
In [191]: out = h5.create_earray('/', 'out', atom=tb.Float64Atom(), shape=(0, n)) In [192]: out.size_on_disk Out[192]: 0 In [193]: expr = tb.Expr('3 * sin(ear) + sqrt(abs(ear))') ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) In [194]: expr.set_output(out, append_mode=True) ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) In [195]: %time expr.eval() ![3](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/3.png) CPU times: user 2.98 s, sys: 1.38 s, total: 4.36 s Wall time: 3.28 s Out[195]: /out (EArray(375000, 500)) '' atom := Float64Atom(shape=(), dflt=0.0) maindim := 0 flavor := 'numpy' byteorder := 'little' chunkshape := (16, 500) In [196]: out.size_on_disk Out[196]: 1500032000 In [197]: out[0, :10] Out[197]: array([-1.73369462, 3.74824436, 0.90627898, 2.86786818, 1.75424957, -0.91108973, -1.68313885, 1.29073295, -1.68665599, -1.71345309]) In [198]: %time out_ = out.read() ![4](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/4.png) CPU times: user 879 ms, sys: 1.11 s, total: 1.99 s Wall time: 2.18 s In [199]: out_[0, :10] Out[199]: array([-1.73369462, 3.74824436, 0.90627898, 2.86786818, 1.75424957, -0.91108973, -1.68313885, 1.29073295, -1.68665599, -1.71345309])
这将基于str
对象的表达式转换为Expr
对象。
这定义了输出为 out
EArray
对象。
这启动了表达式的评估。
这将整个 EArray
读入内存。
考虑到整个操作是在内存之外进行的,可以认为是相当快的,尤其是在标准硬件上执行。作为基准,可以考虑 numexpr
模块的内存性能(也见[Link to Come])。它更快,但并不是很大的优势:
In [200]: import numexpr as ne ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) In [201]: expr = '3 * sin(out_) + sqrt(abs(out_))' ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) In [202]: ne.set_num_threads(1) ![3](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/3.png) Out[202]: 4 In [203]: %time ne.evaluate(expr)[0, :10] ![4](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/4.png) CPU times: user 1.72 s, sys: 529 ms, total: 2.25 s Wall time: 2.38 s Out[203]: array([-1.64358578, 0.22567882, 3.31363043, 2.50443549, 4.27413965, -1.41600606, -1.68373023, 4.01921805, -1.68117412, -1.66053597]) In [204]: ne.set_num_threads(4) ![5](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/5.png) Out[204]: 1 In [205]: %time ne.evaluate(expr)[0, :10] ![6](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/6.png) CPU times: user 2.29 s, sys: 804 ms, total: 3.09 s Wall time: 1.56 s Out[205]: array([-1.64358578, 0.22567882, 3.31363043, 2.50443549, 4.27413965, -1.41600606, -1.68373023, 4.01921805, -1.68117412, -1.66053597]) In [206]: h5.close() In [207]: !rm -f $path*
导入用于 内存中 评估数值表达式的模块。
数值表达式作为 str
对象。
将线程数设置为仅一个。
使用一个线程在内存中评估数值表达式。
将线程数设置为四。
使用四个线程在内存中评估数值表达式。
通过 TsTables 进行 I/O 操作。
TsTables
包使用 PyTables
构建了一个高性能的时间序列数据存储。主要的使用场景是“一次写入,多次检索
”。这是金融分析中的典型场景,因为数据是在市场上创建的,可能是实时或异步检索,并存储在磁盘上以供以后使用。这样的使用场景可能是一个较大的交易策略回测程序,需要反复使用历史金融时间序列的不同子集。因此,数据检索速度很重要。
示例数据
通常情况下,首先生成一些足够大的示例数据集,以说明 TsTables
的好处。以下代码基于几何布朗运动的模拟生成了三个相当长的金融时间序列(见[Link to Come])。
In [208]: no = 5000000 ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) co = 3 ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) interval = 1. / (12 * 30 * 24 * 60) ![3](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/3.png) vol = 0.2 ![4](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/4.png) In [209]: %%time rn = np.random.standard_normal((no, co)) ![5](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/5.png) rn[0] = 0.0 ![6](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/6.png) paths = 100 * np.exp(np.cumsum(-0.5 * vol ** 2 * interval + vol * np.sqrt(interval) * rn, axis=0)) ![7](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/7.png) paths[0] = 100 ![8](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/8.png) CPU times: user 932 ms, sys: 204 ms, total: 1.14 s Wall time: 1.2 s
时间步数。
时间序列的数量。
年份间隔作为年分数。
波动率。
标准正态分布的随机数。
初始随机数设为 0。
基于 Euler 离散化的模拟。
将路径的初始值设为 100。
由于TsTables
与pandas
DataFrame
对象很好地配合,因此数据被转换为这样的对象(另见图 9-7)。
In [210]: dr = pd.date_range('2019-1-1', periods=no, freq='1s') In [211]: dr[-6:] Out[211]: DatetimeIndex(['2019-02-27 20:53:14', '2019-02-27 20:53:15', '2019-02-27 20:53:16', '2019-02-27 20:53:17', '2019-02-27 20:53:18', '2019-02-27 20:53:19'], dtype='datetime64[ns]', freq='S') In [212]: df = pd.DataFrame(paths, index=dr, columns=['ts1', 'ts2', 'ts3']) In [213]: df.info() <class 'pandas.core.frame.DataFrame'> DatetimeIndex: 5000000 entries, 2019-01-01 00:00:00 to 2019-02-27 20:53:19 Freq: S Data columns (total 3 columns): ts1 float64 ts2 float64 ts3 float64 dtypes: float64(3) memory usage: 152.6 MB In [214]: df.head() Out[214]: ts1 ts2 ts3 2019-01-01 00:00:00 100.000000 100.000000 100.000000 2019-01-01 00:00:01 100.018443 99.966644 99.998255 2019-01-01 00:00:02 100.069023 100.004420 99.986646 2019-01-01 00:00:03 100.086757 100.000246 99.992042 2019-01-01 00:00:04 100.105448 100.036033 99.950618 In [215]: df[::100000].plot(figsize=(10, 6)); plt.savefig('../../images/ch09/io_07.png')
图 9-7. 金融时间序列的选定数据点
数据存储
TsTables
基于特定的基于块的结构存储金融时间序列数据,该结构允许根据某个时间间隔快速检索任意数据子集。为此,该软件包将create_ts()
函数添加到PyTables
中。以下代码使用了来自PyTables
的class
基于描述方法,基于tb.IsDescription
类。
In [216]: import tstables as tstab In [217]: class ts_desc(tb.IsDescription): timestamp = tb.Int64Col(pos=0) ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) ts1 = tb.Float64Col(pos=1) ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) ts2 = tb.Float64Col(pos=2) ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) ts3 = tb.Float64Col(pos=3) ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) In [218]: h5 = tb.open_file(path + 'tstab.h5', 'w') ![3](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/3.png) In [219]: ts = h5.create_ts('/', 'ts', ts_desc) ![4](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/4.png) In [220]: %time ts.append(df) ![5](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/5.png) CPU times: user 692 ms, sys: 403 ms, total: 1.1 s Wall time: 1.12 s In [221]: type(ts) Out[221]: tstables.tstable.TsTable In [222]: ls -n $path total 306720 -rw-r--r-- 1 501 20 157037368 Jan 18 10:07 tstab.h5
时间戳的列。
存储数字数据的列。
为写入(w
)打开HDF5
数据库文件。
基于ts_desc
对象创建TsTable
对象。
将DataFrame
对象中的数据附加到TsTable
对象。
数据检索
使用TsTables
编写数据显然非常快,即使与硬件有关。对数据的块的读取也是如此。方便的是,TaTables
返回一个DataFrame
对象(另见图 9-8)。
In [223]: read_start_dt = dt.datetime(2019, 2, 1, 0, 0) ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) read_end_dt = dt.datetime(2019, 2, 5, 23, 59) ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) In [224]: %time rows = ts.read_range(read_start_dt, read_end_dt) ![3](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/3.png) CPU times: user 80.5 ms, sys: 36.2 ms, total: 117 ms Wall time: 116 ms In [225]: rows.info() ![4](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/4.png) <class 'pandas.core.frame.DataFrame'> DatetimeIndex: 431941 entries, 2019-02-01 00:00:00 to 2019-02-05 23:59:00 Data columns (total 3 columns): ts1 431941 non-null float64 ts2 431941 non-null float64 ts3 431941 non-null float64 dtypes: float64(3) memory usage: 13.2 MB In [226]: rows.head() ![4](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/4.png) Out[226]: ts1 ts2 ts3 2019-02-01 00:00:00 52.063640 40.474580 217.324713 2019-02-01 00:00:01 52.087455 40.471911 217.250070 2019-02-01 00:00:02 52.084808 40.458013 217.228712 2019-02-01 00:00:03 52.073536 40.451408 217.302912 2019-02-01 00:00:04 52.056133 40.450951 217.207481 In [227]: h5.close() In [228]: (rows[::500] / rows.iloc[0]).plot(figsize=(10, 6)); plt.savefig('../../images/ch09/io_08.png')
时间间隔的开始时间。
时间间隔的结束时间。
函数ts.read_range()
返回时间间隔的DataFrame
对象。
DataFrame
对象有几十万行数据。
图 9-8. 金融时间序列的特定时间间隔(归一化)
为了更好地说明基于TsTables
的数据检索性能,考虑以下基准,该基准检索由三天的一秒钟柱状图组成的 100 个数据块。检索包含 345,600 行数据的DataFrame
仅需不到十分之一秒。
In [229]: import random In [230]: h5 = tb.open_file(path + 'tstab.h5', 'r') In [231]: ts = h5.root.ts._f_get_timeseries() ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) In [235]: %%time for _ in range(100): ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) d = random.randint(1, 24) ![3](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/3.png) read_start_dt = dt.datetime(2019, 2, d, 0, 0, 0) read_end_dt = dt.datetime(2019, 2, d + 3, 23, 59, 59) rows = ts.read_range(read_start_dt, read_end_dt) CPU times: user 3.51 s, sys: 1.03 s, total: 4.55 s Wall time: 4.62 s In [233]: rows.info() ![4](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/4.png) <class 'pandas.core.frame.DataFrame'> DatetimeIndex: 431941 entries, 2019-02-01 00:00:00 to 2019-02-05 23:59:00 Data columns (total 3 columns): ts1 431941 non-null float64 ts2 431941 non-null float64 ts3 431941 non-null float64 dtypes: float64(3) memory usage: 13.2 MB In [234]: !rm $path/tstab.h5
连接到TsTable
对象。
数据检索重复多次。
起始日值被随机化。
最后检索到的DataFrame
对象。
结论
基于SQL
或关系数据库的方法在处理展示了许多单个对象/表之间关系的复杂数据结构时具有优势。在某些情况下,这可能会使它们在纯NumPy
ndarray
或pandas
DataFrame
方法上的性能劣势成为合理。
金融或一般科学中的许多应用领域可以通过主要基于数组的数据建模方法取得成功。在这些情况下,通过利用原生NumPy
的 I/O 功能、NumPy
和PyTables
功能的组合,或通过HDF5
-based 存储的pandas
方法,可以实现巨大的性能提升。当处理大型(金融)时间序列数据集时,尤其是在“一次写入,多次检索”的场景中,TsTables
特别有用。
虽然最近的一个趋势是使用基于商品硬件的大量计算节点组成的云解决方案,特别是在金融背景下,人们应该仔细考虑哪种硬件架构最适合分析需求。微软的一项研究对这个问题有所启发:
我们声称一个“扩展”服务器可以处理这些工作中的每一个,并且在性能、成本、功耗和服务器密度等方面与集群一样好,甚至更好。
Appuswamy 等人(2013 年)
从事数据分析的公司、研究机构等应该首先分析一般情况下必须完成的具体任务,然后根据以下方面的硬件/软件架构做出决策:
扩展
使用具有标准 CPU 和相对较低内存的许多商品节点的集群
扩展
使用一台或多台强大的服务器,配备多核 CPU,可能还有 GPU 甚至 TPU,当机器学习和深度学习发挥作用时,并拥有大量内存。
扩展硬件规模并应用适当的实现方法可能会显著影响性能。下一章将更多地涉及性能。
进一步阅读
本章开头引用的论文以及“结论”部分是一篇不错的文章,也是思考金融分析硬件架构的良好起点:
- Appuswamy,Raja 等人(2013 年):“
没有人因为购买集群而被解雇。
”微软研究,英格兰剑桥,http://research.microsoft.com/apps/pubs/default.aspx?id=179615。
通常情况下,网络提供了许多有关本章涵盖主题的宝贵资源:
- 对于使用
pickle
对 Python 对象进行序列化,请参阅文档:http://docs.python.org/3/library/pickle.html。 - 关于
NumPy
的 I/O 功能概述可在SciPy
网站上找到:http://docs.scipy.org/doc/numpy/reference/routines.io.html。 - 对于使用
pandas
进行 I/O,请参阅在线文档中的相应部分:http://pandas.pydata.org/pandas-docs/stable/io.html。 PyTables
首页提供了教程和详细文档:http://www.pytables.org。TsTables
的 Github 页面位于https://github.com/afiedler/tstables。
¹ 这里,我们不区分不同级别的 RAM 和处理器缓存。当前内存架构的最佳使用是一个独立的主题。
² 要了解 Python 可用的数据库连接器的概述,请访问https://wiki.python.org/moin/DatabaseInterfaces。与直接使用关系型数据库不同,对象关系映射器,例如SQLAlchemy,通常非常有用。它们引入了一个抽象层,允许更加 Pythonic、面向对象的代码。它们还允许更容易地在后端将一个关系型数据库更换为另一个。
³ 请参阅https://www.sqlite.org/lang.html以了解 SQLite3
语言方言的概述。
⁴ 请参阅http://docs.scipy.org/doc/numpy/reference/arrays.datetime.html。
⁵ 许多其他数据库需要服务器-客户端架构。对于交互式数据和金融分析,基于文件的数据库在一般情况下会更加方便,也足够满足大多数目的。
s: user 3.51 s, sys: 1.03 s, total: 4.55 s
Wall time: 4.62 s
In [233]: rows.info()
<class 'pandas.core.frame.DataFrame'> DatetimeIndex: 431941 entries, 2019-02-01 00:00:00 to 2019-02-05 23:59:00 Data columns (total 3 columns): ts1 431941 non-null float64 ts2 431941 non-null float64 ts3 431941 non-null float64 dtypes: float64(3) memory usage: 13.2 MB
In [234]: !rm $path/tstab.h5
[外链图片转存中...(img-axcfhWBH-1717935705766)] 连接到`TsTable`对象。 [外链图片转存中...(img-ieVQ1Bmg-1717935705766)] 数据检索重复多次。 [外链图片转存中...(img-LvzYt28v-1717935705767)] 起始日值被随机化。 [外链图片转存中...(img-VlipnGfe-1717935705767)] 最后检索到的`DataFrame`对象。 # 结论 基于`SQL`或关系数据库的方法在处理展示了许多单个对象/表之间关系的复杂数据结构时具有优势。在某些情况下,这可能会使它们在纯`NumPy` `ndarray`或`pandas` `DataFrame`方法上的性能劣势成为合理。 金融或一般科学中的许多应用领域可以通过主要基于数组的数据建模方法取得成功。在这些情况下,通过利用原生`NumPy`的 I/O 功能、`NumPy`和`PyTables`功能的组合,或通过`HDF5`-based 存储的`pandas`方法,可以实现巨大的性能提升。当处理大型(金融)时间序列数据集时,尤其是在“一次写入,多次检索”的场景中,`TsTables`特别有用。 虽然最近的一个趋势是使用基于商品硬件的大量计算节点组成的云解决方案,特别是在金融背景下,人们应该仔细考虑哪种硬件架构最适合分析需求。微软的一项研究对这个问题有所启发: > 我们声称一个“扩展”服务器可以处理这些工作中的每一个,并且在性能、成本、功耗和服务器密度等方面与集群一样好,甚至更好。 > > Appuswamy 等人(2013 年) 从事数据分析的公司、研究机构等应该首先分析一般情况下必须完成的具体任务,然后根据以下方面的硬件/软件架构做出决策: 扩展 使用具有标准 CPU 和相对较低内存的许多商品节点的集群 扩展 使用一台或多台强大的服务器,配备多核 CPU,可能还有 GPU 甚至 TPU,当机器学习和深度学习发挥作用时,并拥有大量内存。 扩展硬件规模并应用适当的实现方法可能会显著影响性能。下一章将更多地涉及性能。 # 进一步阅读 本章开头引用的论文以及“结论”部分是一篇不错的文章,也是思考金融分析硬件架构的良好起点: + Appuswamy,Raja 等人(2013 年):“`没有人因为购买集群而被解雇。`”微软研究,英格兰剑桥,[*http://research.microsoft.com/apps/pubs/default.aspx?id=179615*](http://research.microsoft.com/apps/pubs/default.aspx?id=179615)。 通常情况下,网络提供了许多有关本章涵盖主题的宝贵资源: + 对于使用`pickle`对 Python 对象进行序列化,请参阅文档:[*http://docs.python.org/3/library/pickle.html*](http://docs.python.org/3/library/pickle.html)。 + 关于`NumPy`的 I/O 功能概述可在`SciPy`网站上找到:[*http://docs.scipy.org/doc/numpy/reference/routines.io.html*](http://docs.scipy.org/doc/numpy/reference/routines.io.html)。 + 对于使用`pandas`进行 I/O,请参阅在线文档中的相应部分:[*http://pandas.pydata.org/pandas-docs/stable/io.html*](http://pandas.pydata.org/pandas-docs/stable/io.html)。 + `PyTables`首页提供了教程和详细文档:[*http://www.pytables.org*](http://www.pytables.org)。 + `TsTables` 的 Github 页面位于[*https://github.com/afiedler/tstables*](https://github.com/afiedler/tstables)。 ¹ 这里,我们不区分不同级别的 RAM 和处理器缓存。当前内存架构的最佳使用是一个独立的主题。 ² 要了解 Python 可用的数据库连接器的概述,请访问[*https://wiki.python.org/moin/DatabaseInterfaces*](https://wiki.python.org/moin/DatabaseInterfaces)。与直接使用关系型数据库不同,对象关系映射器,例如[SQLAlchemy](https://www.sqlalchemy.org/),通常非常有用。它们引入了一个抽象层,允许更加 Pythonic、面向对象的代码。它们还允许更容易地在后端将一个关系型数据库更换为另一个。 ³ 请参阅[*https://www.sqlite.org/lang.html*](https://www.sqlite.org/lang.html)以了解 `SQLite3` 语言方言的概述。 ⁴ 请参阅[*http://docs.scipy.org/doc/numpy/reference/arrays.datetime.html*](http://docs.scipy.org/doc/numpy/reference/arrays.datetime.html)。 ⁵ 许多其他数据库需要服务器-客户端架构。对于交互式数据和金融分析,基于文件的数据库在一般情况下会更加方便,也足够满足大多数目的。