如何在不丢失信息的情况下将因子转换为整数\数值?

How to convert a factor to integer\numeric without loss of information?

提问人:Adam SO 提问时间:8/6/2010 最后编辑:JaapAdam SO 更新时间:8/4/2023 访问量:1130398

问:

当我将因子转换为数字或整数时,我得到的是基础级别代码,而不是数字形式的值。

f <- factor(sample(runif(5), 20, replace = TRUE))
##  [1] 0.0248644019011408 0.0248644019011408 0.179684827337041 
##  [4] 0.0284090070053935 0.363644931698218  0.363644931698218 
##  [7] 0.179684827337041  0.249704354675487  0.249704354675487 
## [10] 0.0248644019011408 0.249704354675487  0.0284090070053935
## [13] 0.179684827337041  0.0248644019011408 0.179684827337041 
## [16] 0.363644931698218  0.249704354675487  0.363644931698218 
## [19] 0.179684827337041  0.0284090070053935
## 5 Levels: 0.0248644019011408 0.0284090070053935 ... 0.363644931698218

as.numeric(f)
##  [1] 1 1 3 2 5 5 3 4 4 1 4 2 3 1 3 5 4 5 3 2

as.integer(f)
##  [1] 1 1 3 2 5 5 3 4 4 1 4 2 3 1 3 5 4 5 3 2

我必须求助于才能获得真正的价值:paste

as.numeric(paste(f))
##  [1] 0.02486440 0.02486440 0.17968483 0.02840901 0.36364493 0.36364493
##  [7] 0.17968483 0.24970435 0.24970435 0.02486440 0.24970435 0.02840901
## [13] 0.17968483 0.02486440 0.17968483 0.36364493 0.24970435 0.36364493
## [19] 0.17968483 0.02840901

有没有更好的方法可以将因子转换为数值?

铸造 R-常见问题

评论

7赞 CJB 1/25/2016
无论如何,因子的水平都存储为字符数据类型(),所以我认为 .也许最好先想想为什么(在特定情况下)你会得到一个因素,并尝试阻止它。例如,参数设置是否正确?attributes(f)as.numeric(paste(f))decread.table
0赞 davsjob 11/1/2018
如果使用 DataFrame,则可以使用 convert from hablar。.或者,如果你有一个因子向量,你可以使用df %>% convert(num(column))as_reliable_num(factor_vector)
1赞 Denis Cousineau 5/12/2022
谢谢你的这个问题。看到数字几乎随机转换为其他数字,这非常令人沮丧。

答:

842赞 Joshua Ulrich 8/6/2010 #1

请参阅 ?factor 的“警告”部分:

特别适用于: 一个因素是没有意义的,并且可能 通过隐性胁迫发生。自 将因子转换为 近似其原始数字 值,是 推荐和略多 效率比 .as.numericfas.numeric(levels(f))[f]as.numeric(as.character(f))

R 上的常见问题解答也有类似的建议


为什么as.numeric(levels(f))[f]比as.numeric(as.character(f更有效?

as.numeric(as.character(f))实际上是 ,因此您正在执行对值而不是值的数值的转换。对于电平很少的长向量,速度差异最为明显。如果这些值大多是唯一的,则速度不会有太大差异。无论您如何进行转换,此操作都不太可能成为代码中的瓶颈,因此不要太担心。as.numeric(levels(f)[f])length(x)nlevels(x)


一些时间安排

library(microbenchmark)
microbenchmark(
  as.numeric(levels(f))[f],
  as.numeric(levels(f)[f]),
  as.numeric(as.character(f)),
  paste0(x),
  paste(x),
  times = 1e5
)
## Unit: microseconds
##                         expr   min    lq      mean median     uq      max neval
##     as.numeric(levels(f))[f] 3.982 5.120  6.088624  5.405  5.974 1981.418 1e+05
##     as.numeric(levels(f)[f]) 5.973 7.111  8.352032  7.396  8.250 4256.380 1e+05
##  as.numeric(as.character(f)) 6.827 8.249  9.628264  8.534  9.671 1983.694 1e+05
##                    paste0(x) 7.964 9.387 11.026351  9.956 10.810 2911.257 1e+05
##                     paste(x) 7.965 9.387 11.127308  9.956 11.093 2419.458 1e+05

评论

5赞 Ari B. Friedman 8/8/2011
有关时间,请参阅此答案:stackoverflow.com/questions/6979625/...
4赞 Sam 4/18/2014
非常感谢您的解决方案。我能问为什么as.numeric(levels(f))[f]更精确、更快吗?谢谢。
8赞 Jonathan 6/28/2014
@Sam as.character(f) 需要“原始查找”来查找函数 as.character.factor(),该函数定义为 as.numeric(levels(f))[f]。
18赞 maycca 4/14/2016
当应用 as.numeric(levels(f))[f] OR as.numeric(as.character(f)) 时,我有一条警告消息:警告消息:NAs 引入强制。你知道问题可能出在哪里吗?谢谢!
1赞 MBorg 12/14/2020
@user08041991我和maycca有同样的问题。我怀疑这是由于 R 随着时间的推移而逐渐变化的(这个答案发布于 2010 年),这个答案现在已经过时了
108赞 Jealie 3/28/2014 #2

R 具有许多用于转换因子的(未记录的)便利函数:

  • as.character.factor
  • as.data.frame.factor
  • as.Date.factor
  • as.list.factor
  • as.vector.factor
  • ...

但令人讨厌的是,没有什么可以处理因数 -> 数值转换。作为约书亚·乌尔里希(Joshua Ulrich)答案的延伸,我建议通过定义自己的惯用函数来克服这一遗漏:

as.double.factor <- function(x) {as.numeric(levels(x))[x]}

您可以将其存储在脚本的开头,甚至更好地存储在 .Rprofile 文件。

评论

14赞 Joshua Ulrich 4/18/2014
无需处理因子到整数(或数值)的转换,因为预期会返回基础整数代码(如 的示例部分所示)。在全局环境中定义此函数可能是可以的,但如果您实际将其注册为 S3 方法,则可能会导致问题。as.integer(factor)?factor
2赞 Jealie 4/19/2014
这是一个很好的观点,我同意:对因子->数转换的完全重新定义可能会弄乱很多事情。我发现自己写了很多繁琐的转换,然后才意识到它实际上是 R 的一个缺点:应该提供一些便利功能......称它为我是有道理的,但 YMMV。factor->numericas.numeric.factor
8赞 Joshua Ulrich 4/19/2014
如果你发现自己经常这样做,那么你应该在上游做一些事情来避免它。
2赞 jO. 8/8/2014
as.numeric.factor 返回 NA?
1赞 Jealie 10/24/2021
@rui-barradas comment = 作为历史异常,R 有两种浮点向量类型:和 .根据文档,最好为该类型编写代码,因此似乎是一个更合适的名称。文档链接:stat.ethz.ch/R-manual/R-devel/library/base/html/numeric.html .谢谢@rui-barradas !numericdoubledoubleas.double.factor
13赞 djhurio 10/9/2015 #3

只有在因子标签与原始值匹配的情况下才有可能。我将用一个例子来解释它。

假设数据是向量:x

x <- c(20, 10, 30, 20, 10, 40, 10, 40)

现在,我将创建一个具有四个标签的因子:

f <- factor(x, levels = c(10, 20, 30, 40), labels = c("A", "B", "C", "D"))

1) 是 double 类型,是 integer 类型。这是第一次不可避免的信息丢失。因子始终存储为整数。xf

> typeof(x)
[1] "double"
> typeof(f)
[1] "integer"

2) 无法恢复到仅可用的原始值(10、20、30、40)。我们可以看到它只包含整数值 1、2、3、4 和两个属性——标签列表(“A”、“B”、“C”、“D”)和类属性“factor”。而已。ff

> str(f)
 Factor w/ 4 levels "A","B","C","D": 2 1 3 2 1 4 1 4
> attributes(f)
$levels
[1] "A" "B" "C" "D"

$class
[1] "factor"

要恢复到原始值,我们必须知道创建因子时使用的水平值。在这种情况下.如果我们知道原始级别(以正确的顺序),我们可以恢复到原始值。c(10, 20, 30, 40)

> orig_levels <- c(10, 20, 30, 40)
> x1 <- orig_levels[f]
> all.equal(x, x1)
[1] TRUE

只有当为原始数据中的所有可能值定义了标签时,这才有效。

因此,如果您需要原始值,则必须保留它们。否则,很有可能仅从一个因素中返回它们。

43赞 Mehrad Mahmoudian 12/1/2015 #4

最简单的方法是使用包 varhandle 中的函数,它可以接受因子向量甚至数据帧unfactor

unfactor(your_factor_variable)

此示例可以快速入门:

x <- rep(c("a", "b", "c"), 20)
y <- rep(c(1, 1, 0), 20)

class(x)  # -> "character"
class(y)  # -> "numeric"

x <- factor(x)
y <- factor(y)

class(x)  # -> "factor"
class(y)  # -> "factor"

library(varhandle)
x <- unfactor(x)
y <- unfactor(y)

class(x)  # -> "character"
class(y)  # -> "numeric"

您也可以在数据帧上使用它。例如,数据集:iris

sapply(iris, class)
Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species
   "numeric"    "numeric"    "numeric"    "numeric"     "factor"
# load the package
library("varhandle")
# pass the iris to unfactor
tmp_iris <- unfactor(iris)
# check the classes of the columns
sapply(tmp_iris, class)
Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species
   "numeric"    "numeric"    "numeric"    "numeric"  "character"
# check if the last column is correctly converted
tmp_iris$Species
  [1] "setosa"     "setosa"     "setosa"     "setosa"     "setosa"    
  [6] "setosa"     "setosa"     "setosa"     "setosa"     "setosa"    
 [11] "setosa"     "setosa"     "setosa"     "setosa"     "setosa"    
 [16] "setosa"     "setosa"     "setosa"     "setosa"     "setosa"    
 [21] "setosa"     "setosa"     "setosa"     "setosa"     "setosa"    
 [26] "setosa"     "setosa"     "setosa"     "setosa"     "setosa"    
 [31] "setosa"     "setosa"     "setosa"     "setosa"     "setosa"
 [36] "setosa"     "setosa"     "setosa"     "setosa"     "setosa"
 [41] "setosa"     "setosa"     "setosa"     "setosa"     "setosa"
 [46] "setosa"     "setosa"     "setosa"     "setosa"     "setosa"
 [51] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
 [56] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
 [61] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
 [66] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
 [71] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
 [76] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
 [81] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
 [86] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
 [91] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
 [96] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
[101] "virginica"  "virginica"  "virginica"  "virginica"  "virginica"
[106] "virginica"  "virginica"  "virginica"  "virginica"  "virginica"
[111] "virginica"  "virginica"  "virginica"  "virginica"  "virginica"
[116] "virginica"  "virginica"  "virginica"  "virginica"  "virginica"
[121] "virginica"  "virginica"  "virginica"  "virginica"  "virginica"
[126] "virginica"  "virginica"  "virginica"  "virginica"  "virginica"
[131] "virginica"  "virginica"  "virginica"  "virginica"  "virginica"
[136] "virginica"  "virginica"  "virginica"  "virginica"  "virginica"
[141] "virginica"  "virginica"  "virginica"  "virginica"  "virginica"
[146] "virginica"  "virginica"  "virginica"  "virginica"  "virginica"

评论

0赞 CJB 1/25/2016
该函数首先转换为字符数据类型,然后再转换回数字。在控制台上键入,您可以在函数中间看到它。因此,它并没有真正给出比提问者已经拥有的解决方案更好的解决方案。unfactorunfactor
0赞 CJB 1/25/2016
话虽如此,因子的水平无论如何都是字符类型的,因此这种方法不会丢失任何内容。
2赞 Mehrad Mahmoudian 9/29/2016
@Selrac我已经提到过这个函数在 varhandle 包中可用,这意味着你应该先加载包 ()(正如我在回答的第一行中提到的!!library("varhandle")
2赞 Gregor Thomas 11/9/2016
我很欣赏您的软件包可能也具有其他一些不错的功能,但是安装新软件包(并在代码中添加外部依赖项)并不像键入那样好或容易。as.character(as.numeric())
1赞 Mehrad Mahmoudian 11/10/2016
@Gregor添加轻度依赖通常不会造成伤害,当然,如果您正在寻找最有效的方法,编写代码可能会更快。但是,正如您在评论中看到的那样,这并不是微不足道的,因为您还将 和 放在错误的顺序;)你的代码块的作用是将因子的水平索引转换为字符矩阵,所以你将在 and 上得到一个字符向量,其中包含一些曾经分配给因子特定级别的数字。该包中的函数可以防止这些混淆as.numeric()as.character()
46赞 Indi 2/23/2017 #5

注意:此特定答案不是用于将数值因子转换为数值因子,而是用于将分类因子转换为相应的水平数字。


这篇文章中的每个答案都未能为我产生结果,NA 正在生成。

y2<-factor(c("A","B","C","D","A")); 
as.numeric(levels(y2))[y2] 
[1] NA NA NA NA NA Warning message: NAs introduced by coercion

对我有用的是——

as.integer(y2)
# [1] 1 2 3 4 1

评论

0赞 MrFlick 2/23/2017
你确定你有一个因素吗?请看这个例子。这将返回 4,1,3,2,而不是 5,15,20,2。这似乎是不正确的信息。y<-factor(c("5","15","20","2")); unclass(y) %>% as.numeric
6赞 MrFlick 2/23/2017
好吧,这不是上面提出的问题。在这个问题中,因子水平都是“数字”。在你的情况下,应该可以正常工作,不需要.但同样,这不是这个问题的意义所在。这个答案在这里不合适。as.numeric(y)unclass()
7赞 Indi 2/23/2017
好吧,我真的希望它能帮助像我这样匆忙的人,只阅读标题!
1赞 aimme 12/13/2019
如果您有将整数表示为因子的字符,这是我推荐的字符。这是唯一对我有用的方法。
1赞 luchonacho 9/23/2021
这是我们许多人所追求的答案,也是谷歌的第一个热门。我找不到类似的问题。
5赞 davsjob 11/1/2018 #6

如果您有数据框,则可以使用。语法很简单:hablar::convert

样本 df

library(hablar)
library(dplyr)

df <- dplyr::tibble(a = as.factor(c("7", "3")),
                    b = as.factor(c("1.5", "6.3")))

溶液

df %>% 
  convert(num(a, b))

为您提供:

# A tibble: 2 x 2
      a     b
  <dbl> <dbl>
1    7.  1.50
2    3.  6.30

或者,如果您希望一列为整数,而希望一列为数值:

df %>% 
  convert(int(a),
          num(b))

结果如下:

# A tibble: 2 x 2
      a     b
  <int> <dbl>
1     7  1.50
2     3  6.30

评论

0赞 Denis Cousineau 5/12/2022
但是,仅为该单个操作加载另一个包并不重要
4赞 Jerry T 11/13/2018 #7

游戏晚了,不小心,我发现可以转换成.然后你可以调用 .那是:trimws()factor(3:5)c("3","4","5")as.numeric()

as.numeric(trimws(x_factor_var))

评论

4赞 MrFlick 11/14/2018
您推荐使用已接受答案中所述的 over 有什么理由吗?在我看来,除非您实际上有需要删除的空格,否则只会做一堆不必要的正则表达式工作来返回相同的结果。trimwsas.charactertrimws
1赞 Jerry T 2/23/2019
as.numeric(levels(f))[f] 对于初学者来说可能有点令人困惑和难以记住。trimws 不会造成伤害。
-1赞 Xavier Prudent 11/28/2019 #8

从我能读到的许多答案中,唯一给出的方法是根据因子的数量来扩展变量的数量。如果你有一个级别为“狗”和“猫”的可变“宠物”,你最终会得到pet_dog和pet_cat。

就我而言,我想保持相同数量的变量,只需将因子变量转换为数值变量,其方式可以应用于具有许多级别的许多变量,例如 cat=1 和 dog=0。

请在下面找到相应的解决方案:

crime <- data.frame(city = c("SF", "SF", "NYC"),
                    year = c(1990, 2000, 1990),
                    crime = 1:3)

indx <- sapply(crime, is.factor)

crime[indx] <- lapply(crime[indx], function(x){ 
  listOri <- unique(x)
  listMod <- seq_along(listOri)
  res <- factor(x, levels=listOri)
  res <- as.numeric(res)
  return(res)
}
)
-2赞 Life_Searching_Steps 5/25/2020 #9

看起来解决方案as.numeric(levels(f))[f]不再适用于R 4.0。

替代解决方案:

factor2number <- function(x){
    data.frame(levels(x), 1:length(levels(x)), row.names = 1)[x, 1]
}

factor2number(yourFactor)

评论

0赞 Denis Cousineau 5/12/2022
??在 R 4.1 上,它确实有效。
3赞 lotus 6/17/2020 #10

type.convert(f)对于水平完全为数字的因子是另一个基本选项。

在性能方面,它大约相当于 ,但远不如 .as.numeric(as.character(f))as.numeric(levels(f))[f]

identical(type.convert(f), as.numeric(levels(f))[f])

[1] TRUE

也就是说,如果向量在第一个实例中被创建为因子的原因没有得到解决(即它可能包含一些无法强制转换为数字的字符),那么这种方法将不起作用,它将返回一个因子。

levels(f)[1] <- "some character level"
identical(type.convert(f), as.numeric(levels(f))[f])

[1] FALSE
5赞 Robert Bray 5/7/2021 #11

strtoi()如果您的因子水平为整数,则有效。

评论

0赞 Phil 7/7/2021
不错的简单解决方案,与其他解决方案一样快。
1赞 Timothy 10/17/2022 #12

如果有许多列要转换为 ,factornumeric

df <- rapply(df, function(x) as.numeric(levels(x))[x], "factor", how =  "replace")

如果所有因子水平都是数字,则此解决方案对于包含混合类型是可靠的。data.frames

0赞 Mark 3/13/2023 #13

我发现很难使用 tidyverse 语法在列名列表中应用。先转换为一个字符,然后转换为一个整数,然后为我提供了原始的数值,而无需添加额外的包。也许不是性能最高/最优雅的解决方案,但使事情变得简单易读。as.numeric(levels(f))[f]

library(tidyverse)

tbl_df <- tibble(a = as.factor(c("7", "3")),
                 b = as.factor(c("1.5", "6.3")))

cols <- c("a", "b")

tbl_df %>%
  mutate(across(all_of(cols), as.character)) %>% 
  mutate(across(all_of(cols), as.numeric))
0赞 Maël 8/4/2023 #14

该软件包包括一个包装器,将 和 和 包围起来。collapseas.numeric(levels(f))[f]as.character(levels(f))[f]as_numeric_factoras_character_factor

library(collapse)
set.seed(1)
f <- factor(sample(runif(5), 5, replace = TRUE))

as_numeric_factor(f)
# [1] 0.2016819 0.5728534 0.3721239 0.5728534 0.5728534

as_character_factor(f)
# [1] "0.201681931037456" "0.572853363351896" "0.37212389963679" "0.572853363351896" "0.572853363351896"

与 相比,它提供了类似的性能。as.numeric(levels(f))[f]

# Unit: milliseconds
#                      expr      min        lq       mean    median        uq      max neval
#  as.numeric(levels(f))[f]   2.6026   3.01305   5.834900   3.54310   8.57450  66.3497   100
#  as.numeric(levels(f)[f]) 317.2509 336.78690 350.215388 349.85620 361.57980 401.1002   100
#      as_numeric_factor(f)   2.5793   2.92970   5.383223   3.23355   4.29355  68.4460   100

法典:

set.seed(1)
f <- factor(sample(runif(5), 1e6, replace = TRUE))
library(microbenchmark)
microbenchmark(
  as.numeric(levels(f))[f],
  as.numeric(levels(f)[f]),
  as_numeric_factor(f),
  times = 100
)