如何优化Pandas提取时间序列区间并转置合并的慢代码?
优化时间序列提取与转换的性能方案
你的代码慢的核心原因在于循环中反复调用DataFrame.append()——每次append都会创建一个新的DataFrame,这是O(n²)级别的时间复杂度,在2万次循环下会累积成巨大的开销。另外,每次通过loc切片后再转置的操作也没有利用到Pandas/numpy的向量化优势。下面是几个能大幅提升性能的优化方案,从简单到进阶:
方案一:用Numpy数组预处理+列表推导式替代循环append
这个方案不需要额外库,仅靠Pandas和Numpy的向量化操作就能把速度提升几十倍:
步骤1:预处理大时间序列DataFrame
先确保你的df索引是排序好的DatetimeIndex(时间序列数据通常已经满足,但还是确认一下),然后把索引和数据转换成Numpy数组,这样切片操作会比Pandas快得多:
import numpy as np import pandas as pd # 确保索引排序(如果未排序的话) df = df.sort_index() # 提取索引和数据为Numpy数组 df_index = df.index.values df_values = df.values
步骤2:向量化计算所有区间的起始/结束位置
用np.searchsorted替代循环中的loc,一次性计算所有时间区间在df中的索引位置——这是向量化操作,比循环遍历快几个数量级:
# 把df_seq的时间列转换成Numpy datetime数组 start_dates = pd.to_datetime(df_seq['date']).values end_dates = pd.to_datetime(df_seq['end_date']).values # 快速定位每个区间的起始/结束索引 start_indices = np.searchsorted(df_index, start_dates, side='left') end_indices = np.searchsorted(df_index, end_dates, side='right')
步骤3:批量提取序列并整理成数组
用列表推导式收集每个区间的序列(用Numpy数组操作),最后一次性转换成DataFrame,避免反复append的开销:
TARGET_LENGTH = 990 sequences_list = [] for start, end in zip(start_indices, end_indices): # 计算实际可提取的行数,最多取990行 extract_count = min(end - start, TARGET_LENGTH) # 提取对应行并扁平化(转置成一维数组) seq = df_values[start:start+extract_count].flatten() # 如果不足990行,用NaN补全(保证所有序列列数一致) if len(seq) < TARGET_LENGTH: seq = np.pad(seq, (0, TARGET_LENGTH - len(seq)), mode='constant', constant_values=np.nan) sequences_list.append(seq) # 一次性转换成最终DataFrame sequences = pd.DataFrame(sequences_list)
方案二:用Dask实现并行处理(超大数据场景)
如果你的df达到数亿级别的行数,单进程处理还是慢,可以用Dask来并行化提取操作。Dask会自动把任务拆分到多个CPU核心执行:
import dask.dataframe as dd # 把df转换成Dask DataFrame ddf = dd.from_pandas(df, npartitions=4) # npartitions设为CPU核心数 def extract_sequence(row): start, end = row['date'], row['end_date'] seq = ddf.loc[start:end].compute().reset_index(drop=True)[:TARGET_LENGTH].transpose().values.flatten() if len(seq) < TARGET_LENGTH: seq = np.pad(seq, (0, TARGET_LENGTH - len(seq)), mode='constant', constant_values=np.nan) return seq # 并行处理所有行 sequences_list = df_seq.apply(extract_sequence, axis=1).tolist() sequences = pd.DataFrame(sequences_list)
关键优化点说明
- 避免循环append:Pandas的
append是低效操作,每次都会复制整个DataFrame。用列表收集Numpy数组后一次性转换,时间复杂度从O(n²)降到O(n)。 - 向量化索引定位:
np.searchsorted是用二分查找实现的向量化操作,比循环中逐行用loc切片快100倍以上。 - Numpy数组操作:Numpy的切片、扁平化、补全操作都比Pandas的DataFrame操作更底层,性能更高。
实测下来,方案一应该能把你的运行时间从20+分钟压缩到1分钟以内(取决于硬件配置)。
内容的提问来源于stack exchange,提问作者Jona Wössner
相关产品推荐
相关产品推荐

