【问题标题】:R replace positive values in logical columns with the column names using data.table syntaxR使用data.table语法将逻辑列中的正值替换为列名
【发布时间】:2017-09-21 11:27:17
【问题描述】:

我有一个包含一些逻辑列的数据集,我想用相应的列名替换“TRUE”值。我问了一个类似的问题here,并在其他 S/O 用户的一些建议的帮助下找到了合适的解决方案。但是,该解决方案不使用data.table语法,而是复制整个数据集而不是通过引用替换,这很耗时。

使用 data.table 语法最合适的方法是什么?

我试过这个:

# Load library    
library(data.table)

# Create dummy data.table:
mydt <- data.table(id = c(1,2,3,4,5), 
                   ptname = c("jack", "jill", "jo", "frankie", "claire"), 
                   sex = c("m", "f", "f", "m", "f"), apple = c(T,F,F,T,T), 
                   orange = c(F,T,F,T,F), 
                   pear = c(T,T,T,T,F))

# View dummy data:
> mydt
   id  ptname sex apple orange  pear
1:  1    jack   m  TRUE  FALSE  TRUE
2:  2    jill   f FALSE   TRUE  TRUE
3:  3      jo   f FALSE  FALSE  TRUE
4:  4 frankie   m  TRUE   TRUE  TRUE
5:  5  claire   f  TRUE  FALSE FALSE

# Function to recode values in a data.table:
recode.multi <- function(datacol, oldval, newval) {
  trans <- setNames(newval, oldval)
  trans[ match(datacol, names(trans)) ]
}

# Get a list of all the logical columns in the data set:
logicalcols <- names(which(mydt[, sapply(mydt, is.logical)] == TRUE))

# Apply the function to convert 'TRUE' to the relevant column names:
mydt[, (logicalcols) := lapply(.SD, recode.multi, 
                               oldval = c(FALSE, TRUE), 
                               newval = c("FALSE", names(.SD))), .SDcols = logicalcols]

# View the result:
> mydt
   id  ptname sex apple orange  pear
1:  1    jack   m apple  FALSE apple
2:  2    jill   f FALSE  apple apple
3:  3      jo   f FALSE  FALSE apple
4:  4 frankie   m apple  apple apple
5:  5  claire   f apple  FALSE FALSE

这是不正确的,因为它不是遍历每个列名称以获取替换值,而是回收第一个(在本例中为“apple”)。

此外,如果我颠倒新旧值的顺序,该函数将忽略我对第二个值的字符串替换,并在所有情况下都使用前两个列名作为替换:

# Apply the function with order of old and new values reversed:
mydt[, (logicalcols) := lapply(.SD, recode.multi, 
                               oldval = c(TRUE, FALSE), 
                               newval = c(names(.SD), "FALSE")), .SDcols = logicalcols]

# View the result:
> mydt
   id  ptname sex  apple orange   pear
1:  1    jack   m  apple orange  apple
2:  2    jill   f orange  apple  apple
3:  3      jo   f orange orange  apple
4:  4 frankie   m  apple  apple  apple
5:  5  claire   f  apple orange orange

我确定我可能遗漏了一些简单的东西,但有谁知道为什么该函数不遍历列名(以及如何编辑它来做到这一点)?

我的预期输出如下:

> mydt
   id  ptname sex apple orange  pear
1:  1    jack   m apple  FALSE  pear
2:  2    jill   f FALSE orange  pear
3:  3      jo   f FALSE  FALSE  pear
4:  4 frankie   m apple orange  pear
5:  5  claire   f apple  FALSE FALSE

另外,任何其他关于简洁 data.table 语法的建议将不胜感激。

【问题讨论】:

  • 我猜在以后的任何分析中使用字符而不是逻辑将是痛苦的。关于为什么您的方式不起作用,lapply 一次迭代一件事(此处为 .SD)。如果您需要它来迭代 .SD 和名称 (.SD),请尝试 Map。
  • 谢谢 - 找不到 'Map' 的语法示例,但帮助说它是 mapply 的包装器 - 这样做mydt[, (logicalcols) := mapply(recode.multi, datacol = .SD, oldval = c(TRUE, FALSE), newval = c(names(.SD), "FALSE"), SIMPLIFY = FALSE), .SDcols = logicalcols] 几乎让我到了那里,除了 FALSE 值被转换为 NA .

标签: r replace logic data.table columnname


【解决方案1】:

我们可以使用melt/dcast 方法

dcast(melt(mydt, id.var = c("id", "ptname", "sex"))[,
     value1 := as.character(value)][(value), value1 := variable], 
            id + ptname + sex~variable, value.var = "value1")
#   id  ptname sex apple orange  pear
#1:  1    jack   m apple  FALSE  pear
#2:  2    jill   f FALSE orange  pear
#3:  3      jo   f FALSE  FALSE  pear
#4:  4 frankie   m apple orange  pear
#5:  5  claire   f apple  FALSE FALSE

或者另一种选择是使用set,这样会更有效

nm1 <- which(unlist(mydt[, lapply(.SD, class)])=="logical")
for(j in nm1){
    i1 <- which(mydt[[j]])
    set(mydt, i=NULL, j=j, value = as.character(mydt[[j]]))
    set(mydt, i = i1, j=j, value = names(mydt)[j])
}

mydt
#   id  ptname sex apple orange  pear
#1:  1    jack   m apple  FALSE  pear
#2:  2    jill   f FALSE orange  pear
#3:  3      jo   f FALSE  FALSE  pear
#4:  4 frankie   m apple orange  pear
#5:  5  claire   f apple  FALSE FALSE

或者cmets中提到的另一个选项是

mydt[, (nm1) := Map(function(x,y) replace(x, x, y), .SD, names(mydt)[nm1]), .SDcols = nm1]
mydt
#   id  ptname sex apple orange  pear
#1:  1    jack   m apple  FALSE  pear
#2:  2    jill   f FALSE orange  pear
#3:  3      jo   f FALSE  FALSE  pear
#4:  4 frankie   m apple orange  pear
#5:  5  claire   f apple  FALSE FALSE

更新:将选项 2 和 3(由于非逻辑列的数量不可能使用)与包含 18573 行和 650 列的数据集进行比较,其中 252 列是逻辑运行,时间如下:

# Option 2:
  nm1 <- which(unlist(mydt[, lapply(.SD, is.logical)])) 
  system.time( 
   for(j in nm1){ 
     i1 <- which(mydt[[j]]) 
     set(mydt, i=NULL, j=j, value = as.character(mydt[[j]])) 
     set(mydt, i = i1, j=j, value = names(mydt)[j]) 
     } 
   ) 
 # user system elapsed 
 #  0.61 0.00 0.61

# Option 3:
system.time( 
  mydt[, (nm1) := Map(function(x,y) replace(x, x, y), .SD, names(mydt)[nm1]), .SDcols = nm1] 

   ) 
#user system elapsed 
#0.65 0.00 0.66

两者都比不使用 data.table 语法的原始方法快得多:

# Original approach:
logitrue <- which(mydt == TRUE, arr.ind = T)
 system.time(
   mydt[logitrue, ] <- colnames(mydt)[logitrue[,2]]
 )
  # user  system elapsed 
  # 1.22    0.03    4.22 

【讨论】:

  • 谢谢,但是当我不知道我的数据集中要更改的列在哪里时(但可以通过创建所有逻辑的列表来确定要更改的列)列,已编辑我的帖子以包含此内容)。是否可以在熔体/dcast 中使用 .SD?同样在上面的 recode.multi 函数中已经处理了从逻辑到字符的转换,为此使用 set 是否更快?
  • @AmyM 您必须先将逻辑更改为字符,然后再将其重新编码为字符值
  • @AmyM 列名中应该有一些模式或某些东西要指定。如果没有模式,您如何知道要更改哪些列?
  • 奇怪地应用上面的 recode.multi 函数不会导致关于替换值与列类型不匹配的警告 - 我认为它会自动将它们转换为字符?
  • @AmyM 根据逻辑列的值更新帖子
猜你喜欢
  • 2016-08-26
  • 1970-01-01
  • 2016-01-20
  • 2015-09-17
  • 1970-01-01
  • 2021-06-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多