ArcticDB_demo_querybuilder
在 Github 中查看 | 在 Google Colab 中打开ArcticDB 查询构建器演示

请注意,此处演示的所有功能都可通过更直观的 LazyDataFrame API 实现,我们建议优先使用 LazyDataFrame API 而非 QueryBuilder。
在此演示中,我们将探讨 ArcticDB 的 QueryBuilder 的不同功能。我们将涵盖此 API 的各种可能性,包括
- 过滤
- 投影
- 分组和聚合
- 上述功能的组合
为何使用 QueryBuilder?
- 通过使用多线程的高效 C++ 实现提升性能
- 高效数据访问 - 只读取所需数据
- 对于超大数据集,某些查询可以在不全部加载到内存的情况下执行
演示设置¶
必要包安装
In [ ]
已复制!
!pip install arcticdb
!pip install arcticdb
必要库导入
In [3]
已复制!
import os
import numpy as np
import pandas as pd
import random
import arcticdb as adb
from arcticdb.util.test import random_strings_of_length
import os import numpy as np import pandas as pd import random import arcticdb as adb from arcticdb.util.test import random_strings_of_length
在此演示中,我们将配置基于 LMDB 文件的后端。当配置对象存储后端(例如 S3)时,ArcticDB 可实现其高性能和规模。
In [4]
已复制!
arctic = adb.Arctic("lmdb://arcticdb_demo")
arctic = adb.Arctic("lmdb://arcticdb_demo")
您可以拥有无限数量的库,但我们只创建一个作为开始。
In [5]
已复制!
if 'sample' not in arctic.list_libraries():
# library does not already exist
arctic.create_library('sample')
lib = arctic.get_library('sample')
if 'sample' not in arctic.list_libraries(): # library does not already exist arctic.create_library('sample') lib = arctic.get_library('sample')
运行此单元格设置初步变量。100,000 个唯一字符串对我们来说是一个病态的情况,因为在默认的行切片策略下,每个数据段有 100,000 行,因此在这个列中,每个唯一字符串大约在每个数据段中出现一次。
In [6]
已复制!
ten_grouping_values = random_strings_of_length(10, 10, True)
one_hundred_thousand_grouping_values = random_strings_of_length(100_000, 10, True)
rng = np.random.RandomState()
sym_10M = "demo_10M"
sym_100M = "demo_100M"
sym_1B = "demo_1B"
ten_grouping_values = random_strings_of_length(10, 10, True) one_hundred_thousand_grouping_values = random_strings_of_length(100_000, 10, True) rng = np.random.RandomState() sym_10M = "demo_10M" sym_100M = "demo_100M" sym_1B = "demo_1B"
选择您想使用的符号
- sym_10M: 包含 1000 万行的符号
- sym_100M: 包含 1 亿行的符号
- sym_1B: 包含 10 亿行的符号
将您想使用的符号赋值给 sym 变量
- 例如:sym = sym_10M
In [7]
已复制!
sym = sym_10M
sym = sym_10M
运行此单元格根据符号名称设置 DataFrame
In [8]
已复制!
if sym==sym_10M:
num_rows = 10_000_000
elif sym==sym_100M:
num_rows = 100_000_000
elif sym==sym_1B:
num_rows = 1_000_000_000
df = pd.DataFrame(
{
"grouping_column_10": list(random.choices(ten_grouping_values, k=num_rows)),
"grouping_column_100_000": list(random.choices(one_hundred_thousand_grouping_values, k=num_rows)),
"numeric_column": rng.rand((num_rows))
}
)
if sym==sym_10M: num_rows = 10_000_000 elif sym==sym_100M: num_rows = 100_000_000 elif sym==sym_1B: num_rows = 1_000_000_000 df = pd.DataFrame( { "grouping_column_10": list(random.choices(ten_grouping_values, k=num_rows)), "grouping_column_100_000": list(random.choices(one_hundred_thousand_grouping_values, k=num_rows)), "numeric_column": rng.rand((num_rows)) } )
演示开始¶
In [ ]
已复制!
lib.write(sym, df)
lib.write(sym, df)
显示数据如何被切片并写入磁盘。
In [ ]
已复制!
lib._nvs.read_index(sym)
lib._nvs.read_index(sym)
显示前 100 行数据作为示例。
In [ ]
已复制!
lib.head(sym, n=100).data
lib.head(sym, n=100).data
读取¶
读取符号,不进行任何过滤。
In [ ]
已复制!
%%time
lib.read(sym)
%%time lib.read(sym)
大部分时间花在为包含 100,000 个唯一字符串的列分配 Python 字符串上,因此省略此列会快得多。
In [ ]
已复制!
%%time
lib.read(sym, columns=["grouping_column_10", "numeric_column"])
%%time lib.read(sym, columns=["grouping_column_10", "numeric_column"])
过滤¶
请注意,数字列中的所有值都在 0 到 1 之间。因此,此查询不会过滤掉任何数据。这表明进行全表扫描不会显著影响性能。
In [16]
已复制!
q = adb.QueryBuilder()
q = q[q["numeric_column"] < 2.0]
q = adb.QueryBuilder() q = q[q["numeric_column"] < 2.0]
In [ ]
已复制!
%%time
lib.read(sym, query_builder=q)
%%time lib.read(sym, query_builder=q)
现在我们将过滤到符号中大约 10% 的行。这比直接读取更快,因为需要分配的 Python 字符串更少。
In [18]
已复制!
q = adb.QueryBuilder()
q = q[q["numeric_column"] < 0.1]
q = adb.QueryBuilder() q = q[q["numeric_column"] < 0.1]
In [ ]
已复制!
%%time
lib.read(sym, query_builder=q).data
%%time lib.read(sym, query_builder=q).data
In [ ]
已复制!
lib.read(sym, query_builder=q).data
lib.read(sym, query_builder=q).data
投影¶
创建作为现有列和常数函数的新列的速度与不减少显示数据量的过滤器大约相同。
In [21]
已复制!
q = adb.QueryBuilder()
q = q.apply("new_column", q["numeric_column"] * 2.0)
q = adb.QueryBuilder() q = q.apply("new_column", q["numeric_column"] * 2.0)
In [ ]
已复制!
%%time
lib.read(sym, query_builder=q)
%%time lib.read(sym, query_builder=q)
In [ ]
已复制!
lib.read(sym, query_builder=q).data
lib.read(sym, query_builder=q).data
分组和聚合¶
即使执行了额外的计算,分组操作也比直接读取更快,因为减少了 Python 字符串的分配。
In [24]
已复制!
q = adb.QueryBuilder()
q = q.groupby("grouping_column_10").agg({"numeric_column": "mean"})
q = adb.QueryBuilder() q = q.groupby("grouping_column_10").agg({"numeric_column": "mean"})
In [ ]
已复制!
%%time
lib.read(sym, query_builder=q)
%%time lib.read(sym, query_builder=q)
In [ ]
已复制!
lib.read(sym, query_builder=q).data
lib.read(sym, query_builder=q).data
即使对病态多的唯一值进行分组,也不会显著降低性能。
In [27]
已复制!
q = adb.QueryBuilder()
q = q.groupby("grouping_column_100_000").agg({"numeric_column": "mean"})
q = adb.QueryBuilder() q = q.groupby("grouping_column_100_000").agg({"numeric_column": "mean"})
In [ ]
已复制!
%%time
lib.read(sym, query_builder=q)
%%time lib.read(sym, query_builder=q)
In [ ]
已复制!
lib.read(sym, query_builder=q).data
lib.read(sym, query_builder=q).data
组合¶
这些操作可以在顺序流水线中任意组合。
In [30]
已复制!
q = adb.QueryBuilder()
q = q[q["numeric_column"] < 0.1].apply("new_column", q["numeric_column"] * 2.0).groupby("grouping_column_10").agg({"numeric_column": "mean", "new_column": "max"})
q = adb.QueryBuilder() q = q[q["numeric_column"] < 0.1].apply("new_column", q["numeric_column"] * 2.0).groupby("grouping_column_10").agg({"numeric_column": "mean", "new_column": "max"})
In [ ]
已复制!
%%time
lib.read(sym, query_builder=q)
%%time lib.read(sym, query_builder=q)
In [ ]
已复制!
lib.read(sym, query_builder=q).data
lib.read(sym, query_builder=q).data