在 R 数据框中,反转非 NA 值的顺序。

3

我想要在R数据框中反转多列数值的顺序(使最高的数字变为最低,依此类推),同时保留NA值不变。

以下是我的数据框示例:

my_data <- data.frame (animal  = c("fox", "rabbit", "cow", "sheep", "pig", "mole"),
                        x = c("1", "2", "1", "3", "NA", 'NA'),
                       y = c('NA','NA','1','3','2','NA'),
                       z = c('1','2','3','4','NA','5'),
                       area = c("field","field","farm","farm","farm","farm"))

接下来,我想要实现的是:

my_ideal_data <- data.frame (animal  = c("fox", "rabbit", "cow", "sheep", "pig", "mole"),
                             x = c("3", "2", "3", "1", "NA", 'NA'),
                             y = c('NA','NA','3','1','2','NA'),
                             z = c('5','4','3','2','NA','1'),
                             area = c("field","field","farm","farm","farm","farm"))

“animal”和“area”列保持不变,所有的NAs也保持不变 - 但我需要将x、y和z的值在每个列中以相反的顺序放置。

任何帮助都将不胜感激!

谢谢


顺便提一下,这不是关于对值进行排序的问题。它应该被命名为例如“替换每列中所有排名的最小值和最大值”。 - Andre Wildberg
只是提醒一下,如果下面的任何答案解决了您的问题,您可以点击您最喜欢的答案左侧的小勾号来接受它 :) - benson23
3个回答

1
你可以在这里使用for循环。 首先将所有的"NA"字符替换为真正的NA
my_data[my_data == "NA"] <- NA

然后定义一个包含你想要排序的列的向量。

target_col <- c("x", "y", "z")

使用 for 循环遍历目标列,并通过将列值减去 max+ 1 进行替换。
my_data[my_data == "NA"] <- NA

target_col <- c("x", "y", "z")
for (i in target_col) {
  my_data[!is.na(my_data[,i]),i] <- as.integer(max(my_data[,i], na.rm = T)) + 1 - as.integer(my_data[!is.na(my_data[,i]),i])
}

  animal    x    y    z  area
1    fox    3 <NA>    5 field
2 rabbit    2 <NA>    4 field
3    cow    3    3    3  farm
4  sheep    1    1    2  farm
5    pig <NA>    2 <NA>  farm
6   mole <NA> <NA>    1  farm

谢谢 - 这比我做得更好了!但是我认为在生成的数据框中重复的值仍然是一个问题?原来的x列是 '1, 2, 1, 3, NA, NA',而现在转换成了 '3, 2, 1, 1, NA, NA',而不是 '3, 2, 3, 1, NA, NA' - 这是因为有重复的1吗? - Louise Whiteside
1
@LouiseWhiteside 我之前搞错了,现在应该可以正常工作 :) - benson23

1
在这些数据中,你可以将z列转换为数字后,简单地减去6。
my_data$z <- 6 - as.numeric(my_data$z)  

#> my_data
#  animal  x  y  z  area
#1    fox  3 NA  5 field
#2 rabbit  2 NA  4 field
#3    cow  3  3  3  farm
#4  sheep  1  1  2  farm
#5    pig NA  2 NA  farm
#6   mole NA NA  1  farm

如果这些示例数据过于简化,另一种选择是使用grep索引非NA值,然后使用gtools::mixedsort()按降序排序,最后使用[indexing]替换这些值。这可能更具可扩展性,而且您不必转换为数字。
idx <- grep("\\d+", my_data$z)
vals <- gtools::mixedsort(my_data$z[idx], decreasing = TRUE)
my_data$z[idx] <- vals

#  animal  x  y  z  area
#1    fox  3 NA  5 field
#2 rabbit  2 NA  4 field
#3    cow  3  3  3  farm
#4  sheep  1  1  2  farm
#5    pig NA  2 NA  farm
#6   mole NA NA  1  farm

如果您想将其应用于多个列,可以将其包装在使用lapply的函数中:

myfun <- function(x){
  a <-  grep("\\d+", x)
  x[a] <- gtools::mixedsort(x[a], decreasing = TRUE)
  x
}

my_data[c("x", "y", "z")] <- lapply(my_data[c("x", "y", "z")], myfun)

谢谢您的回复!当我尝试重现时,它只适用于 z 列,有没有办法将此代码应用于多个列? - Louise Whiteside

0

使用 dplyr 中的 across

library(dplyr)

Cols <- c("x", "y", "z")

my_data[,Cols] <- Vectorize(\(x) as.numeric(x))(my_data[,Cols])

my_data %>% 
  mutate(across(!!Cols, ~ max(.x[!is.na(.x)]) - .x + 1))
  animal  x  y  z  area
1    fox  3 NA  5 field
2 rabbit  2 NA  4 field
3    cow  3  3  3  farm
4  sheep  1  1  2  farm
5    pig NA  2 NA  farm
6   mole NA NA  1  farm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接