如何无需iterrows高效遍历DataFrame单元格并应用指定函数?
无需iterrows遍历DataFrame单元格的高效方案
当然有!iterrows()因为每次迭代都会把行转成Series,带来不少额外开销,在数据量大的时候效率很低。针对你的需求,我们有几种更高效的方法,既能满足传递列名(dates)、行索引(name)和单元格值(value)给foo函数,又能保持原DataFrame的结构。
方法1:嵌套apply(最直观,逐元素处理)
我们可以先对每一列应用处理函数,在列内部再对每个单元格调用foo——此时列名就是dates,单元格的行索引就是name,单元格值就是value:
import pandas as pd def foo(dates, name, value): return black_box_function(dates, name, value) # 外层apply遍历每一列,内层apply遍历列中的每个单元格 result_df = df.apply(lambda col: col.apply(lambda val: foo(col.name, val.name, val)))
这个方法的优势是逻辑清晰,不需要额外的数据结构转换,而且apply底层是基于C的循环,比纯Python的iterrows快得多。
方法2:stack()+apply()+unstack()(多级索引处理)
把DataFrame转换成多级索引的Series,这样每个元素的索引会包含原行索引和列名,处理后再转回DataFrame:
# 将DataFrame转为多级索引Series(行索引+列名作为索引,单元格值为值) stacked_series = df.stack() # 应用foo函数:x.name[1]是列名(dates),x.name[0]是行索引(name),x是单元格值 processed_series = stacked_series.apply(lambda x: foo(x.name[1], x.name[0], x)) # 转回原DataFrame结构 result_df = processed_series.unstack()
这个方法和嵌套apply效率相近,适合习惯处理Series的场景。
方法3:完全向量化(最快,需函数支持批量输入)
如果你的black_box_function支持向量化输入(比如可以接受数组作为参数),那这是效率最高的方案——完全避免Python循环,直接用numpy广播生成对应参数数组:
import numpy as np # 生成dates数组:每一行重复列名 dates_array = np.tile(df.columns, (len(df), 1)) # 生成name数组:每一列重复行索引 name_array = np.tile(df.index.values.reshape(-1, 1), (1, len(df.columns))) # 获取原DataFrame的值数组 value_array = df.values # 直接批量调用函数 result_array = black_box_function(dates_array, name_array, value_array) # 将结果转回DataFrame result_df = pd.DataFrame(result_array, index=df.index, columns=df.columns)
这种方法的速度是前两种的数倍甚至数十倍,前提是black_box_function能处理数组输入(如果原来的函数是逐元素的,你可以用np.vectorize包装一下,但要注意np.vectorize本质还是循环,只是语法上更像向量化)。
为什么你的原代码无法运行?
你之前尝试的df.apply(foo(df['index'], df['column']))有两个问题:
df['index']和df['column']不是获取索引/列名的正确方式——要获取列名应该在apply的函数内部通过col.name获取,行索引通过单元格的val.name获取;apply需要传入一个函数对象,而你直接调用了foo,相当于把foo的返回值传给了apply,这显然不符合要求。
内容的提问来源于stack exchange,提问作者boboo
相关产品推荐
相关产品推荐

