Julia DataFrames按分组均值填充petal_length缺失值的优化方法
问题
在iris数据框中,我已将部分值替换为missing。现需要将petal_length列中的missing值,替换为对应species分组下的petal_length均值。
现有可运行代码能满足需求(替换前后列均值保持一致),但存在可优化空间:当前实现逐行遍历全表,且额外创建了分组均值字典,需要更高效的实现方案。
原实现代码如下:
using CSV using DataFrames using Random using Statistics using StatsBase download("https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv", "iris.csv") iris = DataFrame(CSV.File("iris.csv", delim = ",")) allowmissing!(iris) Random.seed!(20_000) for i in 1:100 iris[rand(1:nrow(iris)), rand(1:4)] = missing end Random.seed!(20_000) iris[sample(1:nrow(iris), 10), :species] .= missing mean_per_species = combine(groupby(iris, :species), :petal_length => mean∘skipmissing => :mean) mean_per_species_dict = Dict(mean_per_species.species => mean_per_species.mean) for row in eachrow(iris) if ismissing(row.petal_length) row.petal_length = mean_per_species_dict[row.species] end end
优化方案
直接使用DataFrames.jl原生的分组变换接口实现,无需手动创建字典,也无需手写逐行遍历逻辑,核心代码仅需1行,底层走向量化运算,效率远高于手动循环。
优化后完整代码:
using CSV using DataFrames using Random using Statistics using StatsBase download("https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv", "iris.csv") iris = DataFrame(CSV.File("iris.csv", delim = ",")) allowmissing!(iris) Random.seed!(20_000) for i in 1:100 iris[rand(1:nrow(iris)), rand(1:4)] = missing end Random.seed!(20_000) iris[sample(1:nrow(iris), 10), :species] .= missing # 核心优化逻辑:按物种分组,用组内非缺失值的均值填充组内petal_length的缺失项 transform!(groupby(iris, :species), :petal_length => (col -> coalesce.(col, mean(skipmissing(col)))) => :petal_length )
方案优势
- 无额外中间对象:不需要单独生成分组均值表、不需要构造字典映射,
groupby操作会自动完成行和所属分组的匹配 - 无冗余遍历:
coalesce.是向量化操作,仅会对缺失位置做值替换,非缺失值直接保留,不需要逐行写判断逻辑,执行速度更快 - 逻辑可读性强:填充逻辑和数据操作的原生语法一致,不需要维护循环和映射关系
- 结果完全符合预期:和原实现逻辑一致,替换前后
petal_length列的均值保持不变;对于species本身缺失的行,因为没有对应分组,会保留missing值,和原逻辑的行为匹配。
内容的提问来源于stack exchange,提问作者René
相关产品推荐
相关产品推荐

