2016-05-17 60 views
3

我有一个data.frame有几个零值的变量。我需要构造一个额外的变量,它会返回每个观察结果不为零的变量组合。例如。如何在R data.frame中创建组合变量?

df <- data.frame(firm = c("firm1", "firm2", "firm3", "firm4", "firm5"), 
       A = c(0, 0, 0, 1, 2), 
       B = c(0, 1, 0, 42, 0), 
       C = c(1, 1, 0, 0, 0)) 

现在我想生成新的变量:

df$varCombination <- c("C", "B-C", NA, "A-B", "A") 

我想出了这样的事情,这显然没有奏效:

for (i in 1:nrow(df)){ 
    df$varCombination[i] <- paste(names(df[i,2:ncol(df) & > 0]), collapse = "-") 
} 

回答

6

这可能可以解决ED容易使用apply(df, 1, fun),但在这里就是为了解决这个逐列而不是行明智的性能考虑尝试(我曾经看到类似通过@alexis_laz做了一些,但无法找到它现在)

## Create a logical matrix 
tmp <- df[-1] != 0 
## or tmp <- sapply(df[-1], `!=`, 0) 

## Prealocate result 
res <- rep(NA, nrow(tmp)) 

## Run per column instead of per row 
for(j in colnames(tmp)){ 
    res[tmp[, j]] <- paste(res[tmp[, j]], j, sep = "-") 
} 

## Remove the pre-allocated `NA` values from non-NA entries 
gsub("NA-", "", res, fixed = TRUE) 
# [1] "C" "B-C" NA "A-B" "A" 

在一个更大的数据有些基准设置

set.seed(123) 
BigDF <- as.data.frame(matrix(sample(0:1, 1e4, replace = TRUE), ncol = 10)) 

library(microbenchmark) 

MM <- function(df) { 
    var_names <- names(df)[-1] 
    res <- character(nrow(df)) 
    for (i in 1:nrow(df)){ 
    non_zero_names <- var_names[df[i, -1] > 0] 
    res[i] <- paste(non_zero_names, collapse = '-') 
    } 
    res 
} 

ZX <- function(df) { 
    res <- 
    apply(df[,2:ncol(df)]>0, 1, 
      function(i)paste(colnames(df[, 2:ncol(df)])[i], collapse = "-")) 
    res[res == ""] <- NA 
    res 
} 

DA <- function(df) { 
    tmp <- df[-1] != 0 
    res <- rep(NA, nrow(tmp)) 

    for(j in colnames(tmp)){ 
    res[tmp[, j]] <- paste(res[tmp[, j]], j, sep = "-") 
    } 
    gsub("NA-", "", res, fixed = TRUE) 
} 


microbenchmark(MM(BigDF), ZX(BigDF), DA(BigDF)) 
# Unit: milliseconds 
#  expr  min   lq  mean  median   uq  max neval cld 
# MM(BigDF) 239.36704 248.737408 253.159460 252.177439 255.144048 289.340528 100 c 
# ZX(BigDF) 35.83482 37.617473 38.295425 38.022897 38.357285 76.619853 100 b 
# DA(BigDF) 1.62682 1.662979 1.734723 1.735296 1.761695 2.725659 100 a 
5

使用适用于:

# paste column names 
df$varCombination <- 
    apply(df[,2:ncol(df)]>0, 1, 
     function(i)paste(colnames(df[, 2:ncol(df)])[i], collapse = "-")) 

# convert blank to NA 
df$varCombination[df$varCombination == ""] <- NA 

# result 
df 
# firm A B C varCombination 
# 1 firm1 0 0 1    C 
# 2 firm2 0 1 1   B-C 
# 3 firm3 0 0 0   <NA> 
# 4 firm4 1 42 0   A-B 
# 5 firm5 2 0 0    A 
1

你有正确的想法,但在你的循环逻辑比较是不正确的。

我试图保持相当类似,你有什么之前的代码,这应该工作:

var_names <- names(df)[-1] 

df$varCombination <- character(nrow(df)) 

for (i in 1:nrow(df)){ 

    non_zero_names <- var_names[df[i, -1] > 0] 

    df$varCombination[i] <- paste(non_zero_names, collapse = '-') 

} 

> df 
    firm A B C varCombination 
1 firm1 0 0 1    C 
2 firm2 0 1 1   B-C 
3 firm3 0 0 0    
4 firm4 1 42 0   A-B 
5 firm5 2 0 0    A 
+0

谢谢!到目前为止所有建议的解决方案都可以很好地工作所以选择你的版本作为最好的只是我的品味。它缺少NA替代品,但那不是绊脚石。 – Antti

+1

@Antti它不仅是品味的问题。 R明智的操作在R是一种威慑语言和所有的情况下是直观的。你已经选择了最慢的解决方案。在我的答案中看到一些基准。所以请在你的时候定义“* neatest *”。 –

+0

@DavidArenburg我绝对同意R中的逐行循环不是一个快速的解决方案。在我的辩护中,我认为循环使得它更清晰一些,并且我试图保持它接近原始代码,这样逻辑会更容易让提问者遵循。 –