8.3 使用 query() 和 eval() 提高性能 Pandas 性能优化: 和 的妙用 8.3.1 :高效的数据筛选 方法允许你使用字符串表达式来筛选 DataFrame 中的数据。它比传统的布尔索引更加简洁易懂,而且在某些情况下,性能也更胜一筹。 语法: expr: 字符串表达式,用于筛选数据。 inplace: 是否修改原始 DataFrame。 kwargs: 传递给表达式的其他变量。 示例: 性能对比: 在大多数情况下, 的性能优于传统的布尔索引,尤其是在处理大型数据集和复杂查询时。 这是因为 内部使用了 库,它能够对表达式进行优化,并利用多线程加速计算。 的优势: 简洁易懂: 使用字符串表达式,代码更易读。 性能提升: 利用 优化计算。
query() 和 eval() 的妙用query():高效的数据筛选query() 方法允许你使用字符串表达式来筛选 DataFrame 中的数据。它比传统的布尔索引更加简洁易懂,而且在某些情况下,性能也更胜一筹。
语法:
DataFrame.query(expr, inplace=False, **kwargs)
expr: 字符串表达式,用于筛选数据。
inplace: 是否修改原始 DataFrame。
kwargs: 传递给表达式的其他变量。
示例:
import pandas as pd import numpy as np # 创建一个示例 DataFrame np.random.seed(42) df = pd.DataFrame({ 'A': np.random.rand(100000), 'B': np.random.rand(100000), 'C': np.random.randint(0, 10, 100000) }) # 传统布尔索引 filtered_df_bool = df[(df['A'] > 0.5) & (df['B'] < 0.3)] # 使用 query() filtered_df_query = df.query('A > 0.5 and B < 0.3') # 验证结果是否一致 print(filtered_df_bool.equals(filtered_df_query)) # 输出 True
性能对比:
import time # 传统布尔索引 start_time = time.time() filtered_df_bool = df[(df['A'] > 0.5) & (df['B'] < 0.3)] end_time = time.time() bool_time = end_time - start_time print(f"布尔索引耗时: {bool_time:.4f} 秒") # 使用 query() start_time = time.time() filtered_df_query = df.query('A > 0.5 and B < 0.3') end_time = time.time() query_time = end_time - start_time print(f"query() 耗时: {query_time:.4f} 秒") print(f"query() 比 布尔索引 快:{(bool_time - query_time)/bool_time * 100:.2f}%")
在大多数情况下,query() 的性能优于传统的布尔索引,尤其是在处理大型数据集和复杂查询时。 这是因为 query() 内部使用了 Numexpr 库,它能够对表达式进行优化,并利用多线程加速计算。
query() 的优势:
简洁易懂: 使用字符串表达式,代码更易读。
性能提升: 利用 Numexpr 优化计算。
支持局部变量: 可以在表达式中使用 DataFrame 之外的变量。
threshold = 0.7 filtered_df = df.query('A > @threshold') # @符号引用外部变量
query() 的局限性:
语法限制: 只能使用 Numexpr 支持的语法。
表达式求值: query() 会对表达式进行求值,因此需要注意潜在的安全风险,避免执行恶意代码。
最佳实践:
尽量使用 query() 来筛选数据,尤其是在处理大型数据集时。
避免在 query() 表达式中使用复杂的 Python 函数,以确保性能。
注意表达式的安全性,避免执行未知的代码。
eval():加速数值计算eval() 方法允许你使用字符串表达式来计算 DataFrame 的新列。 类似于 query(),它也利用 Numexpr 库来优化计算,从而提高性能。
语法:
DataFrame.eval(expr, inplace=False, **kwargs)
expr: 字符串表达式,用于计算新列。
inplace: 是否修改原始 DataFrame。
kwargs: 传递给表达式的其他变量。
示例:
# 创建一个示例 DataFrame np.random.seed(42) df = pd.DataFrame({ 'A': np.random.rand(100000), 'B': np.random.rand(100000), 'C': np.random.rand(100000) }) # 传统方法 df['D'] = df['A'] + df['B'] * df['C'] # 使用 eval() df['E'] = df.eval('A + B * C') # 验证结果是否一致 print(np.allclose(df['D'], df['E'])) # 输出 True
性能对比:
# 传统方法 start_time = time.time() df['D'] = df['A'] + df['B'] * df['C'] end_time = time.time() traditional_time = end_time - start_time print(f"传统方法耗时: {traditional_time:.4f} 秒") # 使用 eval() start_time = time.time() df['E'] = df.eval('A + B * C') end_time = time.time() eval_time = end_time - start_time print(f"eval() 耗时: {eval_time:.4f} 秒") print(f"eval() 比 传统方法 快:{(traditional_time - eval_time)/traditional_time * 100:.2f}%")
eval() 在进行数值计算时,通常比传统方法更快。 这是因为 eval() 避免了创建临时对象,并直接在 NumPy 数组上进行计算。
eval() 的优势:
性能提升: 利用 Numexpr 优化计算。
减少内存占用: 避免创建临时对象。
链式操作: 可以方便地进行链式计算。
df = df.eval('F = A + B').eval('G = F / C')
eval() 的局限性:
语法限制: 只能使用 Numexpr 支持的语法。
赋值操作: 只能创建新的列,不能修改现有的列。
安全性: 与 query() 类似,需要注意表达式的安全性。
最佳实践:
尽量使用 eval() 来计算新的列,尤其是在进行复杂的数值计算时。
避免在 eval() 表达式中使用 Python 函数,以确保性能。
注意表达式的安全性,避免执行未知的代码。
query() 和 eval() 的内部机制query() 和 eval() 的性能提升主要归功于 Numexpr 库。Numexpr 能够:
解析表达式: 将字符串表达式解析成抽象语法树。
优化表达式: 对表达式进行优化,例如消除公共子表达式、重排计算顺序等。
编译表达式: 将表达式编译成机器码,以便高效执行。
并行计算: 利用多线程并行计算,加速计算过程。
通过这些优化,Numexpr 能够显著提高数值计算的效率。
query() 和 eval() 是 Pandas 中非常有用的性能优化工具。 它们能够利用 Numexpr 库,加速数据筛选和数值计算,从而提高数据分析的效率。 在实际应用中,我们应该根据具体情况选择合适的工具,并注意表达式的安全性,以充分发挥它们的优势。
性能优化流程 (Graph TD):
关键要点:
query() 用于高效的数据筛选。
eval() 用于加速数值计算。
两者都利用 Numexpr 库进行优化。
注意表达式的语法和安全性。
根据实际情况选择合适的工具。
通过学习和实践,我们可以更好地利用 query() 和 eval(),提升 Pandas 数据分析的效率,从而更好地应对大数据时代的挑战。