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

Julia DataFrame如何不显式指定变量生成分组汇总统计量

问题场景

给定一个包含多个数值变量、最后一列为类别列cl的Julia DataFrame,示例输入如下:

julia> df
5×3 DataFrame
 Row │ v1       v2     cl    
     │ Int64?   Int64  Int64 
─────┼───────────────────────
   1 │      10      1      2
   2 │      20      2      2
   3 │     300     10      1
   4 │     400     20      1
   5 │ missing     30      1

需要生成按类别分组的汇总统计DataFrame,要求类别作为列、各变量及对应统计指标作为行,期望输出格式如下:

julia> dfByCl
11×3 DataFrame
 Row │ var      cl_1       cl_2      
     │ String   Float64?   Float64?  
─────┼───────────────────────────────
   1 │ nrow       3.0       2.0
   2 │ v1_mean  350.0      15.0
   3 │ v1_std    70.7107    7.07107
   4 │ v1_lb    252.002     5.20018
   5 │ v1_ub    447.998    24.7998
   6 │ v1_nm      2.0       2.0
   7 │ v2_mean   20.0       1.5
   8 │ v2_std    10.0       0.707107
   9 │ v2_lb      8.68414   0.520018
  10 │ v2_ub     31.3159    2.47998
  11 │ v2_nm      3.0       2.0

要求代码中不需要显式枚举所有变量名,实现比原有示例代码更简洁优雅。

原有示例代码:

using Statistics, DataFrames, Distributions

meansk(data) = mean(skipmissing(data))
stdsk(data)  = std(skipmissing(data))
nm(data)     = sum(.! ismissing.(data))
ci(data::AbstractVector,α=0.05) = meansk(data) - quantile(Normal(),1-α/2)*stdsk(data)/sqrt(nm(data)), meansk(data) + quantile(Normal(),1-α/2)*stdsk(data)/sqrt(nm(data))
cilb(data) = ci(data)[1]
ciub(data) = ci(data)[2]

df = DataFrame(v1=[10,20,300,400,missing],v2=[1,2,10,20,30],cl=[2,2,1,1,1])

dfByCl_w = combine(groupby(df,["cl"]),
   nrow,
   names(df) .=> meansk .=> col -> col * "_mean",
   names(df) .=> stdsk  .=> col -> col * "_std",
   names(df) .=> cilb   .=> col -> col * "_lb",
   names(df) .=> ciub   .=> col -> col * "_ub",
   names(df) .=> nm     .=> col -> col * "_nm",
)
orderedNames = vcat("cl","nrow",[ ["$(n)_mean", "$(n)_std", "$(n)_lb", "$(n)_ub", "$(n)_nm"] for n in names(df)[1:end-1]]...)
dfByCl_w     = dfByCl_w[:, orderedNames]
toStack      = vcat("nrow",[ ["$(n)_mean", "$(n)_std", "$(n)_lb", "$(n)_ub", "$(n)_nm"] for n in names(df)[1:end-1]]...)
dfByCl_l     = stack(dfByCl_w,toStack)
dfByCl       = unstack(dfByCl_l,"cl","value")
rename!(dfByCl,vcat("var",["cl_$(c)" for c in unique(dfByCl_w.cl)]))
更简洁的实现方案

通过提前定义统计配置列表、自动识别非类别列的方式消除重复代码,不需要手动指定列顺序、重复构造列名列表,后续新增/修改统计指标、新增数值变量时不需要改动核心逻辑:

using Statistics, DataFrames, Distributions

# 基础统计函数
meansk(x) = mean(skipmissing(x))
stdsk(x) = std(skipmissing(x))
nm(x) = count(!ismissing, x)
# 置信区间上下限
function cilb(x, α=0.05)
    m, s, n = meansk(x), stdsk(x), nm(x)
    m - quantile(Normal(), 1-α/2) * s / sqrt(n)
end
function ciub(x, α=0.05)
    m, s, n = meansk(x), stdsk(x), nm(x)
    m + quantile(Normal(), 1-α/2) * s / sqrt(n)
end

# 统计规则配置:(统计函数, 列名后缀),新增指标直接在这里加即可
stats = [
    meansk => "_mean",
    stdsk => "_std",
    cilb => "_lb",
    ciub => "_ub",
    nm => "_nm"
]

df = DataFrame(v1=[10,20,300,400,missing],v2=[1,2,10,20,30],cl=[2,2,1,1,1])

# 自动提取所有非类别列,不需要手动指定变量名
val_cols = setdiff(names(df), ["cl"])

# 自动生成所有聚合操作
agg_ops = Any[nrow]
for col in val_cols, (fn, suffix) in stats
    push!(agg_ops, col => fn => Symbol(col, suffix))
end

# 分组聚合得到宽表
wide = combine(groupby(df, :cl), agg_ops...)

# 长宽表转换自动得到目标格式
long = stack(wide, Not(:cl), variable_name=:var)
res = unstack(long, :var, :cl, :value, renamecols=x -> Symbol("cl_", x))

# 调整行顺序:nrow在前,后续按变量+指标顺序排列
row_order = vcat("nrow", vec([string(col, suffix) for col in val_cols, (_, suffix) in stats]))
res = res[indexin(row_order, string.(res.var)), :]

运行得到的res和期望输出完全一致,代码可维护性更高:新增统计指标只需要修改stats配置列表,新增数值列时代码会自动识别处理,不需要修改任何聚合逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 00:57:21