如何使用纯Pandas/Apply方法替代循环实现按时间戳索引的布尔DataFrame拆分并保存CSV
如何用更简洁的Pandas方法替代循环,将布尔型DataFrame每行的True列名按时间戳保存为CSV?
问题背景
我有一个以时间戳为索引的布尔型DataFrame df,结构如下:
import pandas as pd data = { 'x0': [True, True, False], 'x1': [False, False, True], 'x2': [True, True, True] } df = pd.DataFrame(data, index=pd.to_datetime(['2020-01-01', '2020-01-02', '2020-01-03']))
需求是:将每行中值为True的列名保存为单独的CSV文件,文件名用当前行的时间戳(格式为YYYYMMDD)。预期输出3个CSV文件:
20200101.csv:内容为x0,x220200102.csv:内容为x0,x220200103.csv:内容为x1,x2
我已经用for循环结合Pandas实现了功能,但代码有点繁琐(在R里用split和lapply几乎一行搞定),现有代码如下:
import numpy as np for idx, row in df.iterrows(): tmp = row.replace({False: np.nan}).dropna() tmp = pd.DataFrame({"my_col": tmp.index.tolist()}) file_name = ''.join(str(idx.date()).split('-')) tmp.to_csv(f"{file_name}.csv", index=False)
想请教有没有更简洁的实现方式,用纯Pandas、Apply或者类似MapReduce的方法,避免显式的for循环?
解决方案
我来分享几个更简洁的纯Pandas实现方式,完美替代你的循环代码:
方法1:用apply逐行处理(最贴近你的需求风格)
这个方法用apply遍历每一行,直接筛选出True对应的列名并保存,代码非常紧凑:
df.apply( lambda row: pd.Series(row[row].index).to_csv( f"{row.name.strftime('%Y%m%d')}.csv", index=False, header=False ), axis=1 )
细节说明:
row[row]是Pandas里的实用小技巧:布尔Series作为索引时,会直接筛选出值为True的元素,再取index就是我们要的列名row.name.strftime('%Y%m%d')比手动分割字符串更规范可靠,是专门的时间格式转换方法- 把列名列表转成
pd.Series后直接调用to_csv,指定index=False和header=False就能得到无多余内容的CSV
方法2:先重塑数据再分组保存(大数据集更高效)
如果你的DataFrame行数很多,逐行apply可能效率一般,这种先转长表再分组的方式会更高效:
# 把宽表转成长表,只保留值为True的记录 stacked_df = df.stack()[lambda s: s].reset_index() # 按时间戳分组,每组保存为一个CSV for date, group in stacked_df.groupby('level_0'): file_name = date.strftime('%Y%m%d') group['level_1'].to_csv(f"{file_name}.csv", index=False, header=False)
细节说明:
df.stack()会把原列名转成二级索引,生成一个以(timestamp, column_name)为索引、布尔值为内容的Series[lambda s: s]筛选出所有值为True的行- 按原时间戳(
level_0)分组后,每组的level_1就是该行对应的列名,直接保存即可
方法3:极致一行式实现(对标R的split+lapply)
如果你追求像R那样的一行代码风格,直接把方法1压缩成一行就行:
df.apply(lambda r: pd.Series(r[r].index).to_csv(f"{r.name.strftime('%Y%m%d')}.csv", index=False, header=False), axis=1)
内容的提问来源于stack exchange,提问作者PyRsquared
相关产品推荐
相关产品推荐

