将多列重新编码为R中递增的数字

bq9c1y66  于 2023-01-18  发布在  其他
关注(0)|答案(4)|浏览(152)

我有50列名称,但为了方便起见,这里只列出4列。

Name1       Name2         Name3      Name4
Rose,Ali    Van,Hall      Ghol,Dam   Murr,kate
Camp,Laura  Ka,Klo        Dan,Dan    Ali,Hoss
Rose,Ali    Van,Hall      Ghol,Dam   Kol,Kan
Murr,Kate   Ismal, Ismal  Sian,Rozi  Nas,Ami
Ghol,Dam    Ka,Klo        Rose,Ali   Nor,Ko
Murr,Kate   Ismal, Ismal  Dan,Dan    Nas,Ami

我想根据列给每个人分配数字,一个数字序列。
例如,在名称1中,我们得到1-4的数字。重复的名称将得到相同的数字。
在名称2中,应该从5开始,以此类推。这将给予下表:

Assign1 Assian2 Assian3 Assian4
      1       5       8      12
      2       6       9      13
      1       5       8      14
      3       7      10      15
      4       6      11      17
      3       7       9      15

我希望它没有循环,即sapply,即sapply(dat, function(x) match(x, unique(x)))
使用dplyr或tidyverse将是伟大的。

pu82cl6c

pu82cl6c1#

tidyverse解决方案与purrr::accumulate()

library(tidyverse)

df %>%
  mutate(as_tibble(
    accumulate(across(Name1:Name4, ~ match(.x, unique(.x))), ~ .y + max(.x))
  ))

#   Name1 Name2 Name3 Name4
# 1     1     5     8    12
# 2     2     6     9    13
# 3     1     5     8    14
# 4     3     7    10    15
# 5     4     6    11    16
# 6     3     7     9    15
vdgimpew

vdgimpew2#

因为每列的值都依赖于前一列的值,所以计算必须按顺序进行。这可能是通过循环实现的最简洁的方法。请记住,lapplysapply只是伪装的循环,不会比显式循环更快。
请注意,您的预期输出中有一个错误(有一个数字17,应该是16)

output <- setNames(df, paste0('Assign', seq_along(df)))
                   
for(i in seq_along(output)) {
  output[[i]] <- match(output[[i]], unique(output[[i]]))
  if(i > 1) output[[i]] <- output[[i]] + max(output[[i - 1]])
}

output
#>    Assign1  Assign2  Assign3  Assign4
#> 1        1        5        8       12
#> 2        2        6        9       13
#> 3        1        5        8       14
#> 4        3        7       10       15
#> 5        4        6       11       16
#> 6        3        7        9       15
    • 编辑**

如果你真的不想使用显式循环,你可以这样做:

res <- sapply(seq_along(df), \(i) match(df[[i]], unique(df[[i]]))) 
res + t(replicate(nrow(df), head(c(0, cumsum(apply(res, 2, max))), -1))) |>
  as.data.frame() |>
  setNames(paste0('Assign', seq_along(df)))
#>   Assign1 Assign2 Assign3 Assign4
#> 1       1       5       8      12
#> 2       2       6       9      13
#> 3       1       5       8      14
#> 4       3       7      10      15
#> 5       4       6      11      16
#> 6       3       7       9      15

创建于2023年1月13日,使用reprex v2.0.2

    • 数据取自可复制格式的问题**
df <- structure(list(Name1 = c("Rose,Ali", "Camp,Laura", "Rose,Ali", 
"Murr,Kate", "Ghol,Dam", "Murr,Kate"), Name2 = c("Van,Hall", 
"Ka,Klo", "Van,Hall", "Ismal, Ismal", "Ka,Klo", "Ismal, Ismal"
), Name3 = c("Ghol,Dam", "Dan,Dan", "Ghol,Dam", "Sian,Rozi", 
"Rose,Ali", "Dan,Dan"), Name4 = c("Murr,kate", "Ali,Hoss", "Kol,Kan", 
"Nas,Ami", "Nor,Ko", "Nas,Ami")), row.names = c(NA, -6L), 
class = "data.frame")
mrwjdhj3

mrwjdhj33#

下面是一个tidyverse方法:
首先paste所有列中每个字符串后面的列名,以便以后排序;然后pivot它到一个两列df中,以便我们可以通过match给它们分配ID;最后pivot它回到一个宽格式,并取消嵌套列表列。

library(tidyverse)

df %>% 
  mutate(across(everything(), ~ paste0(.x, "_", cur_column()))) %>% 
  pivot_longer(everything(), names_to = "ab", values_to = "a") %>% 
  arrange(ab) %>% 
  mutate(b = match(a, unique(a)), .keep = "unused") %>% 
  pivot_wider(names_from = "ab", values_from = "b") %>% 
  unnest(everything())

# A tibble: 6 × 4
  Name1 Name2 Name3 Name4
  <int> <int> <int> <int>
1     1     5     8    12
2     2     6     9    13
3     1     5     8    14
4     3     7    10    15
5     4     6    11    16
6     3     7     9    15

数据

摘自@艾伦·卡梅隆。

df <- structure(list(Name1 = c("Rose,Ali", "Camp,Laura", "Rose,Ali", 
"Murr,Kate", "Ghol,Dam", "Murr,Kate"), Name2 = c("Van,Hall", 
"Ka,Klo", "Van,Hall", "Ismal, Ismal", "Ka,Klo", "Ismal, Ismal"
), Name3 = c("Ghol,Dam", "Dan,Dan", "Ghol,Dam", "Sian,Rozi", 
"Rose,Ali", "Dan,Dan"), Name4 = c("Murr,kate", "Ali,Hoss", "Kol,Kan", 
"Nas,Ami", "Nor,Ko", "Nas,Ami")), row.names = c(NA, -6L), 
class = "data.frame")
ukqbszuj

ukqbszuj4#

    • 更新:下面的方法并不理想,因为ID不是唯一的。抱歉。**

将查找表与tidyverse一起使用:

library(dplyr)
library(tidyr)

lookup <-
  df |> 
  pivot_longer(everything()) |>
  distinct() |>
  arrange(name) |>
  transmute(name = value, value = row_number()) |>
  deframe()

df |>
  mutate(across(everything(), ~ recode(., !!!lookup)))

输出:

Name1 Name2 Name3 Name4
1     1     5     4    12
2     2     6     9    13
3     1     5     4    14
4     3     7    10    15
5     4     6     1    16
6     3     7     9    15

数据来自@艾伦·卡梅隆,谢谢。

相关问题