sapply没有应用为R数据帧中的所有行创建的函数

时间:2018-02-22 07:30:15

标签: r function sapply strsplit

我在R中有以下数据框,并尝试使用stringsplit函数来生成不同的数据框

DF
A         B       C
"1,2,3"        "1,2"
  "2"     "1"

数据框的单元格用字符填充。空格是空白值。我创建了以下函数

sepfunc<-function(x){strsplit(as.character(x, split= ","))[[1]][1]}

当我在一个列上使用它时,该功能可以正常工作

sapply(DF$A, sepfunc)

 [1] "1" "2"

但是,以下命令仅产生一行

sapply(DF, sepfunc)
 A        B       C
"1"       NA      "1"

不显示第二行。我知道我必须遗漏一些简陋的东西。我请求别人帮忙。

预期输出

  A        B       C
 "1"       NA      "1"
 "2"      "1"       "NA"

4 个答案:

答案 0 :(得分:3)

当我们执行strsplit时,输出为listvector。如果我们只使用list对第一个[[1]]元素进行子集化,则会跳过其余元素。这里第一个元素对应第一行。但是,当我们在一个列上执行相同操作时,它会循环遍历每个元素,然后执行strsplit。取第一个元素[[1]]不会有害,因为list长度为1.这里的情况不同。 list元素的数量与每列的行数相同。所以,我们需要循环遍历list(使用sapply/lapply - 前者给出一个向量取决于大小写,而后者总是返回list

sapply(DF, function(x) sapply(strsplit(as.character(x), ","), `[`, 1))
#      A   B   C  
#[1,] "1" NA  "1"
#[2,] "2" "1" NA 

让我们通过将代码分成块来更仔细地看待它。在每列上,我们可以找到分割list s

的输出vector
lapply(DF, function(x) strsplit(as.character(x), ","))
#$A
#$A[[1]]
#[1] "1" "2" "3"

#$A[[2]]  
#[1] "2"


#$B
#$B[[1]]
#[1] NA

#$B[[2]]
#[1] "1"


#$C
#$C[[1]]
#[1] "1" "2"

#$C[[2]]
#character(0)

当我们[[1]]时,第一个元素被提取,即第一行'A','B','C'

lapply(DF, function(x) strsplit(as.character(x), ",")[[1]])
#$A
#[1] "1" "2" "3"

#$B
#[1] NA

#$C
#[1] "1" "2"

如果我们再次对上面的元素(即第一个元素)进行子集化,则输出将为1 NA 1

相反,我们希望遍历list并获取每个list的第一个元素

答案 1 :(得分:2)

由于您只想在,之前提取第一部分,您也可以

sapply(DF, function(x) gsub("^([^,]*),.*$", "\\1", x))

#       A   B  C  
# [1,] "1" NA "1"
# [2,] "2" NA "1"

这将提取第一组(\\1),此处标有括号。 ([^,]*)

stringr

library(stringr)
sapply(DF, function(x) str_extract(x, "^([^,]*)"))

答案 2 :(得分:1)

这是

的另一个版本
lapply(X = df, FUN = function(x) sapply(strsplit(x = as.character(x), split = ","), FUN = head, n=1))

答案 3 :(得分:1)

首先,请注意您的sepfun应始终出错:

sepfunc<-function(x){strsplit(as.character(x, split= ","))[[1]][1]}

split应该与strsplit一起使用,而不是as.character,所以你的意思可能是:

sepfunc<-function(x){strsplit(as.character(x), split= ",")[[1]][1]}

第二,数据健全性问题。您将字符变量存储为因子,并将缺少的数据存储为空字符串。在尝试做任何其他事情之前,我建议处理这些问题。 (为什么我说NA在这里比空字符串更明智?因为你告诉我了。你想在输出中NA,所以我想这意味着如果有的话字符串中没有数字,这意味着缺少某些东西。缺少= NA。还有一个技术原因需要更长的时间才能解释。)

所以在下文中,我只使用了DF的更改版本:

DF <- data.frame(A=c("1,2,3", "2"), B=c(NA, "1"), C=c("1,2", NA), stringsAsFactors=FALSE)

(如果DF来自文件,那么您可以使用read.csv("file", as.is=TRUE)。然后使用DF[DF==""] <- NA。)

strsplit的输出为list,因此您需要sapply才能从中获取有用的内容。另一个sapply将其应用于数据框中的所有列。

sapply(DF, function(x) sapply(strsplit(x, ","), head, 1))
#      A   B   C  
# [1,] "1" NA  "1"
# [2,] "2" "1" NA 

或一步一步。在sapply对数据框的所有列进行函数之前,需要它为所有列提供有意义的结果。试试吧:

sf <- function(x) sapply(strsplit(x, ","), head, 1)
# and sepfunc as defined above:
sepfunc<-function(x){strsplit(as.character(x), split= ",")[[1]][1]}

sf(DF$A)
# [1] "1" "2"
# as expected

sepfunc(DF$A)
# [1] "1"

请注意,sepfunc仅使用每列的第一个元素(如您所说!),其余元素将被丢弃。您需要sapply或类似的东西才能使用所有元素。因此,你得到了这个:

sapply(DF, sepfunc)
#  A   B   C 
# "1"  NA "1" 

(它有效,因为我们已经将空字符串重新定义为NA。但是只能获得每个变量的第一行的结果。)

sapply(DF, sf)
# A   B   C  
# [1,] "1" NA  "1"
# [2,] "2" "1" NA