如何高效合并多个已排序pd.Series?大数据量场景优化方案
我有两个已排序的pd.Series示例:
A = [1, 3, 5, 7] B = [3, 4, 5, 8, 10]
需要合并去重后得到有序列表:
C = [1, 3, 4, 5, 7, 8, 10]
现有两种实现方式:
- Pandas 拼接去重排序:
A = pd.Series([1, 3, 5, 7], name='col') B = pd.Series([3, 4, 5, 8, 10], name='col') pd.concat([A,B], axis=0).drop_duplicates().sort_values(ascending=True)
- 集合合并转列表排序:
list(set(A).union(set(B))).sort()
实际业务中要处理50个各含10万+字符串的已排序Series,且99%以上元素重叠,需执行50次合并操作。想知道哪种方案效率更高,以及有没有不用Cython/numba的更高效方法。
现有方案的效率短板
Pandas 拼接方案
这种方法完全浪费了「原始Series已排序」的核心前提:concat会先把所有数据堆成一个大Series,drop_duplicates要遍历全量数据去重,最后sort_values又要重新排序——明明输入都是有序的,最后排序完全是做无用功。加上要处理50个10万级数据,concat出来的临时数据量极大,99%都是重复元素,内存和时间开销都非常高。
集合合并方案
集合的union去重速度快,但集合会打乱原始顺序,必须最后重新排序,同样浪费了有序的优势。另外,字符串的哈希计算有额外开销,把Series转成集合还要遍历全量数据,多次转换的成本在10万级数据下会被放大。
结论:这两种方案在你的场景下都不是最优解,核心问题是没利用「输入有序」这个关键条件。
更高效的方案:有序序列归并去重
既然所有输入都是已排序的,我们可以用归并排序的思路,逐个合并序列的同时去重,全程不需要额外排序,时间复杂度是O(N)(N为总元素数),比前两种O(N log N)的方案高效得多。
代码实现(纯Python+Pandas,无需Cython/numba)
import pandas as pd import heapq def merge_sorted_series(series_list): # 生成每个Series的迭代器,用于逐个取元素 iterators = [iter(s) for s in series_list] heap = [] seen = set() # 初始化堆:把每个迭代器的第一个不重复元素加入 for idx, it in enumerate(iterators): try: val = next(it) if val not in seen: heapq.heappush(heap, (val, idx)) seen.add(val) except StopIteration: continue result = [] while heap: current_val, idx = heapq.heappop(heap) result.append(current_val) # 取出当前迭代器的下一个元素,跳过连续重复项(因为原Series有序) it = iterators[idx] try: next_val = next(it) while next_val == current_val: next_val = next(it) # 这个新元素没被处理过的话,加入堆 if next_val not in seen: heapq.heappush(heap, (next_val, idx)) seen.add(next_val) except StopIteration: continue return pd.Series(result, name=series_list[0].name)
优势说明
- 利用有序特性:全程不需要额外排序,避免了O(N log N)的排序开销,这在大数据量下是巨大的性能提升。
- 高效去重:归并过程中直接跳过连续重复元素,同时用
seen集合避免跨序列的重复,99%重叠的场景下,seen集合不会过度膨胀,内存占用低。 - 低内存开销:不需要预先合并所有数据,而是逐个处理元素,减少临时内存占用。
批量合并的优化方式
如果是要执行50次合并(比如每次新增一个Series到已有结果),直接复用上面的函数即可——因为每次合并后的结果也是有序的,每次只需要合并两个有序序列,时间复杂度是O(M+N)(M为已有结果长度,N为新Series长度):
# 假设series_list是包含50个已排序Series的列表 merged_result = series_list[0] for s in series_list[1:]: merged_result = merge_sorted_series([merged_result, s])
性能对比总结
在你的场景(10万+字符串、99%重叠)下:
- Pandas拼接方案:最慢,因为concat、全量去重、重新排序的开销都极大。
- 集合方案:比Pandas方案快,但哈希计算和最后排序的开销依然明显。
- 归并去重方案:速度最快,内存占用最低,完全适配你的场景特性。
内容的提问来源于stack exchange,提问作者cat

