Pandas循环删除DataFrame行后,如何调整迭代范围避免索引越界?
解决Pandas循环遍历并删除行时的索引越界问题
问题场景
初始有一个含12675行的Pandas DataFrame,其info()输出如下:
Proyectonevera2.info() <class 'pandas.core.frame.DataFrame'> Int64Index: 12689 entries, 3 to 12683 Data columns (total 60 columns):
编写了嵌套循环代码,用于遍历DataFrame、修改数据并删除指定行:
for i in range(0,len(Proyectonevera2)-1): if Proyectonevera2.loc[i,'Clave'] == "P": CodIng = Proyectonevera2.loc[i,'Ingrediente'] NuevoInv_Ing = Proyectonevera2.loc[i,'Nuevo Inventario'] for j in range(0,len(Proyectonevera2)-1): if Proyectonevera2.loc[j,'Clave'] == "N": Codprod = Proyectonevera2.loc[j,'CodProducto'] if Proyectonevera2.loc[j,'Ingrediente'] == CodIng: Proyectonevera2.loc[j,'Inv_Ing'] = NuevoInv_Ing if Proyectonevera2.loc[j,'Cant Ingr SUM'] !=0 and Proyectonevera2.loc[j,'Cant Prod SUM']!=0: Proyectonevera2.loc[j,'Cantidad producir']=Proyectonevera2.loc[j,'Inv_Ing']*Proyectonevera2.loc[j,'Cant Prod SUM']/Proyectonevera2.loc[j,'Cant Ingr SUM'] dfc = Proyectonevera2.groupby('CodProducto')['Cantidad producir'] Proyectonevera2.assign(min=dfc.transform(min)) Proyectonevera2.loc[j,'Consumo Agosto'] = Proyectonevera2.loc[j,'min']*Proyectonevera2.loc[j,'Cant Ingr SUM']/Proyectonevera2.loc[j,'Cant Prod SUM'] Proyectonevera2.loc[j,'Nuevo Inventario'] = Proyectonevera2.loc[j,'Inv_Ing']-Proyectonevera2.loc[j,'Consumo Agosto'] Proyectonevera2= Proyectonevera2.drop(Proyectonevera2[Proyectonevera2['Clave']=="P"].index) Proyectonevera2 = Proyectonevera2.reset_index(drop=True)
执行后触发索引越界报错:
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) C:\ProgramData\Anaconda3\lib\site-packages\pandas\core\indexes\range.py in get_loc(self, key, method, tolerance) 384 try: --> 385 return self._range.index(new_key) 386 except ValueError as err: ValueError: 12675 is not in range
问题核心:循环基于DataFrame初始长度,但删除行后长度动态缩小,导致后续循环索引超出有效范围。
解决方案
临时调整:基于固定初始索引遍历
如果一定要保留循环逻辑,不要依赖动态变化的len(Proyectonevera2),而是提前获取初始索引列表,遍历固定列表并跳过已删除的索引:
# 提前获取所有初始索引并转为列表(避免迭代时索引动态变化) initial_indices = list(Proyectonevera2.index) for idx in initial_indices: # 跳过已被删除的索引 if idx not in Proyectonevera2.index: continue if Proyectonevera2.loc[idx, 'Clave'] == "P": CodIng = Proyectonevera2.loc[idx, 'Ingrediente'] NuevoInv_Ing = Proyectonevera2.loc[idx, 'Nuevo Inventario'] # 提前获取所有Clave为"N"的初始索引 n_indices = list(Proyectonevera2[Proyectonevera2['Clave']=="N"].index) for j_idx in n_indices: if j_idx not in Proyectonevera2.index: continue if Proyectonevera2.loc[j_idx, 'Ingrediente'] == CodIng: Proyectonevera2.loc[j_idx, 'Inv_Ing'] = NuevoInv_Ing if Proyectonevera2.loc[j_idx, 'Cant Ingr SUM'] != 0 and Proyectonevera2.loc[j_idx, 'Cant Prod SUM'] != 0: Proyectonevera2.loc[j_idx, 'Cantidad producir'] = ( Proyectonevera2.loc[j_idx, 'Inv_Ing'] * Proyectonevera2.loc[j_idx, 'Cant Prod SUM'] / Proyectonevera2.loc[j_idx, 'Cant Ingr SUM'] ) # 修正assign的用法:直接赋值列而非返回新DataFrame Proyectonevera2['min'] = Proyectonevera2.groupby('CodProducto')['Cantidad producir'].transform('min') Proyectonevera2.loc[j_idx, 'Consumo Agosto'] = ( Proyectonevera2.loc[j_idx, 'min'] * Proyectonevera2.loc[j_idx, 'Cant Ingr SUM'] / Proyectonevera2.loc[j_idx, 'Cant Prod SUM'] ) Proyectonevera2.loc[j_idx, 'Nuevo Inventario'] = ( Proyectonevera2.loc[j_idx, 'Inv_Ing'] - Proyectonevera2.loc[j_idx, 'Consumo Agosto'] ) # 所有处理完成后统一删除P类行,避免循环内频繁修改DataFrame Proyectonevera2 = Proyectonevera2.drop(Proyectonevera2[Proyectonevera2['Clave']=="P"].index).reset_index(drop=True)
推荐方案:使用Pandas原生向量式操作
Pandas的核心优势是向量式运算,完全不需要嵌套循环,效率更高且彻底避免索引问题:
# 1. 提取P类的Ingrediente与Nuevo Inventario的映射关系 p_mapping = Proyectonevera2[Proyectonevera2['Clave']=="P"].set_index('Ingrediente')['Nuevo Inventario'].to_dict() # 2. 批量更新N类的Inv_Ing Proyectonevera2.loc[Proyectonevera2['Clave']=="N", 'Inv_Ing'] = Proyectonevera2.loc[Proyectonevera2['Clave']=="N", 'Ingrediente'].map(p_mapping) # 3. 定义有效计算的掩码(过滤非零值和N类行) mask = (Proyectonevera2['Cant Ingr SUM'] != 0) & (Proyectonevera2['Cant Prod SUM'] != 0) & (Proyectonevera2['Clave']=="N") # 4. 批量计算Cantidad producir Proyectonevera2.loc[mask, 'Cantidad producir'] = ( Proyectonevera2.loc[mask, 'Inv_Ing'] * Proyectonevera2.loc[mask, 'Cant Prod SUM'] / Proyectonevera2.loc[mask, 'Cant Ingr SUM'] ) # 5. 分组计算min值 Proyectonevera2['min'] = Proyectonevera2.groupby('CodProducto')['Cantidad producir'].transform('min') # 6. 批量计算Consumo Agosto和Nuevo Inventario Proyectonevera2.loc[mask, 'Consumo Agosto'] = ( Proyectonevera2.loc[mask, 'min'] * Proyectonevera2.loc[mask, 'Cant Ingr SUM'] / Proyectonevera2.loc[mask, 'Cant Prod SUM'] ) Proyectonevera2.loc[mask, 'Nuevo Inventario'] = Proyectonevera2.loc[mask, 'Inv_Ing'] - Proyectonevera2.loc[mask, 'Consumo Agosto'] # 7. 最后统一删除P类行 Proyectonevera2 = Proyectonevera2[Proyectonevera2['Clave']!="P"].reset_index(drop=True)
关键优化说明
- 避免循环内删除行:统一在所有数据处理完成后删除指定行,减少DataFrame的动态变更
- 替换嵌套循环为向量操作:运算速度提升数倍至数十倍,彻底规避索引越界风险
- 修正
assign用法:assign返回新DataFrame,不会修改原数据,需直接对列赋值
内容的提问来源于stack exchange,提问作者Aleja Gallo
相关产品推荐
相关产品推荐

