我有一个数据框。我们称他为 bob
:
> head(bob)
phenotype exclusion
GSM399350 3- 4- 8- 25- 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399351 3- 4- 8- 25- 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399352 3- 4- 8- 25- 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399353 3- 4- 8- 25+ 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399354 3- 4- 8- 25+ 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399355 3- 4- 8- 25+ 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
我想连接这个数据框的行(这将是另一个问题)。但看:
> class(bob$phenotype)
[1] "factor"
Bob
的列是因子。因此,例如:
> as.character(head(bob))
[1] "c(3, 3, 3, 6, 6, 6)" "c(3, 3, 3, 3, 3, 3)"
[3] "c(29, 29, 29, 30, 30, 30)"
我开始不明白这一点,但我想这些是bob
的列(国王caractacus的法院)的因子水平的索引?不是我需要的。
奇怪的是我可以手动浏览bob
的列,然后做
bob$phenotype <- as.character(bob$phenotype)
效果很好。而且,在一些输入之后,我可以得到一个 data.frame,它的列是字符而不是因子。所以我的问题是:我怎样才能自动做到这一点?如何将具有因子列的 data.frame 转换为具有字符列的 data.frame 而无需手动遍历每一列?
额外的问题:为什么手动方法有效?
bob
的结构。
只是跟随马特和德克。如果要在不更改全局选项的情况下重新创建现有数据框,可以使用 apply 语句重新创建它:
bob <- data.frame(lapply(bob, as.character), stringsAsFactors=FALSE)
这会将所有变量转换为“字符”类,如果您只想转换因子,请参阅 Marek's solution below。
正如@hadley 指出的那样,以下内容更简洁。
bob[] <- lapply(bob, as.character)
在这两种情况下,lapply
都会输出一个列表;然而,由于 R 的神奇属性,在第二种情况下使用 []
保留了 bob
对象的 data.frame 类,从而消除了使用 as.data.frame
转换回 data.frame 的需要论点 stringsAsFactors = FALSE
。
仅替换因子:
i <- sapply(bob, is.factor)
bob[i] <- lapply(bob[i], as.character)
在包 dplyr in version 0.5.0 new function mutate_if
was introduced 中:
library(dplyr)
bob %>% mutate_if(is.factor, as.character) -> bob
...和in version 1.0.0 was replaced by across
:
library(dplyr)
bob %>% mutate(across(where(is.factor), as.character)) -> bob
Package purrr from RStudio 提供了另一种选择:
library(purrr)
bob %>% modify_if(is.factor, as.character) -> bob
purrr
行返回一个列表,而不是 data.frame
!
colnames()
向量的 i
,这也适用。
modify_if
而不是 map_if
:)
全局选项
stringsAsFactors:data.frame 和 read.table 参数的默认设置。
可能是您想在启动文件中设置为 FALSE
的内容(例如 ~/.Rprofile)。请参阅help(options)
。
如果您了解因子的存储方式,则可以避免使用基于应用的函数来完成此操作。这并不意味着应用解决方案不能很好地工作。
因子被构造为与“级别”列表相关的数字索引。如果将因子转换为数字,则可以看到这一点。所以:
> fact <- as.factor(c("a","b","a","d")
> fact
[1] a b a d
Levels: a b d
> as.numeric(fact)
[1] 1 2 1 3
最后一行返回的数字对应于因子的水平。
> levels(fact)
[1] "a" "b" "d"
请注意,levels()
返回一个字符数组。您可以使用此事实轻松而紧凑地将因子转换为字符串或数字,如下所示:
> fact_character <- levels(fact)[as.numeric(fact)]
> fact_character
[1] "a" "b" "a" "d"
如果您将表达式包装在 as.numeric()
中,这也适用于数值。
> num_fact <- factor(c(1,2,3,6,5,4))
> num_fact
[1] 1 2 3 6 5 4
Levels: 1 2 3 4 5 6
> num_num <- as.numeric(levels(num_fact)[as.numeric(num_fact)])
> num_num
[1] 1 2 3 6 5 4
as.character(f)
,在可读性和效率上都优于 levels(f)[as.numeric(f)]
。如果你想聪明一点,你可以用 levels(f)[f]
代替。请注意,当用数值转换因子时,您确实从 as.numeric(levels(f))[f]
中获得了一些好处,例如 as.numeric(as.character(f))
,但这是因为您只需将级别转换为数值,然后再转换为子集。 as.character(f)
就可以了。
如果您想要一个新的数据框 bobc
,其中 bobf
中的每个因子向量都转换为字符向量,请尝试以下操作:
bobc <- rapply(bobf, as.character, classes="factor", how="replace")
如果你想把它转换回来,你可以创建一个逻辑向量,其中列是因子,并使用它来有选择地应用因子
f <- sapply(bobf, class) == "factor"
bobc[,f] <- lapply(bobc[,f], factor)
我通常将此功能与我的所有项目分开。快捷方便。
unfactorize <- function(df){
for(i in which(sapply(df, class) == "factor")) df[[i]] = as.character(df[[i]])
return(df)
}
另一种方法是使用 apply 进行转换
bob2 <- apply(bob,2,as.character)
还有一个更好的(前一个是“矩阵”类)
bob2 <- as.data.frame(as.matrix(bob),stringsAsFactors=F)
as.data.frame(lapply(...
更新:这是一个不起作用的例子。我认为它会,但我认为 stringsAsFactors 选项仅适用于字符串 - 它不理会因素。
尝试这个:
bob2 <- data.frame(bob, stringsAsFactors = FALSE)
一般来说,每当您遇到应该是字符的因素时,有一个 stringsAsFactors
设置可以帮助您(包括全局设置)。
bob
开始时设置它(但不是事后),这确实有效。
或者您可以尝试 transform
:
newbob <- transform(bob, phenotype = as.character(phenotype))
只需确保将您想要转换为角色的每个因素都放入其中。
或者你可以这样做,一击杀死所有害虫:
newbob_char <- as.data.frame(lapply(bob[sapply(bob, is.factor)], as.character), stringsAsFactors = FALSE)
newbob_rest <- bob[!(sapply(bob, is.factor))]
newbob <- cbind(newbob_char, newbob_rest)
像这样在代码中推送数据不是好主意,我可以单独执行 sapply
部分(实际上,这样做更容易),但是你明白了......我没有检查代码,因为我不在家,所以我希望它有效! =)
然而,这种方法有一个缺点......您必须在之后重新组织列,而使用 transform
您可以做任何您喜欢的事情,但代价是“pedestrian-style-code-writting”。 ..
所以那里... =)
在数据框的开头包含 stringsAsFactors = FALSE
以忽略所有误解。
如果您将 data.table
包用于 data.frame 上的操作,则问题不存在。
library(data.table)
dt = data.table(col1 = c("a","b","c"), col2 = 1:3)
sapply(dt, class)
# col1 col2
#"character" "integer"
如果您的数据集中已经有一个因子列并且您想将它们转换为字符,您可以执行以下操作。
library(data.table)
dt = data.table(col1 = factor(c("a","b","c")), col2 = 1:3)
sapply(dt, class)
# col1 col2
# "factor" "integer"
upd.cols = sapply(dt, is.factor)
dt[, names(dt)[upd.cols] := lapply(.SD, as.character), .SDcols = upd.cols]
sapply(dt, class)
# col1 col2
#"character" "integer"
In [<-.data.table(*tmp*, sapply(bob, is.factor), : Coerced 'character' RHS to 'double' to match the column's type. Either change the target column to 'character' first (by creating a new 'character' vector length 1234 (nrows of entire table) and assign that; i.e. 'replace' column), or coerce RHS to 'double' (e.g. 1L, NA_[real|integer]_, as.*, etc) to make your intent clear and for speed. Or, set the column type correctly up front when you create the table and stick to it, please.
修复 DF 和重新创建 DT 更容易。
这对我有用——我终于想出了一个衬里
df <- as.data.frame(lapply(df,function (y) if(class(y)=="factor" ) as.character(y) else y),stringsAsFactors=F)
dplyr version 1.0.0 中引入了新功能“跨越”。新函数将取代作用域变量(_if、_at、_all)。这是官方的documentation
library(dplyr)
bob <- bob %>%
mutate(across(where(is.factor), as.character))
这个功能可以解决问题
df <- stacomirtools::killfactor(df)
您应该在 hablar
中使用 convert
,它提供与 tidyverse
管道兼容的可读语法:
library(dplyr)
library(hablar)
df <- tibble(a = factor(c(1, 2, 3, 4)),
b = factor(c(5, 6, 7, 8)))
df %>% convert(chr(a:b))
这给了你:
a b
<chr> <chr>
1 1 5
2 2 6
3 3 7
4 4 8
也许是一个更新的选择?
library("tidyverse")
bob <- bob %>% group_by_if(is.factor, as.character)
使用 dplyr
-package 加载
bob=bob%>%mutate_at("phenotype", as.character)
如果您只想专门更改 phenotype
列。
这可以将所有转换为字符,然后将数字转换为数字:
makenumcols<-function(df){
df<-as.data.frame(df)
df[] <- lapply(df, as.character)
cond <- apply(df, 2, function(x) {
x <- x[!is.na(x)]
all(suppressWarnings(!is.na(as.numeric(x))))
})
numeric_cols <- names(df)[cond]
df[,numeric_cols] <- sapply(df[,numeric_cols], as.numeric)
return(df)
}
改编自:Get column types of excel sheet automatically
character
后使用type.convert
,然后再次将factors
重新转换为character
。bob[] <-
还是bob <-
?;第一个保留data.frame;第二个将 data.frame 更改为列表,删除行名。我会更新答案iris[] <- lapply(iris, function(x) if (is.factor(x)) as.character(x) else {x})