使用出现最频繁的值交换data.table列

3

我有数据

test = data.table(
  a = c(1,1,3,4,5,6), 
  b = c("a", "be", "a", "c", "d", "c"), 
  c = rep(1, 6)
)

我希望将列a的唯一值提取出来,并存储在另一个数据表中,然后用剩余列中最常见的值填充剩下的列,以使得我的结果数据表如下:

test2 = data.table(a = c(1,3,4,5,6), b = "a", c = 1)

该列包含相等数量的 "a" 和 "c",但在这些情况下选择哪一个并不重要。

目前的尝试:

test2 = unique(test, by = "a")
test2[, c("b", "c") := lapply(.SD, FUN = function(x){test2[, .N, by = x][order(-N)][1,1]}), .SDcols = c("b", "c")]

编辑: 我更倾向于一个通用解决方案,与我指定要“唯一化”的列以及其余列使用单个最普遍值的函数兼容。因此,我使用了lapply和.SD =)

编辑2: 正如@MichaelChirico所指出的那样,我们如何保持类别(数据类型)。对于以下data.table中的解决方案中的一些解决方案不起作用,尽管@chinsoon12的解决方案起作用:

test = data.table(a = c(1,1,3,4,5,6), 
                  b = c("a", "be", "a", "c", "d", "c"), 
                  c = rep(1, 6),
                  d = as.Date("2019-01-01"))
5个回答

3

另一种选择:

dtmode <- function(x) x[which.max(rowid(x))]
test[, .(A=unique(A), B=dtmode(B), C=dtmode(C))] 

数据:

test = data.table(
    A = c(1,1,3,4,5,6), 
    B = c("a", "be", "a", "c", "d", "c"), 
    C = rep(1, 6)
)

啊,这非常优雅,谢谢!我不知道rowid()。 - Berthrand Eros

2
这并不是一种规范的做法,但它可以实现目标。
test = data.frame(a = c(1,1,3,4,5,6), b = c("a", "be", "a", "c", "d", "c"), c = rep(1, 6))

a = unique(test$a)
b = tail(names(sort(table(test$b))), 1)
c = tail(names(sort(table(test$c))), 1)

test2 = cbind(a,b,c)

输出的结果如下:
> test2
     a   b   c  
[1,] "1" "c" "1"
[2,] "3" "c" "1"
[3,] "4" "c" "1"
[4,] "5" "c" "1"
[5,] "6" "c" "1"
> 

谢谢您的帮助!不幸的是,这不是我要找的。我不想硬编码任何东西 =) - Berthrand Eros
2
@MichaelChirico:抱歉,那不是很清楚,我同意。我的意思是:1)一种解决方案,在该解决方案中,变量名称不会在代码中明确写出,例如第2、3和4行。2)一种可以适用于比我在示例中提到的更多列的解决方案,即我想要一个使用.SD + .SDcols的data.table解决方案。 - Berthrand Eros

2

@EmreKiratli非常接近我会做的事情:

test[ , c(
  list(a = unique(a)),
  lapply(.SD, function(x) as(tail(names(sort(table(x))), 1L), class(x)))
), .SDcols = !'a']

as(., class(x))这部分是因为在R中,names总是character类型,所以我们需要将其转换回x的原始类型。

如果您喜欢使用magrittr形式,可能会更好,因为它包含许多嵌套函数:

library(magrittr)
test[ , c(
  list(a = unique(a)),
  lapply(.SD, function(x) {
    table(x) %>% sort %>% names %>% tail(1L) %>% as(class(x))
  })
), .SDcols = !'a']

1
非常感谢!关于类的观点非常好。当我们在data.table test中有一个日期列时,我们的代码实际上都不起作用。test = data.table(a = c(1,1,3,4,5,6), b = c("a", "be", "a", "c", "d", "c"), c = rep(1, 6), d = as.Date("2019-01-01")) - Berthrand Eros

1

我能够做出一个还不错的解决方案,但如果有人能更优雅地完成,例如不需要像下面在refLevel中排序列表这一步骤,那请告诉我!我非常有兴趣学习正确使用data.table!

#solution:
test = data.table(a = c(1,1,3,4,5,6), b = c("a", "be", "a", "c", "d", "c"), c = rep(1, 6))
test2 = unique(test, by="a")
funPrev = function(x){unlist(as.data.table(x)[, .N, by=x][order(-N)][1,1], use.names = F)}
refLevel = lapply(test[, c("b", "c")], funPrev)
test2[, c("b", "c") := refLevel]

...并使用一个函数(如果有人看到任何不必要的步骤,请让我知道):

genData = function(dt, var_unique, vars_prev){

  data = copy(dt)
  data = unique(data, by = var_unique)

  funPrev = function(x){unlist(as.data.table(x)[, .N, by=x][order(-N)][1,1], use.names = F)}

  refLevel = lapply(dt[, .SD, .SDcols = vars_prev], funPrev)
  data[, (vars_prev) := refLevel] 

  return(data)
}
test2 = genData(test, "a", c("b", "c"))

1
这是另一种变体,可能不太复杂,但更易读。本质上是将chinsoon12的rowid方法推广到任意数量的列。同时保留类别信息。
test = data.table(a = c(1,1,3,4,5,6), 
                  b = c("a", "be", "a", "c", "d", "c"), 
                  c = rep(1, 6),
                  d = as.Date("2019-01-01"))
test2 = unique(test, by = "a")
for (col in setdiff(names(test2), "a")) test2[[col]] = test2[[col]][which.max(rowid(test2[[col]]))]

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