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
时,输出是 list
的 vector
。如果我们只是将第一个 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
让我们通过将代码分成块来更仔细地研究一下。在每一列上,我们可以找到拆分 vector
s
的输出 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
我在 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
时,输出是 list
的 vector
。如果我们只是将第一个 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
让我们通过将代码分成块来更仔细地研究一下。在每一列上,我们可以找到拆分 vector
s
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