You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用df.drop(idx)出现内存不足错误,求删除记录的替代方案

批量删除符合条件的DataFrame记录的优化方案

问题场景

我有一个包含536000+条记录的DataFrame df_clean,需要筛选并删除满足以下条件的交易记录:

  • 按CustomerID、StockCode、Quantity的绝对值分组
  • 分组内Quantity绝对值的记录数为偶数,且分组内Quantity总和为0

原尝试方案及问题

方案1:groupby+filter+drop

先筛选出目标记录,再通过索引删除:

df_pairs = df_clean.groupby([df_clean.CustomerID, df_clean.StockCode, df_clean.Quantity.abs()]).filter(lambda x: (len(x.Quantity.abs()) % 2 == 0) and (x.Quantity.sum() == 0))
idx = df_pairs.index
df_clean.drop(idx)

结果:len(df_pairs)显示有4016条目标记录,但执行drop时耗时极长,最终触发内存不足错误导致页面崩溃,重启内核、电脑都无法解决。

方案2:loc+~反向筛选

尝试直接用布尔索引反向保留记录:

df_clean = df_clean.loc[~((df_clean.groupby([df_clean.CustomerID, df_clean.StockCode, df_clean.Quantity.abs()]).filter(lambda x: (len(x.Quantity.abs()) % 2 == 0) and (x.Quantity.sum() == 0))))]

结果触发类型错误:

TypeError                                 Traceback (most recent call last)
C:\Users\MARTIN~1\AppData\Local\Temp/ipykernel_7792/227912236.py in <module>
----> 1 df_clean = df_clean.loc[~((df_clean.groupby([df_clean.CustomerID, df_clean.StockCode, df_clean.Quantity.abs()]).filter(lambda x: (len(x.Quantity.abs()) % 2 == 0) and (x.Quantity.sum() == 0))))]

~\anaconda3\lib\site-packages\pandas\core\generic.py in __invert__(self)
   1530             return self
   1531 
-> 1532         new_data = self._mgr.apply(operator.invert)
   1533         return self._constructor(new_data).__finalize__(self, method="__invert__")
   1534 

~\anaconda3\lib\site-packages\pandas\core\internals\managers.py in apply(self, f, align_keys, ignore_failures, **kwargs)
    323             try:
    324                 if callable(f):
--> 325                     applied = b.apply(f, **kwargs)
    326                 else:
    327                     applied = getattr(b, f)(**kwargs)

~\anaconda3\lib\site-packages\pandas\core\internals\blocks.py in apply(self, func, **kwargs)
    379         """
    380         with np.errstate(all="ignore"):
--> 381             result = func(self.values, **kwargs)
    382 
    383         return self._split_op_result(result)

TypeError: bad operand type for unary ~: 'DatetimeArray'

注:数据集为销售交易历史(每条记录对应发票行),无法使用isin()或pd.concat后drop_duplicates()的方法,数据集示例如下:

InvoiceNoStockCodeDescriptionQuantityInvoiceDateUnitPriceCustomerIDTotalSales
53636585123AWHITE HANGING HEART T-LIGHT HOLDER62018-11-29 08:26:002.551785015.30
53636571053WHITE METAL LANTERN62018-11-29 08:26:003.391785020.34
53636584406BCREAM CUPID HEARTS COAT HANGER82018-11-29 08:26:002.751785022.00
53636584029GKNITTED UNION FLAG HOT WATER BOTTLE62018-11-29 08:26:003.391785020.34
53636584029ERED WOOLLY HOTTIE WHITE HEART.62018-11-29 08:26:003.391785020.34

优化解决方案

方案1:用transform生成布尔标记(最省内存)

避免生成中间DataFrame,直接用transform给每条记录标记是否属于要删除的分组:

# 定义分组键
group_keys = ['CustomerID', 'StockCode', df_clean['Quantity'].abs()]

# 用transform生成每条记录的条件标记
cond1 = df_clean.groupby(group_keys)['Quantity'].transform(lambda x: len(x) % 2 == 0)
cond2 = df_clean.groupby(group_keys)['Quantity'].transform(lambda x: x.sum() == 0)

# 反向筛选保留非目标记录
df_clean = df_clean.loc[~(cond1 & cond2)]

原理:transform返回和原DataFrame长度一致的Series,直接生成布尔索引,无需创建中间大对象,内存占用极低。

方案2:先筛选分组再标记记录

先在分组层面筛选出符合条件的组,再匹配原数据中的对应记录:

# 计算每个分组的统计值:记录数、Quantity总和
group_stats = df_clean.groupby(['CustomerID', 'StockCode', df_clean['Quantity'].abs()])['Quantity'].agg(['count', 'sum'])

# 筛选出符合条件的分组
target_groups = group_stats[(group_stats['count'] % 2 == 0) & (group_stats['sum'] == 0)].index

# 标记原数据中属于目标分组的记录
df_clean['is_target'] = df_clean.apply(
    lambda row: (row['CustomerID'], row['StockCode'], abs(row['Quantity'])) in target_groups,
    axis=1
)

# 过滤并删除临时标记列
df_clean = df_clean[~df_clean['is_target']].drop('is_target', axis=1)

原理:先在分组层面完成筛选,再匹配原数据,适合分组数量远小于总记录数的场景。

方案3:分块处理(极端内存不足时用)

如果内存实在紧张,把DataFrame按CustomerID拆分成小块处理,逐块筛选后合并:

import pandas as pd

chunks = []
# 按CustomerID分块,降低单块内存负载
for _, chunk in df_clean.groupby('CustomerID'):
    group_keys = ['CustomerID', 'StockCode', chunk['Quantity'].abs()]
    cond1 = chunk.groupby(group_keys)['Quantity'].transform(lambda x: len(x) % 2 == 0)
    cond2 = chunk.groupby(group_keys)['Quantity'].transform(lambda x: x.sum() == 0)
    chunks.append(chunk[~(cond1 & cond2)])

# 合并所有处理后的块
df_clean = pd.concat(chunks, ignore_index=True)

原理:把大数据集拆分成小批量处理,避免一次性加载全量数据占用过多内存。

内容的提问来源于stack exchange,提问作者DorothyK

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 20:36:27