ChatGPT解决这个技术问题 Extra ChatGPT

将 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的列(国王caractacus的法院)的因子水平的索引?不是我需要的。

奇怪的是我可以手动浏览bob的列,然后做

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

效果很好。而且,在一些输入之后,我可以得到一个 data.frame,它的列是字符而不是因子。所以我的问题是:我怎样才能自动做到这一点?如何将具有因子列的 data.frame 转换为具有字符列的 data.frame 而无需手动遍历每一列?

额外的问题:为什么手动方法有效?

如果您可以使问题可重现,那就太好了,因此请包含 bob 的结构。

C
Community

只是跟随马特和德克。如果要在不更改全局选项的情况下重新创建现有数据框,可以使用 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


Shane,这也会将数字列变成字符。
@Dirk:的确如此,尽管尚不清楚这是否是个问题。显然,预先正确地创建事物是最好的解决方案。我认为跨数据框自动转换数据类型并不容易。一种选择是使用上述方法,但在将所有内容转换为 character 后使用 type.convert,然后再次将 factors 重新转换为 character
这似乎丢弃了行名。
@piccolbo 您在示例中使用的是 bob[] <- 还是 bob <- ?;第一个保留data.frame;第二个将 data.frame 更改为列表,删除行名。我会更新答案
仅使用匿名函数将因子列转换为字符的变体:iris[] <- lapply(iris, function(x) if (is.factor(x)) as.character(x) else {x})
M
Marek

仅替换因子:

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

可悲的是,不为我工作。不知道为什么。可能是因为我有colnames?
@mohawkjohn 不应该是问题。你得到错误或结果不符合你的预期?
注意:purrr 行返回一个列表,而不是 data.frame
如果您已经有一个作为 colnames() 向量的 i,这也适用。
@RoyalTS 从一开始就应该是 modify_if 而不是 map_if :)
m
micstr

全局选项

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

可能是您想在启动文件中设置为 FALSE 的内容(例如 ~/.Rprofile)。请参阅help(options)


这样做的问题是,当您在缺少该 .Rprofile 文件的环境中执行代码时,您会遇到错误!
我倾向于在脚本的开头调用它,而不是在 .Rprofile 中设置。
D
De Novo

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

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

> 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

该答案没有解决问题,即如何将数据框中的 all 因子列转换为字符。 as.character(f),在可读性和效率上都优于 levels(f)[as.numeric(f)]。如果你想聪明一点,你可以用 levels(f)[f] 代替。请注意,当用数值转换因子时,您确实从 as.numeric(levels(f))[f] 中获得了一些好处,例如 as.numeric(as.character(f)),但这是因为您只需将级别转换为数值,然后再转换为子集。 as.character(f) 就可以了。
s
scentoni

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

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

如果你想把它转换回来,你可以创建一个逻辑向量,其中列是因子,并使用它来有选择地应用因子

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

+1 只做必要的事情(即不将整个 data.frame 转换为字符)。该解决方案对于包含混合类型的 data.frame 是健壮的。
此示例应位于 rapply 的“示例”部分,例如:stat.ethz.ch/R-manual/R-devel/library/base/html/rapply.html。任何人都知道如何要求这样吗?
如果您想以数据框结束,只需将 rapply 包装在 data.frame 调用中(使用设置为 FALSE 参数的 stringsAsFactors)
O
Omar Wagih

我通常将此功能与我的所有项目分开。快捷方便。

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

G
George Dontas

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

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

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

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

关注@Shane 的评论:为了获取 data.frame,请执行 as.data.frame(lapply(...
M
Matt Parker

更新:这是一个不起作用的例子。我认为它会,但我认为 stringsAsFactors 选项仅适用于字符串 - 它不理会因素。

尝试这个:

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

一般来说,每当您遇到应该是字符的因素时,有一个 stringsAsFactors 设置可以帮助您(包括全局设置)。


如果他在创建 bob 开始时设置它(但不是事后),这确实有效。
正确的。只是想明确一点,这本身并不能解决问题 - 但感谢您注意到它确实阻止了它。
a
aL3xa

或者您可以尝试 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 以忽略所有误解。


j
jangorecki

如果您将 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 提出的 sapply 修复: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 更容易。
u
user1617979

这对我有用——我终于想出了一个衬里

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

r
radhikesh93

dplyr version 1.0.0 中引入了新功能“跨越”。新函数将取代作用域变量(_if、_at、_all)。这是官方的documentation

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

我在我的答案中包含了这个变化。感谢引起我的注意。
没问题。我试图编辑您的答案,但被审核团队拒绝。
C
Cedric

这个功能可以解决问题

df <- stacomirtools::killfactor(df)

d
davsjob

您应该在 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   

r
rachelette

也许是一个更新的选择?

library("tidyverse")

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

n
nexonvantec

使用 dplyr-package 加载

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

如果您只想专门更改 phenotype 列。


F
Ferroao

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

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