【问题标题】:Replacing duplicates within a row with NA keeping the first in R in the row用 NA 替换行中的重复项,保留行中 R 中的第一个
【发布时间】:2020-05-22 18:46:34
【问题描述】:

我正在尝试逐行删除重复数据。例如

> head(data_Final)
  ID   Value1    Value2  Value3   Value4
1 a      876      989      989      758
2 b      921      801      971      995
3 c      636      889      7724      95
4 d      999      999      896      999
5 e      251      254      251      235
6 f      552      100      669      015

我需要这样的结果:

> head(data_Final)
  ID   Value1    Value2  Value3   Value4
1 a      876      989      NA      758
2 b      921      801      971     995
3 c      636      889      7724    95
4 d      999      NA       896     NA
5 e      251      254      NA      235
6 f      552      100      669     015

我搜索了很多,但结果是在一列而不是一行中的重复项。

【问题讨论】:

  • 您可以将数据转为长格式,按 ID 分组,改变重复值,然后转回宽格式。

标签: r dplyr duplicates


【解决方案1】:

我们可以使用apply 循环数字列,并使用replace duplicated 元素和NA

data_Final[-1] <- t(apply(data_Final[-1], 1,
       function(x) replace(x, duplicated(x), NA)))
data_Final
#  ID Value1 Value2 Value3 Value4
#1  a    876    989     NA    758
#2  b    921    801    971    995
#3  c    636    889   7724     95
#4  d    999     NA    896     NA
#5  e    251    254     NA    235
#6  f    552    100    669     15

apply 也可以更改为 for 循环

for(i in seq_len(nrow(data_Final))) {
   tmp <- data_Final[i, -1]
   data_Final[i, -1] <- replace(tmp, duplicated(tmp), NA)
  }

或者使用来自purrrpmap

library(dplyr)
library(purrr)
data_Final %>%
     select(-ID) %>%
     pmap_dfr(., ~ c(...) %>%
            replace(., duplicated(.), NA)) %>%
     bind_cols(select(data_Final, ID), .)

基准测试

system.time(t(apply(data_Final[-1], 1,
       function(x) replace(x, duplicated(x), NA))))
#   user  system elapsed 
#  0.013   0.003   0.015 


system.time(for(i in seq_len(nrow(data_Final))) {
        tmp <- data_Final[i, -1]
        data_Final[i, -1] <- replace(tmp, duplicated(tmp), NA)
       }
 )
#   user  system elapsed 
#  0.014   0.004   0.018 

关于forapply的讨论,已经在多个帖子中记录了herehere,并没有太大区别

数据

data_Final <- structure(list(ID = c("a", "b", "c", "d", "e", "f"), 
     Value1 = c(876L, 
921L, 636L, 999L, 251L, 552L), Value2 = c(989L, 801L, 889L, 999L, 
254L, 100L), Value3 = c(989L, 971L, 7724L, 896L, 251L, 669L), 
    Value4 = c(758L, 995L, 95L, 999L, 235L, 15L)), class = "data.frame",
    row.names = c("1", 
"2", "3", "4", "5", "6"))

【讨论】:

  • 嗯,好久没见你用apply了
  • @Onyambu 行明智,它更容易表达和得到计算,而不是旋转/旋转等。我喜欢 for 循环的优雅,但这只是主观的
  • @akrun 是的,我还发现 for-loop 更易读。有时真的“更快” 因为在数据帧上使用apply 时,通常必须使用t()data.frame() 才能做到这一点。但正如我所说,有时使用apply() 时计算机会变得非常慢。当 df 真的很大时,它正在appreading。也许是因为人们就地改变了价值观……
【解决方案2】:

你可以使用ave:

data_Final[-1]<-ave(unlist(data_Final[-1]), row(data_Final[-1]),
                    FUN = function(x)`is.na<-`(x,duplicated(x)))
data_Final
  ID Value1 Value2 Value3 Value4
1  a    876    989     NA    758
2  b    921    801    971    995
3  c    636    889   7724     95
4  d    999     NA    896     NA
5  e    251    254     NA    235
6  f    552    100    669     15

【讨论】:

  • 有兴趣直接使用is.na&lt;-
【解决方案3】:
replace.dup <- function(x, val=NA) {
    x[duplicated(x)] <- val
    x
}


replace.row.wise.dups <- function(df, val=NA) {
    for (i in 1:nrow(df)) {
      df[i, ] <- replace.dup(unlist(df[i, , drop=T]), val)
    }
    df
}

在这种情况下,我将只使用for-loop。 (l)applyt() 可以很容易地改变你的想法...... for 没那么慢。有时apply 函数很慢。

重新创建df

text <- "  ID   Value1    Value2  Value3   Value4
1 a      876      989      989      758
2 b      921      801      971      995
3 c      636      889      7724      95
4 d      999      999      896      999
5 e      251      254      251      235
6 f      552      100      669      015"

txt <- gsub("\ +", "\t", text)

df <- read.delim(text = txt, sep="\t", row.names=1, stringsAsFactors=FALSE)

运行它

replace.row.wise.dups(df, NA)

#   ID Value1 Value2 Value3 Value4
# 1  a    876    989   <NA>    758
# 2  b    921    801    971    995
# 3  c    636    889   7724     95
# 4  d    999   <NA>    896   <NA>
# 5  e    251    254   <NA>    235
# 6  f    552    100    669     15

for-loop 比 apply 稍慢

对不起,我错了。我认为for-loop 更快。但是如果你的 df 非常大,for-loop 可能会给你更快的速度。正如@akrun 指出的那样,可能是因为内存问题。

# from @akrun
replace.row.wise.dup.1 <- function(df, val=NA) {
  as.data.frame(t(apply(df, 1, function(x) replace(x, duplicated(x), NA))))
}

require(microbenchmark)
mbm <- microbenchmark("apply" = replace.row.wise.dups.1(df, NA),
                      "for-loop" = replace.row.wise.dups(df, NA),
                      times = 1000)
# > mbm
# Unit: microseconds
#      expr     min       lq     mean   median      uq      max neval
#     apply 600.950 623.9905 673.0897 632.4485 645.910 3668.063  1000
#  for-loop 696.792 727.8785 791.7684 754.1875 772.129 2491.147  1000

【讨论】:

  • @akrun 是的,我从你那里得到的。 (我有任务)。但在这种情况下,它是关于 for 循环的。 apply 用于数据帧中此类内容的函数可能会变得非常慢。
  • @akrun - 我告诉过你,我的回答不是关于replace()。即将申请for-loop。在 R 中,有一种说法是 apply 函数比 for-loop 更快、更高效。但这根本不是真的。
  • 您可以编译 for 循环并使其更快一些,但无论如何与 apply 进行比较是没有意义的,这就是我的看法。我强烈评论的原因是提供正确的信息,仅此而已。谢谢
  • 我发现t 非常快,但我可能是错的。唯一的问题可能是大型数据集的内存
  • @akrun 啊,这可能是内存占用……有时应用函数需要 30 多分钟来处理一个巨大的数据帧。这是真的......可能是因为内存......(我在生物信息学领域,有时我们有几百 Mb 甚至几个 Gb 大小的表)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-01-25
  • 1970-01-01
  • 1970-01-01
  • 2021-05-13
  • 1970-01-01
  • 1970-01-01
  • 2019-10-11
相关资源
最近更新 更多