优化 BigQuery DataFrames 性能

BigQuery DataFrames 可帮助您使用与 pandas 兼容的 API 分析和转换 BigQuery 中的数据。为了更快、更经济高效地处理数据,您可以使用多种技巧来提升性能。

本文档介绍了以下优化性能的方法:

使用部分排序模式

BigQuery DataFrames 具有排序模式功能,可针对窗口函数和联接等操作强制执行特定的行顺序。您可以通过将 ordering_mode 属性设置为 strict(称为严格排序模式,这是默认设置)或 partial(称为部分排序模式)来指定排序模式。使用 partial 设置可以提高查询效率。

部分排序模式与严格排序模式不同。严格排序模式会按特定顺序排列所有行。这种总排序可使 BigQuery DataFrames 更好地与 pandas 搭配使用,让您可以使用 DataFrame.iloc 属性按行的顺序访问行。不过,总排序及其默认顺序索引会阻止列过滤条件或行过滤条件减少扫描的数据量。除非您将这些过滤条件作为参数应用于 read_gbqread_gbq_table 函数,否则系统会阻止这种情况。为了对 DataFrame 中的所有行进行排序,BigQuery DataFrames 会创建所有行的哈希。此操作可能会导致进行忽略行和列过滤条件的完整数据扫描。

部分排序模式会阻止 BigQuery DataFrames 为所有行创建总排序,并关闭需要总排序的功能,例如 DataFrame.iloc 属性。部分排序模式还会将 DefaultIndexKind设置为 null 索引,而不是顺序索引。

使用部分排序模式过滤 DataFrame 对象时,BigQuery DataFrames 不会计算顺序索引中缺少哪些行。部分排序模式也不会自动根据索引合并数据。这些方法可以提高查询效率。 不过,无论您使用默认的严格排序模式还是部分排序模式,BigQuery DataFrames API 的运作方式都与熟悉的 pandas API 类似。

对于部分排序模式和严格排序模式,您都需要为所使用的 BigQuery 资源付费。不过,在处理大型聚簇表和分区表时,使用部分排序模式可以降低成本。之所以能降低成本,是因为针对聚簇列和分区列的行过滤条件可以减少处理的数据量。

启用部分排序模式

如需使用部分排序,请先将 ordering_mode 属性设置为 partial,然后再使用 BigQuery DataFrames 执行任何其他操作,如以下代码示例所示:

import bigframes.pandas as bpd

bpd.options.bigquery.ordering_mode = "partial"

由于部分排序模式缺少顺序索引,因此可防止不相关的 BigQuery DataFrames 对象进行隐式联接。相反,您必须明确调用 DataFrame.merge 方法,才能联接源自不同表表达式的两个 BigQuery DataFrame 对象。

Series.unique()Series.drop_duplicates() 功能与部分排序模式不兼容。请改用 groupby 方法查找唯一值,如以下示例所示:

# Avoid order dependency by using groupby instead of drop_duplicates.
unique_col = df.groupby(["column"], as_index=False).size().drop(columns="size")

使用部分排序模式时,DataFrame.head(n)Series.head(n) 函数的输出可能每次运行都不相同。如需下载任意小型数据样本,请使用 DataFrame.peek()Series.peek() 方法

如需查看使用 ordering_mode = "partial" 属性的详细教程,请参阅使用 BigQuery DataFrames 分析通过 PyPI 进行的软件包下载情况

问题排查

由于部分排序模式下的 BigQuery DataFrames 有时缺少排序或索引,因此在使用某些与 pandas 兼容的方法时,您可能会遇到以下问题。

需要排序错误

某些功能(例如 DataFrame.head()DataFrame.iloc 函数)需要排序。如需查看需要排序的功能列表,请参阅支持的 Pandas API 中的需要排序列。

如果对象没有排序,操作会失败,并显示 OrderRequiredError 消息,如下所示:OrderRequiredError: Op iloc requires an ordering. Use .sort_values or .sort_index to provide an ordering.

如错误消息所述,您可以使用 DataFrame.sort_values() 方法提供排序,以便按一个或多个列进行排序。其他方法(例如 DataFrame.groupby())会按键隐式提供总排序。

如果排序不是针对所有行的完全稳定总排序,后续操作可能会显示 AmbiguousWindowWarning 消息,如下所示:AmbiguousWindowWarning: Window ordering may be ambiguous, this can cause unstable results.

如果您的工作可以处理不总是相同的结果,或者您可以手动检查您的排序是否为总排序,则可以通过以下方式过滤掉 AmbiguousWindowWarning 消息:

import warnings

import bigframes.exceptions

warnings.simplefilter(
    "ignore", category=bigframes.exceptions.AmbiguousWindowWarning
)

Null 索引错误

某些功能(例如 DataFrame.unstack()Series.interpolate() 属性)需要索引。如需查看需要索引的功能列表,请参阅支持的 Pandas API 中的需要索引列。

如果您在部分排序模式下使用需要索引的操作,该操作会引发 NullIndexError 消息,如下所示:NullIndexError: DataFrame cannot perform interpolate as it has no index. Set an index using set_index.

如错误消息所述,您可以使用 DataFrame.set_index() 方法提供索引,以便按一个或多个列进行排序。除非设置 as_index=False 参数,否则其他方法(例如 DataFrame.groupby())会按键隐式提供索引。

在执行开销大的操作后缓存结果

BigQuery DataFrames 会在本地存储操作,并延迟运行查询,直到满足特定条件。这可能会导致同一操作在不同查询中运行多次。

为避免重复执行开销大的操作,请使用 cache() 方法保存中间结果,如以下示例所示:

# Assume you have 3 large dataframes "users", "group" and "transactions"

# Expensive join operations
final_df = users.join(groups).join(transactions)
final_df.cache()
# Subsequent derived results will reuse the cached join
print(final_df.peek())
print(len(final_df[final_df["completed"]]))
print(final_df.groupby("group_id")["amount"].mean().peek(30))

此方法会创建一个临时 BigQuery 表来存储结果。您需要支付此临时表在 BigQuery 中的存储费用。

使用 peek() 方法预览数据

BigQuery DataFrames 提供两种 API 方法来预览数据:

  • peek(n) 返回 n 行数据,其中 n 是行数。
  • head(n) 返回前 n 行数据(具体取决于上下文),其中 n 是行数。

仅当数据顺序很重要时才使用 head() 方法,例如,当您需要列中五个最大的值时。在其他情况下,请使用 peek() 方法来更高效地检索数据,如以下代码示例所示:

import bigframes.pandas as bpd

# Read the "Penguins" table into a dataframe
df = bpd.read_gbq("bigquery-public-data.ml_datasets.penguins")

# Preview 3 random rows
df.peek(3)

您还可以使用 peek() 方法下载少量随机数据样本,同时使用部分排序模式

延迟 repr() 数据检索

您可以在 BigQuery DataFrames 中通过笔记本或 IDE 调试器调用 repr() 方法。此调用会触发 head() 调用,后者会检索实际数据。这种检索会减慢迭代编码和调试过程,还会产生费用。

如需阻止 repr() 方法检索数据,请将 repr_mode 属性设置为 "deferred",如以下示例所示:

import bigframes.pandas as bpd

bpd.options.display.repr_mode = "deferred"

在延迟模式下,您只能通过显式 peek()head() 调用来预览数据。

后续步骤