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

如何在R语言中按组将大于0的唯一值替换为从1开始的序号?

问题:分组内将非零唯一值替换为连续序号

原始数据

df <- data.frame(
  group = c("A","A","B","B","C"),
  value1 = c(55, 0, 92, 93, 173),
  value2 = c(55, 0, 92, 93, 174),
  value3 = c(66, 77, 92, 0, 175)
)

print(df)

输出:

group value1 value2 value3
1     A     55     55     66
2     A      0      0     77
3     B     92     92     92
4     B     93     93      0
5     C    173    174    175

期望结果

group value1 value2 value3
1     A      1      1      2
2     A      0      0      3
3     B      1      1      1
4     B      2      2      0
5     C      1      2      3

尝试的代码及错误

尝试用mutate+across+dense_rank实现,但代码报错:

df <- df %>%
   group_by(group) %>%
   mutate(across(-group, ~ifelse(. > 0, dense_rank(.), .))) %>%
   ungroup()

错误信息:

Error: Problem with `mutate()` input `..1`.
x Can't subset columns that don't exist.
x Column `group` doesn't exist.
i Input `..1` is `across(-group, ~ifelse(. > 0, dense_rank(.), .))`.
i The error occurred in group 1: group = "A".

问题分析与解决方案

问题点

  1. 报错原因:group_by后,across中用-group无法正确排除分组列,改用指定目标列(如starts_with("value"))更稳妥。
  2. 逻辑错误:原代码的dense_rank(.)是对每一列单独排名,而非整个组内所有非零值统一分配序号,无法得到期望结果。

正确代码

我们需要先提取每个组内所有非零的唯一值,建立“原始值→序号”的映射,再批量替换各value列的非零值:

library(dplyr)

df <- df %>%
  group_by(group) %>%
  mutate(
    across(starts_with("value"), ~{
      # 提取当前组所有非零的唯一值,按出现顺序生成序号
      group_nonzero_vals <- unique(c_across(starts_with("value"))[c_across(starts_with("value")) > 0])
      # 匹配当前值对应的序号,0保持不变
      ifelse(. > 0, match(., group_nonzero_vals), 0)
    })
  ) %>%
  ungroup()

print(df)

运行后即可得到期望结果:

# A tibble: 5 × 4
  group value1 value2 value3
  <chr>  <int>  <int>  <int>
1 A          1      1      2
2 A          0      0      3
3 B          1      1      1
4 B          2      2      0
5 C          1      2      3

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 17:23:33