You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python中分组DataFrame后按条件为每行计算自定义统计值的方法

Pandas实现按ID和年份分组计算条件化的均值与中位数

问题背景

现有如下Pandas DataFrame:

import pandas as pd
import numpy as np

df = pd.DataFrame(
         {"CALDT": ["1980-01-31", "1980-02-28", "1980-03-31",
                    "1980-01-31", "1980-02-28", "1980-03-31",
                    "1980-01-31"],
          "ID": [1, 1, 1, 
                 2, 2, 2,
                 3],
          "Return": [0.02, 0.05, 0.10,
                     0.05, -0.02, 0.03,
                     -0.03]
          })

df['Year'] = pd.to_datetime(df['CALDT']).dt.year

需求:按ID和Year分组,若分组内的存活月份(即唯一CALDT的数量)≥2,则基于Return列计算均值和中位数并乘以12,将结果赋值给该分组的每一行;否则赋值为NaN。

预期输出如下:

df_new = pd.DataFrame(
    {"CALDT": ["1980-01-31", "1980-02-28", "1980-03-31",
               "1980-01-31", "1980-02-28", "1980-03-31",
               "1980-01-31"],
     "Year": [1980, 1980, 1980,
              1980, 1980, 1980,
              1980],
     "ID": [1, 1, 1, 
            2, 2, 2,
            3],
     "Return": [0.02, 0.05, 0.10,
                0.05, -0.02, 0.03,
                -0.03],
     "Mean_Return": [0.68, 0.68, 0.68,
                     0.24, 0.24, 0.24,
                     np.nan],
     "Median_Return": [0.60, 0.60, 0.60,
                       0.36, 0.36, 0.36,
                       np.nan]
     })

在R中可以通过tidyverse的group_by轻松实现:

df = df %>% 
  mutate(Year = year(CALDT)) %>% 
  group_by(CRSP_FUNDNO, Year) %>% 
  mutate(months_alive = length(unique(CALDT))) %>% 
  mutate(mean = case_when(months_alive >= 2 ~ mean(Return)*12,
                          .default = NA)) %>% 
  mutate(median = case_when(months_alive >= 2 ~ median(Return)*12,
                            .default = NA))

Python实现方案

以下是对应Pandas的实现代码,逻辑与R版本完全对齐:

步骤1:计算每个分组的存活月份数

通过groupby.transform将分组内唯一CALDT的数量广播到每一行:

# 计算每个ID-年份分组的存活月份数
df['months_alive'] = df.groupby(['ID', 'Year'])['CALDT'].transform(lambda x: x.nunique())

步骤2:计算条件化的均值与中位数

同样使用transform计算分组内的均值和中位数并乘以12,再通过where方法筛选满足条件的结果:

# 计算年度化均值,仅保留存活月份≥2的分组结果
df['Mean_Return'] = df.groupby(['ID', 'Year'])['Return'].transform(lambda x: x.mean() * 12).where(df['months_alive'] >= 2)

# 计算年度化中位数,仅保留存活月份≥2的分组结果
df['Median_Return'] = df.groupby(['ID', 'Year'])['Return'].transform(lambda x: x.median() * 12).where(df['months_alive'] >= 2)

步骤3:整理结果(可选)

如果不需要中间列months_alive,可以直接移除:

# 移除中间列,得到最终结果
df_new = df.drop('months_alive', axis=1)

执行上述代码后,df_new的结果将与预期输出完全一致。

内容的提问来源于stack exchange,提问作者MF1992

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 04:56:01