我有一个数据框。我们叫他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,而无需手动遍历每列?
奖金问题:为什么手动方法有效?
答案 0 :(得分:332)
关注Matt和Dirk。如果要在不更改全局选项的情况下重新创建现有数据框,可以使用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类,从而无需使用转换回data.frame带有参数as.data.frame
的{{1}}。
答案 1 :(得分:271)
仅替换因素:
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
Package purrr from RStudio提供了另一种选择:
library(purrr)
library(dplyr)
bob %>% map_if(is.factor, as.character) %>% as_data_frame -> bob
(请记住它是新包装)
答案 2 :(得分:37)
全局选项
stringsAsFactors: data.frame和read.table参数的默认设置。
可能是您要在启动文件中设置为FALSE
的内容(例如〜/ .Rprofile)。请参阅help(options)
。
答案 3 :(得分:21)
如果您了解如何存储因子,则可以避免使用基于应用的函数来完成此操作。这并不意味着应用解决方案效果不佳。
因子被构造为与“水平”列表相关联的数字索引。如果将因子转换为数字,则可以看到这一点。所以:
> 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
答案 4 :(得分:17)
如果您想要一个新的数据框bobc
,其中 bobf
中的每个因子向量都会转换为字符向量,请尝试以下操作:
bobc <- rapply(bobf, as.character, classes="factor", how="replace")
如果您想要将其转换回来,您可以创建一个逻辑向量,其中列是因子,并使用它来有选择地应用因子
f <- sapply(bobf, class) == "factor"
bobc[,f] <- lapply(bobc[,f], factor)
答案 5 :(得分:12)
我通常将此功能与我的所有项目区分开来。快速而简单。
unfactorize <- function(df){
for(i in which(sapply(df, class) == "factor")) df[[i]] = as.character(df[[i]])
return(df)
}
答案 6 :(得分:7)
另一种方法是使用apply
转换它bob2 <- apply(bob,2,as.character)
一个更好的(前一个是'矩阵'类)
bob2 <- as.data.frame(as.matrix(bob),stringsAsFactors=F)
答案 7 :(得分:6)
或者您可以尝试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
你可以做任何你喜欢的事情,但代价是“行人风格的代码写入” ...
所以...... =)
答案 8 :(得分:6)
更新:这是一个不起作用的例子。我认为它会,但我认为stringsAsFactors选项仅适用于字符串 - 它只留下因素。
试试这个:
bob2 <- data.frame(bob, stringsAsFactors = FALSE)
一般来说,每当您遇到应该是字符的因素时,都会有stringsAsFactors
设置帮助您(包括全局设置)。
答案 9 :(得分:5)
在数据框的开头包含stringsAsFactors = FALSE
以忽略所有误解。
答案 10 :(得分:3)
如果您使用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"
答案 11 :(得分:2)
这对我有用 - 我终于找到了一个班轮
df <- as.data.frame(lapply(df,function (y) if(class(y)=="factor" ) as.character(y) else y),stringsAsFactors=F)
答案 12 :(得分:2)
也许是一个较新的选择?
library("tidyverse")
bob <- bob %>% group_by_if(is.factor, as.character)
答案 13 :(得分:2)
这个功能可以解决这个问题
df <- stacomirtools::killfactor(df)
答案 14 :(得分:1)
您应在convert
中使用hablar
,以提供与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
答案 15 :(得分:1)
在装入dplyr
软件包的情况下使用
bob=bob%>%mutate_at("phenotype", as.character)
如果您只想专门更改phenotype
列。
答案 16 :(得分:0)
这可以将所有字符转换为字符,然后将数字转换为数字:
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)
}
答案 17 :(得分:0)
dplyr version 1.0.0中引入了新功能“ across”。新函数将取代作用域变量(_if,_at,_all)。这是官方的documentation
library(dplyr)
bob <- bob %>%
mutate(across(where(is.factor), as.character))