将data.frame列从因子转换为字符

我有一个数据帧。让我们称他为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的列(卡拉克塔克斯国王的法庭)的因子水平的指数?不是我需要的。

奇怪的是,我可以手动遍历bob的列,并且这样做

bob$phenotype <- as.character(bob$phenotype)

这很好。并且,在一些输入之后,我可以得到一个data.frame,它的列是字符而不是因子。我的问题是:我如何自动地做到这一点?我如何将一个data.frame与因子列转换为一个data.frame与字符列,而不必手动遍历每一列?

附加问题:为什么手动方法有效?

734553 次浏览

更新:这里有一个不起作用的例子。我认为它会,但我认为stringsAsFactors选项只适用于字符串-它离开因子单独。

试试这个:

bob2 <- data.frame(bob, stringsAsFactors = FALSE)

一般来说,当你遇到应该是字符的因子的问题时,在某个地方会有stringsAsFactors设置来帮助你(包括全局设置)。

全局选项

< p > stringsAsFactors:

. data.frame和read.table参数的默认设置

可能是你想在你的启动文件(例如~/. rprofile)中设置为FALSE的东西。请参见help(options)

另一种方法是使用apply转换它

bob2 <- apply(bob,2,as.character)

和一个更好的(前一个是'matrix'类)

bob2 <- as.data.frame(as.matrix(bob),stringsAsFactors=F)

只是跟着马特和德克。如果你想在不改变全局选项的情况下重新创建现有的数据帧,你可以用apply语句重新创建它:

bob <- data.frame(lapply(bob, as.character), stringsAsFactors=FALSE)

这将把所有变量转换为“character”类,如果你只想转换因子,请参见下面是Marek的解决方案

正如@hadley指出的那样,以下内容更简洁。

bob[] <- lapply(bob, as.character)

在这两种情况下,lapply输出一个列表;然而,由于R的神奇属性,在第二种情况下使用[]保留了bob对象的data.frame类,从而消除了使用as.data.frame和参数stringsAsFactors = FALSE转换回data.frame的需要。

或者你可以尝试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)

像这样把数据塞进代码中是不< em > < / em >的好主意,我可以单独做sapply部分(实际上,这样做要容易得多),但你明白了…我还没有检查代码,因为我不在家,所以我希望它能工作!=)

然而,这种方法有一个缺点……你必须在之后重新组织列,而使用transform你可以做任何你喜欢的事情,但代价是“pedestrian-style-code-writting”

所以…=)

仅替换因素:

i <- sapply(bob, is.factor)
bob[i] <- lapply(bob[i], as.character)

在包dplyr 在0.5.0版本中引入了新函数mutate_if中:

library(dplyr)
bob %>% mutate_if(is.factor, as.character) -> bob

...和在1.0.0版本中已被across取代:

library(dplyr)
bob %>% mutate(across(where(is.factor), as.character)) -> bob

从RStudio打包purrr给出了另一种选择:

library(purrr)
bob %>% modify_if(is.factor, as.character) -> bob

如果你想要一个新的数据帧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)
}

如果您了解因子是如何存储的,就可以避免使用基于应用程序的函数来实现这一点。这并不是说应用解决方案不能很好地工作。

因素的结构是与“级别”列表相关联的数字索引。如果将因数转换为数字,就可以看到这一点。所以:

> 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

这对我来说很管用——我终于想出了一句话

df <- as.data.frame(lapply(df,function (y) if(class(y)=="factor" ) as.character(y) else y),stringsAsFactors=F)

如果你使用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"

在数据帧的开头包含stringsAsFactors = FALSE以忽略所有误解。

这个函数很有用

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)

这可以将所有内容转换为字符,然后将数字转换为数字:

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)
}

改编自:自动获取excel表的列类型

dplyr-package加载使用

bob=bob%>%mutate_at("phenotype", as.character)

如果你只想具体地改变__abc0列。

新功能“across”;在dplyr 版本1.0.0中引入。新函数将取代作用域变量(_if, _at, _all)。下面是官方的文档

library(dplyr)
bob <- bob %>%
mutate(across(where(is.factor), as.character))