去除重复项并保留绝对值最大的条目。

39

假设我有四个样本:id=1、2、3和4,每个样本上都有一个或多个测量值:

> a <- data.frame(id=c(1,1,2,2,3,4), value=c(1,2,3,-4,-5,6))
> a
  id value
1  1     1
2  1     2
3  2     3
4  2    -4
5  3    -5
6  4     6

我想要去除重复项,每个ID只保留一条记录 - 保留“value”列绝对值最大的记录。也就是说,这就是我想要的:

> a[c(2,4,5,6), ]
  id value
2  1     2
4  2    -4
5  3    -5
6  4     6

我该如何在R中实现这个功能?


1
你提到 "每个ID仅保留一个条目 - 其中具有'value'列的最大绝对值。" 如果每个ID匹配此条件的条目超过一个,那么期望的行为是什么?返回两个值还是其中任意一个?例如,如果 a[3, 2] <- 4,则您期望的输出是什么? - A5C1D2H2I1M1N2O1R2T1
啊..这是个好问题。值列实际上是一个实数而不是整数,并且很可能永远不会完全相等。理想的期望行为应该是舍弃这两个观测值,但正如我所说,这可能不会发生。 - Stephen Turner
感谢大家的帮助。 - Stephen Turner
7个回答

53

首先,在每个id组内,将不太需要的项目放在最后进行排序。

 aa <- a[order(a$id, -abs(a$value) ), ] #sort by id and reverse of abs(value)

然后:删除在 id 组中第一个元素之后的所有项

aa[ !duplicated(aa$id), ] #logical index extracts only first row within each id
  id value
2  1     2
4  2    -4
5  3    -5
6  4     6

如果不反转排序,这可以保持最小值。


15

如果你的数据集非常大,那么采用 data.table 方法可能会更合适:

library(data.table)

aDT <- as.data.table(a)
setkey(aDT,"id")

aDT[J(unique(id)), list(value = value[which.max(abs(value))])]


或者一个速度不如此快但仍然很快的替代方案:

library(data.table)
as.data.table(a)[, .SD[which.max(abs(value))], by=id]

这个版本返回a的所有列,以防实际数据集中有更多的列。


对我来说它不起作用,我只得到一个值,在一个行一列的DT中。语法改变了吗?相反,第二个例子有效。 - Herman Toothrot

13

这里是一个dplyr的方法

library(dplyr)
a %>% 
        group_by(id) %>%
        top_n(1, abs(value))

# A tibble: 4 x 2
# Groups:   id [4]
#     id value
#  <dbl> <dbl>
#1     1     2
#2     2    -4
#3     3    -5
#4     4     6

3
以防其他人需要使用-1来获取最小值。top_n(-1, abs(value)) - Ahdee
很方便,但有人能解释一下这个逻辑吗? - Negrito
1
@Negrito top_n()是一个包装器,用于选择每个组的前几个或后几个条目。更多细节请参见此处。在这里,我们对“value”列的最大(top_n(n = 1,...))绝对值感兴趣,即top_n(..., wt = abs(value)) - nghauran

10

查看?aggregate

aggregate(value~id,a,function(x) x[which.max(abs(x))])

我喜欢@DWin的答案,但是我想展示如何使用元数据实现同样的效果:

aa<-merge(aggregate(value~id,a,function(x) x[which.max(abs(x))]),a)
# Fails if the max value is duplicated for a single id without next line.
aa[!duplicated(aa),]

我情不自禁地创建了一个最后的答案:

do.call(rbind,lapply(split(a,a$id),function(x) x[which.max(abs(x$value)),]))

这个程序按照我的描述运行良好,但我应该更详细地说明。实际上,有一个单一的ID和许多其他元数据列对于每个ID都是相同的,并且对于每个ID还有许多其他值列。我想保留数据框中的所有列,而不仅仅是一个ID和值。 - Stephen Turner

5

另一种方法(尽管代码可能看起来有些繁琐)是使用ave()

a[which(abs(a$value) == ave(a$value, a$id, 
                            FUN=function(x) max(abs(x)))), ]
#   id value
# 2  1     2
# 4  2    -4
# 5  3    -5
# 6  4     6

@DWin,"我是通过看你的学习的!" ;) - A5C1D2H2I1M1N2O1R2T1
嘿。这不是我的文化框架,但我在YouTube上看到的“大脑受毒品影响”片段后得到的《实习医生风云》花絮非常有趣。 - IRTFM

3
library(plyr)
ddply(a, .(id), function(x) return(x[which(abs(x$value)==max(abs(x$value))),]))

plyr非常慢 - chupvl

1
您可以使用dplyr按如下方式执行此操作:

library(dplyr)
a %>%
  group_by(name) %>%
  filter(n == max(n)) %>%
  ungroup()

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