sapply 不将创建的函数应用于 R 数据框中的所有行

sapply not applying a function created to all rows in R dataframe

我在 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"

当我们执行 strsplit 时,输出是 listvector。如果我们只是将第一个 list 元素与 [[1]] 子集,则将跳过其余元素。这里的第一个元素对应于第一行。但是,当我们对单个列执行相同操作时,它会遍历每个元素,然后执行 strsplit。使用第一个元素 [[1]] 不会有什么坏处,因为 list 的长度为 1。这里的情况有所不同。 list 元素的数量与每列的行数相同。因此,我们需要遍历 list(使用 sapply/lapply - 前者根据情况给出向量,而后者总是 return list

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

让我们通过将代码分成块来更仔细地研究一下。在每一列上,我们可以找到拆分 vectors

的输出 list
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

的第一个元素

因为您只想提取 , 之前的第一部分,您也可以

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, "^([^,]*)"))

这是另一个版本

lapply(X = df, FUN = function(x) sapply(strsplit(x = as.character(x), split = ","), FUN = head, n=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 在输出中,所以我想这意味着如果没有数字字符串,这意味着缺少某些东西。Missing = 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