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

在R中基于另一数据框对数据框多列高效计算众数占比

高效计算数据框各列众数及占比(dplyr方案)

需求说明

我有一个包含字符型、字符型数值的大型数据框,需要不使用循环快速计算每列的众数及其在该列中的占比。之前用自定义函数得到了各列众数,但用lapply/sapply调用占比计算函数时速度很慢,希望用dplyr包的mutate或summarize实现高效解决方案。

示例数据

df <- data.frame(
  id = paste0("SubID_", 1:100),
  score = as.character(sample(1:100, 100, replace=TRUE)),
  dob = sample(seq(as.Date('1999/01/01'), as.Date('2000/01/01'), by="day"), 100)
)

原有实现及问题

自定义众数函数

用以下函数获取了各列众数并存入数据框f:

# 自定义众数计算函数
mode <- function(v) {
  uniqv <- unique(v)
  uniqv[which.max(tabulate(match(v, uniqv)))]
}

# 获取各列众数
f <- data.frame(sapply(df, mode))

得到的结果:

> f
     sapply.df..mode.
id             SubID_1
score               84
dob              10739  # 日期列被转成数值,类型丢失

占比计算的问题

尝试编写frequencycounter函数计算占比,但用lapply/sapply调用时速度很慢:

frequencycounter <- function(x,df,f){
  sum(str_count(df[,x], f[x,]))/length(df[,x])
}

# 调用方式(速度慢)
lapply(colnames(df),frequencycounter,df=df,f=f)
sapply(colnames(df),frequencycounter,df=df,f=f)

问题在于:str_count是字符串匹配操作,对数值、日期类型的列不适用,且逐个列循环处理在大型数据框上效率极低。

dplyr高效解决方案

优化后的众数函数

先优化众数函数,保留原列的数据类型(比如日期列不会被转成数值):

get_mode <- function(v) {
  uniq_v <- unique(v)
  uniq_v[which.max(tabulate(match(v, uniq_v)))]
}

一次性计算众数及占比

用dplyr的across实现向量化操作,一次性完成所有列的众数和占比计算:

library(dplyr)
library(tidyr)

# 计算各列众数及占比
mode_stats <- df %>%
  summarize(
    across(
      .cols = everything(),  # 对所有列处理
      .fns = list(
        mode = ~get_mode(.),  # 计算众数
        mode_pct = ~mean(. == get_mode(.))  # 计算众数占比,mean(逻辑值)等价于占比
      )
    )
  )

# 整理结果为长格式(可选,更易读)
mode_stats <- mode_stats %>%
  pivot_longer(
    everything(),
    names_to = c("column", ".value"),
    names_sep = "_"
  )

结果展示

运行后得到清晰的统计结果:

> print(mode_stats)
# # A tibble: 3 × 3
#   column mode      mode_pct
#   <chr>  <chr>        <dbl>
# 1 id     SubID_1       0.01
# 2 score  84            0.02
# 3 dob    1999-05-15    0.01

方案优势

  1. 向量化操作:dplyr的across底层是向量化处理,比循环或lapply逐个列处理快得多,尤其适合大型数据框。
  2. 类型保留:优化后的get_mode函数会保留原列的数据类型(比如日期列显示为日期格式,而非数值)。
  3. 高效占比计算:mean(. == get_mode(.))直接对整列进行逻辑比较,再取均值得到占比,避免了字符串匹配的开销,且比sum(...) / length(...)更简洁高效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 20:58:22