R:避免在使用应用函数时将单行数据帧转换为向量
R: avoid turning one-row data frames into a vector when using apply functions
我经常遇到 R 将我的一列数据帧转换为字符向量的问题,我使用 drop=FALSE
选项解决了这个问题。
但是,在某些情况下,我不知道如何在 R 中解决此类行为,这就是其中之一。
我有一个如下所示的数据框:
mydf <- data.frame(ID=LETTERS[1:3], value1=paste(LETTERS[1:3], 1:3), value2=paste(rev(LETTERS)[1:3], 1:3))
看起来像:
> mydf
ID value1 value2
1 A A 1 Z 1
2 B B 2 Y 2
3 C C 3 X 3
我在这里做的任务是在除第一列之外的每一列中用 _
替换空格,我想为此使用 apply
族函数,sapply
在这种情况下。
我执行以下操作:
new_df <- as.data.frame(sapply(mydf[,-1,drop=F], function(x) gsub("\s+","_",x)))
new_df <- cbind(mydf[,1,drop=F], new_df)
生成的数据框看起来正是我想要的:
> new_df
ID value1 value2
1 A A_1 Z_1
2 B B_2 Y_2
3 C C_3 X_3
我的问题始于一些罕见的情况,在这些情况下,我的输入只能包含一行数据。出于某种原因,我一直不明白,R 在这些情况下有完全不同的行为,但没有 drop=FALSE
选项可以拯救我...
我现在的输入数据框是:
mydf <- data.frame(ID=LETTERS[1], value1=paste(LETTERS[1], 1), value2=paste(rev(LETTERS)[1], 1))
看起来像:
> mydf
ID value1 value2
1 A A 1 Z 1
但是,当我应用相同的代码时,生成的数据框看起来像这样丑陋:
> new_df
ID sapply(mydf[, -1, drop = F], function(x) gsub("\\s+", "_", x))
value1 A A_1
value2 A Z_1
如何解决这个问题,使同一行代码为任意行数的输入数据帧提供相同类型的结果?
一个更深层次的问题是 R 到底为什么要这样做?当我有一些新的奇怪输入时,我会继续回到我的代码 row/column 因为它们破坏了一切......谢谢!
您可以使用 lapply
而不是 sapply
来解决您的问题,然后使用 do.call
组合结果如下
new_df <- as.data.frame(lapply(mydf[,-1,drop=F], function(x) gsub("\s+","_",x)))
new_df <- do.call(cbind, new_df)
new_df
# value1 value2
#[1,] "A_1" "Z_1"
new_df <- cbind(mydf[,1,drop=F], new_df)
#new_df
# ID value1 value2
#1 A A_1 Z_1
至于你关于sapply
的不可预测行为的问题,是因为sapply
中的s
代表简化,但简化后的结果不保证是数据框。它可以是数据框、矩阵或向量。
根据sapply
的文档:
sapply is a user-friendly version and wrapper of lapply by default
returning a vector, matrix or, if simplify = "array", an array if
appropriate, by applying simplify2array().
关于 simplify
参数:
logical or character string; should the result be simplified
to a vector, matrix or higher dimensional array if possible? For
sapply it must be named and not abbreviated. The default value, TRUE,
returns a vector or matrix if appropriate, whereas if simplify =
"array" the result may be an array of “rank” (=length(dim(.))) one
higher than the result of FUN(X[[i]]).
详细信息部分解释了它的行为与您所经历的不相似(重点来自我):
Simplification in sapply is only attempted if X has length greater
than zero and if the return values from all elements of X are all of
the same (positive) length. If the common length is one the result is
a vector, and if greater than one is a matrix with a column
corresponding to each element of X.
Hadley Wickham 也建议不要使用 sapply
:
I recommend that you avoid sapply() because it tries to simplify the
result, so it can return a list, a vector, or a matrix. This makes it
difficult to program with, and it should be avoided in non-interactive
settings
他还建议不要将 apply
与数据框一起使用。请参阅 Advanced R 了解更多说明。
您还可以使用 purrr
包中的 map_df
函数,它对对象的每个元素以及 returns 数据框应用一个函数:
library(dplyr)
library(purrr)
mydf %>%
mutate(map_df(select(cur_data(), starts_with("value")), ~ gsub("\s", "_", .x)))
ID value1 value2
1 A A_1 Z_1
与原始数据框:
ID value1 value2
1 A A_1 Z_1
2 B B_2 Y_2
3 C C_3 X_3
这是一个替换原始数据的解决方案。不过,不确定这是否适用于您的工作流程。请注意,我使用了 apply
用于按行或列处理 data.frames。
mydf <- data.frame(ID=LETTERS[1], value1=paste(LETTERS[1], 1), value2=paste(rev(LETTERS)[1], 1))
xy <- apply(X = mydf[, -1, drop = FALSE],
MARGIN = 2,
FUN = function(x) gsub("\s+", "_", x),
simplify = FALSE
)
xy <- do.call(cbind, xy)
xy <- as.data.frame(xy)
mydf[, -1] <- as.data.frame(xy)
mydf
ID value1 value2
1 A A_1 Z_1
我经常遇到 R 将我的一列数据帧转换为字符向量的问题,我使用 drop=FALSE
选项解决了这个问题。
但是,在某些情况下,我不知道如何在 R 中解决此类行为,这就是其中之一。
我有一个如下所示的数据框:
mydf <- data.frame(ID=LETTERS[1:3], value1=paste(LETTERS[1:3], 1:3), value2=paste(rev(LETTERS)[1:3], 1:3))
看起来像:
> mydf
ID value1 value2
1 A A 1 Z 1
2 B B 2 Y 2
3 C C 3 X 3
我在这里做的任务是在除第一列之外的每一列中用 _
替换空格,我想为此使用 apply
族函数,sapply
在这种情况下。
我执行以下操作:
new_df <- as.data.frame(sapply(mydf[,-1,drop=F], function(x) gsub("\s+","_",x)))
new_df <- cbind(mydf[,1,drop=F], new_df)
生成的数据框看起来正是我想要的:
> new_df
ID value1 value2
1 A A_1 Z_1
2 B B_2 Y_2
3 C C_3 X_3
我的问题始于一些罕见的情况,在这些情况下,我的输入只能包含一行数据。出于某种原因,我一直不明白,R 在这些情况下有完全不同的行为,但没有 drop=FALSE
选项可以拯救我...
我现在的输入数据框是:
mydf <- data.frame(ID=LETTERS[1], value1=paste(LETTERS[1], 1), value2=paste(rev(LETTERS)[1], 1))
看起来像:
> mydf
ID value1 value2
1 A A 1 Z 1
但是,当我应用相同的代码时,生成的数据框看起来像这样丑陋:
> new_df
ID sapply(mydf[, -1, drop = F], function(x) gsub("\\s+", "_", x))
value1 A A_1
value2 A Z_1
如何解决这个问题,使同一行代码为任意行数的输入数据帧提供相同类型的结果?
一个更深层次的问题是 R 到底为什么要这样做?当我有一些新的奇怪输入时,我会继续回到我的代码 row/column 因为它们破坏了一切......谢谢!
您可以使用 lapply
而不是 sapply
来解决您的问题,然后使用 do.call
组合结果如下
new_df <- as.data.frame(lapply(mydf[,-1,drop=F], function(x) gsub("\s+","_",x)))
new_df <- do.call(cbind, new_df)
new_df
# value1 value2
#[1,] "A_1" "Z_1"
new_df <- cbind(mydf[,1,drop=F], new_df)
#new_df
# ID value1 value2
#1 A A_1 Z_1
至于你关于sapply
的不可预测行为的问题,是因为sapply
中的s
代表简化,但简化后的结果不保证是数据框。它可以是数据框、矩阵或向量。
根据sapply
的文档:
sapply is a user-friendly version and wrapper of lapply by default returning a vector, matrix or, if simplify = "array", an array if appropriate, by applying simplify2array().
关于 simplify
参数:
logical or character string; should the result be simplified to a vector, matrix or higher dimensional array if possible? For sapply it must be named and not abbreviated. The default value, TRUE, returns a vector or matrix if appropriate, whereas if simplify = "array" the result may be an array of “rank” (=length(dim(.))) one higher than the result of FUN(X[[i]]).
详细信息部分解释了它的行为与您所经历的不相似(重点来自我):
Simplification in sapply is only attempted if X has length greater than zero and if the return values from all elements of X are all of the same (positive) length. If the common length is one the result is a vector, and if greater than one is a matrix with a column corresponding to each element of X.
Hadley Wickham 也建议不要使用 sapply
:
I recommend that you avoid sapply() because it tries to simplify the result, so it can return a list, a vector, or a matrix. This makes it difficult to program with, and it should be avoided in non-interactive settings
他还建议不要将 apply
与数据框一起使用。请参阅 Advanced R 了解更多说明。
您还可以使用 purrr
包中的 map_df
函数,它对对象的每个元素以及 returns 数据框应用一个函数:
library(dplyr)
library(purrr)
mydf %>%
mutate(map_df(select(cur_data(), starts_with("value")), ~ gsub("\s", "_", .x)))
ID value1 value2
1 A A_1 Z_1
与原始数据框:
ID value1 value2
1 A A_1 Z_1
2 B B_2 Y_2
3 C C_3 X_3
这是一个替换原始数据的解决方案。不过,不确定这是否适用于您的工作流程。请注意,我使用了 apply
用于按行或列处理 data.frames。
mydf <- data.frame(ID=LETTERS[1], value1=paste(LETTERS[1], 1), value2=paste(rev(LETTERS)[1], 1))
xy <- apply(X = mydf[, -1, drop = FALSE],
MARGIN = 2,
FUN = function(x) gsub("\s+", "_", x),
simplify = FALSE
)
xy <- do.call(cbind, xy)
xy <- as.data.frame(xy)
mydf[, -1] <- as.data.frame(xy)
mydf
ID value1 value2
1 A A_1 Z_1