如何根据另一DataFrame的索引对DataFrame多列按指定行范围切片?
寻找Pandas中按列保留指定时间切片范围值的最优方法
我正在寻找执行以下操作的最优/最具Python风格的方法(暂未在Stack Overflow上找到类似问题):
对于以日期为索引的df1的每一列,保留df2中定义的(时间)切片范围内的值(df2的keys列对应df1的列名),其余位置填充NaN。
示例数据
df1(日期为索引):
A B C 01/01 10 100 1 02/01 20 200 2 03/01 30 300 3 04/01 40 400 4 05/01 50 500 5 06/01 60 600 5
df2(定义各列的时间范围):
keys start end 0 A 02/01 04/01 1 B 04/01 06/01 2 C 01/01 04/01
期望处理结果:
A B C 01/01 NaN NaN 1 02/01 20 NaN 2 03/01 30 NaN 3 04/01 40 400 4 05/01 NaN 500 NaN 06/01 NaN 600 NaN
目前我找到的方法是遍历df1的每一列,结合重索引后的df2使用loc,但我不太喜欢在Pandas中使用循环的方式。请问还有其他更优雅的方法吗?谢谢!
解决方案
这里有几种无需显式循环的Pandas风格实现方式,完全贴合Pythonic的代码习惯:
方法1:利用广播创建掩码(高效首选)
先把时间格式统一转为datetime(避免字符串比较的潜在问题),然后通过Pandas的广播机制直接生成布尔掩码,一次性完成过滤:
import pandas as pd # 转换时间格式(如果原始数据是字符串类型) df1.index = pd.to_datetime(df1.index, format="%d/%m") df2[["start", "end"]] = df2[["start", "end"]].apply(pd.to_datetime, format="%d/%m") # 生成列级时间范围掩码:索引是否在对应列的start和end之间 mask = (df1.index >= df2.set_index("keys")["start"].values) & \ (df1.index <= df2.set_index("keys")["end"].values) # 应用掩码,不符合范围的值设为NaN result = df1.where(mask)
这种方法完全利用Pandas的向量运算特性,没有循环,效率最高,适合处理大规模数据。
方法2:用apply实现列级逻辑(可读性强)
如果追求代码的直观性,可以用df.apply对每列单独处理,逻辑清晰易懂:
# 先把df2转为字典,方便快速查询各列的时间范围 range_dict = df2.set_index("keys")[["start", "end"]].to_dict("index") # 对每列应用过滤逻辑:仅保留索引在指定时间范围内的值 result = df1.apply(lambda col: col.where( (col.index >= range_dict[col.name]["start"]) & (col.index <= range_dict[col.name]["end"]) ))
这里的apply是Pandas内置的列级遍历,比手动写for循环更符合Pandas的设计思路,代码也更简洁。
方法3:长格式合并重塑(灵活拓展)
如果后续需要添加更多关联逻辑,可以先把df1转成长格式,和df2合并后过滤,再转回宽格式:
# 将df1转为长格式 df1_long = df1.stack().reset_index(name="value") df1_long.columns = ["date", "key", "value"] # 合并df2的时间范围数据 merged = pd.merge(df1_long, df2, left_on="key", right_on="keys") # 过滤出时间范围内的数据 filtered = merged[(merged["date"] >= merged["start"]) & (merged["date"] <= merged["end"])] # 转回宽格式得到结果 result = filtered.pivot(index="date", columns="key", values="value")
这种方法的优势是灵活,当需要加入更多列的关联条件时,调整起来会更方便。
内容的提问来源于stack exchange,提问作者DrGorilla.eth
相关产品推荐
相关产品推荐

