如何用pandas.Series.apply()与lambda函数按条件统计DataFrame列值
问题:统计每月初满足时间区间条件的记录数
这是此前相关帖子的后续内容。
数据集
import pandas as pd import numpy as np from datetime import timedelta import random random.seed(365) # 生成数据 start_date = pd.date_range(start="2015-01-09", end="2022-09-11", freq="6D") end_date = [s + timedelta(days=np.random.exponential(scale=100)) for s in start_date] df = pd.DataFrame( {"start_date": start_date, "end_date": end_date} ) # 随机剔除部分end_date值 df["end_date"] = df["end_date"].sample(frac=0.7).reset_index(drop=True) df["end_date"] = df["end_date"].dt.date.astype("datetime64[ns]")
已准备的每月初日期序列
先生成了数据全周期内每个月第一天的Series:
dates = pd.Series(df["start_date"].dt.to_period("M").sort_values(ascending=True).unique()).dt.start_time
需求
统计DataFrame中同时满足以下两个条件的行数:
df["start_date"]小于dates中每个月的第一天df["end_date"]大于该月的第一天
尝试的代码
我尝试用apply结合lambda或np.logical_and实现,但写法存在问题:
# 筛选出有end_date的记录 inactives = df[df["end_date"].isnull() == False] # 写法1 dates.apply( lambda x: (inactives[inactives["start_date"] < x] & inactives[inactives["cancel_date"] > x]).count() ) # 写法2 dates.apply( lambda x: np.logical_and( inactives[inactives["start_date"] < x, inactives[inactives["cancel_date"] > x]] ).sum())
期望输出
| month_first | count |
|---|---|
| 2015-01-01 | 10 |
| 2015-02-01 | 25 |
| 2015-03-01 | 45 |
解决方法
你的尝试存在两处问题:一是误写了列名cancel_date(数据集实际列名为end_date),二是布尔索引的拼接逻辑错误。以下是两种可行的实现方式:
方法1:优化apply写法
# 筛选出有有效end_date的记录 inactives = df.dropna(subset=["end_date"]) # 对每个月初日期统计符合条件的记录数 count_result = dates.apply(lambda x: inactives[(inactives["start_date"] < x) & (inactives["end_date"] > x)].shape[0]) # 整理为期望的DataFrame格式 result_df = count_result.reset_index(drop=True).to_frame(name="count") result_df.insert(0, "month_first", dates.dt.date)
方法2:向量化操作(大数据量更高效)
如果数据集规模较大,apply的循环效率偏低,可以用向量化广播实现:
inactives = df.dropna(subset=["end_date"]) # 将日期转为可广播的数组,批量比较 start_mask = inactives["start_date"].values[:, None] < dates.values end_mask = inactives["end_date"].values[:, None] > dates.values # 统计每个月初对应的符合条件的记录数 counts = (start_mask & end_mask).sum(axis=0) # 生成结果DataFrame result_df = pd.DataFrame({"month_first": dates.dt.date, "count": counts})
运行后即可得到符合预期格式的输出。
内容的提问来源于stack exchange,提问作者JoMcGee
相关产品推荐
相关产品推荐

