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


351

我有一个数据框。叫他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,而无需手动遍历每一列?

额外的问题:为什么手动方法起作用?


3
如果您想让问题重现,那就太好了,所以请包括的结构bob
jangorecki 2015年

Answers:


362

紧随马特和德克。如果要在不更改全局选项的情况下重新创建现有数据框,则可以使用一条apply语句重新创建它:

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

如果只想转换因子,这会将所有变量转换为“字符”类,请参阅下面的Marek解决方案

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

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

在两种情况下,都lapply输出一个列表。但是,由于R的神奇特性,[]在第二种情况下使用可以保留bob对象的data.frame类,从而消除了使用as.data.frame带有参数转换为data.frame的需要stringsAsFactors = FALSE


27
Shane,这还将把数字列变成字符。
德克·埃德比布特

@Dirk:是的,尽管目前尚不清楚这是否是一个问题。显然,正确地预先创建事物是最好的解决方案。我认为跨数据帧自动转换数据类型并不容易。一种选择是使用上面的方法,但是type.convert在将所有内容强制转换character为之后再使用,然后重铸factors回去character
Shane 2010年

这似乎放弃了行名。
piccolbo

2
bob[] <- 在示例中使用了@piccolbo 还是bob <- ??第一个保留data.frame; 第二个将data.frame更改为列表,删除行名。我将更新答案
David LeBauer 2014年

6
一种仅使用匿名函数将因子列转换为字符的变体: iris[] <- lapply(iris, function(x) if (is.factor(x)) as.character(x) else {x})
Stefan F

313

仅替换因素:

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

0.5.0版的mutate_if dplyr软件包,引入了新功能

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

RStudio的purrr软件包提供了另一种选择:

library(purrr)
library(dplyr)
bob %>% map_if(is.factor, as.character) %>% as_tibble -> bob

不幸的是,它不为我工作。不知道为什么。可能是因为我有姓?
Autumnsault 2014年

@mohawkjohn不会有问题。您有错误或结果不符合预期吗?
Marek

2
注意:该purrr行返回一个列表,而不是data.frame
RoyalTS

如果您已经有的i向量,这也可以使用colnames()
verbamour

39

全局选项

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

可能是您要FALSE在启动文件(例如〜/ .Rprofile)中设置的内容。请看help(options)


5
这样做的问题是,当您在缺少.Rprofile文件的环境中执行代码时,会遇到错误!
威斯汀薄

4
我倾向于在脚本开始时调用它,而不是在.Rprofile中设置。
gregmacfarlane,2015年

22

如果您了解因素的存储方式,则可以避免使用基于应用的函数来完成此操作。这一点并不意味着应用解决方案不能很好地工作。

因素被构造为与“级别”列表绑定的数字索引。如果将因子转换为数字,则可以看到。所以:

> 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)一切都很好。
De Novo

20

如果您想要一个新的数据帧bobc,其中将每个因子向量bobf都转换为字符向量,请尝试以下操作:

bobc <- rapply(bobf, as.character, classes="factor", how="replace")

如果您随后想要将其转换回去,则可以创建一个逻辑向量,其中哪些列是因子,然后使用该逻辑向量有选择地应用因子

f <- sapply(bobf, class) == "factor"
bobc[,f] <- lapply(bobc[,f], factor)

2
+1仅用于执行必要的操作(即,不将整个data.frame转换为字符)。该解决方案对于包含混合类型的data.frame是健壮的。
约书亚·乌尔里希

3
该示例应该在rapply的“示例”部分中,例如:stat.ethz.ch/R-manual/R-devel/library/base/html/rapply.html。任何人都知道如何要求那样吗?
mpettis

如果要结束数据帧,只需将rapply包装在data.frame调用中(使用stringsAsFactors设置为FALSE参数)
Taylored网站

13

我通常将此功能放在所有项目中。快捷方便。

unfactorize <- function(df){
  for(i in which(sapply(df, class) == "factor")) df[[i]] = as.character(df[[i]])
  return(df)
}

8

另一种方法是使用Apply进行转换

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

还有一个更好的(以前是“矩阵”类)

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

跟随@Shane的评论:为了获取data.frame,请执行as.data.frame(lapply(...
aL3xa 2010年

7

更新:这是不起作用的示例。我以为可以,但是我认为stringsAsFactors选项仅适用于字符串-它不考虑因素。

尝试这个:

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

一般来说,每当您遇到应该是字符的因素问题时,都会在stringsAsFactors某处提供帮助(包括全局设置)。


1
如果他在创建时将其设置bob为开始(但不是在事实之后),则此方法有效。
Shane 2010年

对。只是想弄清楚这本身并不能解决问题-但感谢您指出确实可以解决问题。
马特·帕克

7

或者您可以尝试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您可以随便做什么,但要付出“行人风格代码编写”的代价……

所以那里... =)


6

在数据框架的开头,请stringsAsFactors = FALSE忽略所有误解。


4

如果将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" 

DT规避了Marek提出的明智的修复方法: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更容易。
马特·钱伯斯

2

这对我有用-我终于想到了一个衬板

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


2

也许是更新的选择?

library("tidyverse")

bob <- bob %>% group_by_if(is.factor, as.character)

1

您应该使用converthablar这给可读的语法兼容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   

1

dplyr-package一起使用

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

如果您只想phenotype专门更改-column。


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

改编自:自动获取Excel工作表的列类型

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.