pandas提高查询效率的多种方法

使用 Pandas 提高查询效率的多种方法

Pandas 是 Python 中强大的数据处理库,但在处理大数据集时可能会遇到性能问题。以下是提高 Pandas 查询效率的多种方法:

1. 选择合适的数据类型

# 将对象类型转换为更高效的类型
df['category_col'] = df['category_col'].astype('category')
df['int_col'] = df['int_col'].astype('int32')  # 而不是默认的 int64
df['float_col'] = df['float_col'].astype('float32')  # 而不是默认的 float64

# 使用日期时间类型
df['date_col'] = pd.to_datetime(df['date_col'])

2. 使用向量化操作

# 避免使用 apply() 和循环,使用向量化操作
# 慢
df['new_col'] = df['col1'].apply(lambda x: x * 2)

# 快
df['new_col'] = df['col1'] * 2

# 更复杂的向量化操作
df['result'] = np.where(df['col1'] > df['col2'], df['col1'], df['col2'])

3. 使用查询方法优化

# 使用 query() 方法进行条件筛选(语法更简洁,有时更快)
result = df.query('col1 > 100 & col2 < 50')

# 使用 eval() 进行表达式求值(对于复杂表达式更高效)
df.eval('result = col1 + col2 * col3', inplace=True)

# 使用 isin() 而不是多个 OR 条件
values = [1, 2, 3, 4, 5]
result = df[df['col'].isin(values)]

# 使用 between() 进行范围查询
result = df[df['col'].between(10, 100)]

4. 索引优化

# 为常用查询列设置索引
df.set_index('frequently_queried_column', inplace=True)

# 使用多级索引
df.set_index(['col1', 'col2'], inplace=True)

# 使用 sort_index() 提高索引查询速度
df.sort_index(inplace=True)

# 使用 loc 和 iloc 进行高效索引
result = df.loc[df.index > '2023-01-01']

5. 内存优化

# 检查内存使用情况
print(df.memory_usage(deep=True))

# 使用更节省内存的数据类型
df = df.astype({col: 'category' for col in df.select_dtypes(include='object').columns})

# 使用稀疏数据结构(对于包含大量零值的数据)
df = df.astype(pd.SparseDtype("float", 0))

6. 分批处理大数据集

# 使用 chunksize 分批读取大数据文件
chunk_size = 10000
for chunk in pd.read_csv('large_file.csv', chunksize=chunk_size):
    process_chunk(chunk)

# 或者手动分批处理
batch_size = 5000
for i in range(0, len(df), batch_size):
    batch = df.iloc[i:i+batch_size]
    process_batch(batch)

7. 使用更高效的数据存储格式

# 使用 Parquet 格式(列式存储,压缩率高)
df.to_parquet('data.parquet')
df = pd.read_parquet('data.parquet')

# 使用 Feather 格式(快速读写)
df.to_feather('data.feather')
df = pd.read_feather('data.feather')

# 使用 HDF5 格式(适合大型科学数据集)
df.to_hdf('data.h5', key='df', mode='w')
df = pd.read_hdf('data.h5', key='df')

8. 使用并行处理

# 使用 pandarallel(简化并行处理)
from pandarallel import pandarallel
pandarallel.initialize()

# 并行 apply
df['new_col'] = df['col'].parallel_apply(lambda x: process_value(x))

# 使用 Dask(处理比内存大的数据集)
import dask.dataframe as dd
ddf = dd.from_pandas(df, npartitions=4)
result = ddf[dd['col'] > 100].compute()

9. 避免常见性能陷阱

# 避免链式赋值(会创建副本)
# 不好
df[df['col'] > 100]['new_col'] = 1

# 好
df.loc[df['col'] > 100, 'new_col'] = 1

# 避免在循环中追加数据
# 不好
results = []
for i in range(1000):
    results.append(process_data(i))
result_df = pd.DataFrame(results)

# 好
# 先处理所有数据,然后一次性创建 DataFrame

# 使用 copy() 谨慎(只有在必要时才创建副本)
df_copy = df.copy()  # 这会创建完整副本,消耗内存

10. 使用内置的聚合函数

# 使用内置聚合函数而不是自定义函数
# 慢
df.groupby('group_col')['value_col'].apply(lambda x: x.sum())

# 快
df.groupby('group_col')['value_col'].sum()

# 使用 agg() 进行多个聚合
df.groupby('group_col').agg({
    'col1': ['mean', 'min', 'max'],
    'col2': 'sum'
})

11. 使用 Cython 或 Numba 加速

对于极其性能关键的代码部分,可以考虑使用 Cython 或 Numba 进行加速:

# 使用 Numba 加速数值计算
from numba import jit

@jit(nopython=True)
def calculate_value(x, y):
    # 复杂的数值计算
    return x * y + x**2 - y**2

df['result'] = calculate_value(df['col1'].values, df['col2'].values)

12. 监控和优化性能

# 使用 %timeit 魔术命令测试代码性能
%timeit df[df['col'] > 100]

# 使用 line_profiler 分析代码行级性能
# 安装: pip install line_profiler
# 使用: %load_ext line_profiler
# 然后: %lprun -f function_name function_call()

# 使用 memory_profiler 分析内存使用
# 安装: pip install memory_profiler
# 使用: %load_ext memory_profiler
# 然后: %mprun -f function_name function_call()

实际应用示例

结合您之前的代码,这里是一个优化后的版本:

def optimized_customer_flows(self, data_df):
    # 填充缺失值并优化数据类型
    data_df_fill = data_df.fillna(0)
    
    # 优化数据类型
    for col in data_df_fill.select_dtypes(include=['object']).columns:
        data_df_fill[col] = data_df_fill[col].astype('category')
    
    # 定义批次大小
    batch_size = 3000
    
    # 计算总批次数
    total_rows = len(data_df_fill)
    num_batches = (total_rows + batch_size - 1) // batch_size
    
    print(f"客户流量总数据量: {total_rows}, 将分为 {num_batches} 批处理")
    
    # 预先计算一些映射关系,避免在循环中重复计算
    site_map = {site: column_target(self.get_site(site), site) for site in data_df_fill["来源地"].unique()}
    product_map = {product: column_target(self.get_product(product), product) for product in data_df_fill["产品"].unique()}
    customer_map = {customer: column_target(self.get_customer(customer), customer) for customer in data_df_fill["客户"].unique()}
    period_map = {period: column_target(self.get_period(period), period) for period in pd.concat([data_df_fill["到达周期"], data_df_fill["发货周期"]]).unique()}
    
    # 分批处理数据
    for batch_num in range(num_batches):
        # 计算当前批次的起始和结束索引
        start_idx = batch_num * batch_size
        end_idx = min((batch_num + 1) * batch_size, total_rows)
        
        # 获取当前批次的数据
        batch_df = data_df_fill.iloc[start_idx:end_idx]
        
        # 使用列表推导式提高处理效率
        flows_ls = [
            {
                "source": site_map[row["来源地"]],
                "product": product_map[row["产品"]],
                'customer': customer_map[row["客户"]],
                "mode": column_target(str(ObjectId()), row["运输模式"]),
                "arrivingPeriodName": period_map[row["到达周期"]],
                "departingPeriodName": period_map[row["发货周期"]],
                "arrivingPeriod": period_map[row["到达周期"]],
                "departingPeriod": period_map[row["发货周期"]],
                "flowUnits": compute_column(row["流量"]),
                "flowWeight": compute_column(row["流量重量"]),
                "flowCubic": compute_column(row["流量体积"]),
                "serviceHours": compute_column(row["服务小时数"]),
                "serviceDistance": compute_column(row["服务距离"]),
                "transportationPolicyCost": compute_column(row["运输成本"]),
                "inTransitInventory": compute_column(row["在途库存"]),
                "inTransitInventoryHoldingCost": compute_column(row["在途库存持有成本"]),
                "co2": 0,
                "co2Cost": 0,
                "totalCost": compute_column(row["总成本"]),
                "totalTransportationCost": compute_column(row["运输总成本"]),
                "outboundWarehousingPolicyCost": compute_column(0),
                "sourcingPolicyCost": compute_column(0),
                "dutyCost": compute_column(0),
                "leadTimeCost": compute_column(0),
                "totalSourcingCost": compute_column(1),
                "totalOutboundWarehousingCost": compute_column(0),
                **self.add_fields({}, self.get_scenario(row["场景"]), row["场景"])
            }
            for _, row in batch_df.iterrows()
        ]
        
        # 写入当前批次数据
        table_name = "output_customer_flows"
        self.handle_data(table_name=table_name, data=flows_ls, conn=self.data.db)
        
        print(f"已处理第 {batch_num + 1}/{num_batches} 批数据,本批处理 {len(flows_ls)} 条记录")
    
    print(f"客户流量处理完成,总共处理了 {total_rows} 条记录")

这些方法可以根据您的具体数据和查询模式进行组合使用,以最大程度地提高 Pandas 的查询效率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值