如何从数据框中的行值创建字符串,忽略NA

时间:2014-11-04 16:36:34

标签: r dataframe

这是我的数据框:

x1 <- c("a", "c", "f", "j")
x2 <- c("b", "c", "g", "k")
x3 <- c("b", "d", "h", NA)
x4 <- c("a", "e", "i", NA)
df <- data.frame(x1, x2, x3, x4, stringsAsFactors=F)

df

  x1 x2   x3   x4
1  a  b    b    a
2  c  c    d    e
3  f  g    h    i
4  j  k <NA> <NA>

使用

apply(df, 1, paste, collapse = "_")

给了我

[1] "a_b_b_a"   "c_c_d_e"   "f_g_h_i"   "j_k_NA_NA"

我想忽略这些NA,所以最后得到的元素应该是&#34; j_k&#34;而不是&#34; j_k_NA_NA&#34;。

非常感谢您的支持。

拉​​米

4 个答案:

答案 0 :(得分:3)

使用您的代码,

apply(df, 1, function(x) paste(na.omit(x), collapse="_") )
#[1] "a_b_b_a" "c_c_d_e" "f_g_h_i" "j_k"    

另一种选择是

df[is.na(df)] <-''
 gsub("^_+|_+$", "", do.call(paste,c(df, sep="_")))
#[1] "a_b_b_a" "c_c_d_e" "f_g_h_i" "j_k"  

修改

如果有内部NAs,也许这有效

gsub("^_+|_+$|_+(?=_)", "", do.call(paste,c(df, sep="_")), perl=TRUE)

或基于@David Arenburg的评论

gsub("NA_|_NA", "", apply(df, 1, paste, collapse = "_"))

例如

v1 <- c(NA,'a', 'b', NA, NA, NA, 'c',NA, 'd', NA)
v1[is.na(v1)] <-''
gsub("^_+|_+$|_+(?=_)", "", paste(v1, collapse="_"), perl=TRUE)
#[1] "a_b_c_d"

答案 1 :(得分:2)

以下是使用zoo

的建议
library(zoo)
gsub("NA_|_NA", "", rollapply(t(df), width = 4, FUN = paste, collapse = "_"))
##      [,1]      [,2]      [,3]      [,4] 
## [1,] "a_b_b_a" "c_c_d_e" "f_g_h_i" "j_k"

答案 2 :(得分:1)

@ akrun的第二个选项很可能是最快的,但你也可以考虑这样的事情:

library(data.table)
na.omit(data.table(
  rn = rep(1:nrow(df), ncol(df)), 
  val = unlist(df, use.names = FALSE)))[, paste(val, collapse = "_"), by = rn]
#    rn   value
# 1:  1 a_b_b_a
# 2:  2 c_c_d_e
# 3:  3 f_g_h_i
# 4:  4     j_k

基本想法是从“长”data.table开始,删除NA值,然后将剩余值粘贴在一起。

对于此特定示例,在速度方面使用na.omit会产生差异。


更新

以下是一些使用相同样本数据(100K行)I shared at a related question的基准测试。

这些是我测试的功能:

AM <- function() {
  na.omit(data.table(
    rn = rep(1:nrow(df), ncol(df)), 
    val = unlist(df, use.names = FALSE)))[, paste(val, collapse = "_"), by = rn]
}

AK <- function() {
  df[is.na(df)] <-''
  gsub("^_+|_+$|_+(?=_)", "", do.call(paste,c(df, sep="_")), perl=TRUE)
}

RS <- function() {
  s <- split(df[!is.na(df)], row(df)[!is.na(df)])
  vapply(s, paste, character(1L), collapse = "_", USE.NAMES=FALSE)
}

结果:

microbenchmark(AM(), AK(), RS(), times = 50)
# Unit: milliseconds
#  expr       min        lq      mean    median        uq      max neval
#  AM()  819.4639  925.1636 1020.5084  979.6239 1118.8065 1384.873    50
#  AK()  490.6802  495.5576  559.4551  508.0861  602.8413 1192.798    50
#  RS() 1419.8630 1540.5424 1680.6115 1622.7701 1786.9931 2424.541    50

答案 3 :(得分:1)

您可以在删除了NA值的列表中使用vapply。这似乎是安全的。

> s <- split(df[!is.na(df)], row(df)[!is.na(df)])
> vapply(s, paste, character(1L), collapse = "_", USE.NAMES=FALSE)
[1] "a_b_b_a" "c_c_d_e" "f_g_h_i" "j_k"