【问题标题】:sapply not applying a function created to all rows in R dataframesapply 未将创建的函数应用于 R 数据框中的所有行
【发布时间】:2018-08-01 23:19:33
【问题描述】:

我在 R 中有以下数据帧,并尝试使用字符串拆分函数来生成不同的数据帧

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 的第一个元素作为子集,即第一行带有[[1]]。不清楚你的预期。你想要什么输出
  • 是的。但是即使我对第一个元素进行子集化,拆分也适用于单个列

标签: r function sapply strsplit


【解决方案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==""] &lt;- 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 

【讨论】:

    【解决方案2】:

    这是另一个版本

    lapply(X = df, FUN = function(x) sapply(strsplit(x = as.character(x), split = ","), FUN = head, n=1))
    

    【讨论】:

      【解决方案3】:

      由于您只想提取, 之前的第一部分,您也可以这样做

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

      【讨论】:

        【解决方案4】:

        当我们执行strsplit 时,输出是vectors 中的list。如果我们只是用[[1]] 对第一个list 元素进行子集化,那么将跳过其余元素。这里第一个元素对应于第一行。但是,当我们对单个列执行相同操作时,它会遍历每个元素,然后执行 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 的拆分vectors

        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 的第一个元素

        【讨论】:

          猜你喜欢
          • 2021-08-28
          • 1970-01-01
          • 2022-01-04
          • 1970-01-01
          • 2020-05-24
          • 1970-01-01
          • 1970-01-01
          • 2017-03-09
          • 2021-06-29
          相关资源
          最近更新 更多