【问题标题】:Arithmetic operations on R factorsR因子的算术运算
【发布时间】:2011-10-22 05:14:55
【问题描述】:

我有一个 R 数据框,我正在尝试从另一列中减去一列。我使用$ 运算符提取列,但列的类是“因子”,R 不会对因子执行算术运算。是否有特殊功能可以做到这一点?

【问题讨论】:

  • R 中的因子通常用于分类(或序数)数据。您如何定义分类数据的算术?

标签: r r-faq


【解决方案1】:

如果您真的希望使用因子的级别,那么您要么做错了事,要么太聪明了。

如果您所拥有的是包含存储在因子级别中的数字的因子,那么您希望首先使用 as.numeric(as.character(...)) 将其强制转换为数字:

dat <- data.frame(f=as.character(runif(10)))

您可以在此处查看访问因子索引和分配因子内容之间的区别:

> as.numeric(dat$f)
 [1]  9  7  2  1  4  6  5  3 10  8
> as.numeric(as.character(dat$f))
 [1] 0.6369432 0.4455214 0.1204000 0.0336245 0.2731787 0.4219241 0.2910194
 [8] 0.1868443 0.9443593 0.5784658

时间与仅在级别上进行转换的替代方法表明,如果级别不是每个元素唯一的,它会更快:

dat <- data.frame( f = sample(as.character(runif(10)),10^4,replace=TRUE) )
library(microbenchmark)
microbenchmark(
  as.numeric(as.character(dat$f)),
  as.numeric( levels(dat$f) )[dat$f] ,
  as.numeric( levels(dat$f)[dat$f] ),
  times=50
  )

                              expr     min      lq  median      uq     max
1  as.numeric(as.character(dat$f)) 7835865 7869228 7919699 7998399 9576694
2 as.numeric(levels(dat$f))[dat$f]  237814  242947  255778  270321  371263
3 as.numeric(levels(dat$f)[dat$f]) 7817045 7905156 7964610 8121583 9297819

因此,如果是length(levels(dat$f)) &lt; length(dat$f),请使用as.numeric(levels(dat$f))[dat$f] 以获得显着的速度增益。

如果length(levels(dat$f)) 约等于length(dat$f),则没有速度增益:

dat <- data.frame( f = as.character(runif(10^4) ) )
library(microbenchmark)
microbenchmark(
  as.numeric(as.character(dat$f)),
  as.numeric( levels(dat$f) )[dat$f] ,
  as.numeric( levels(dat$f)[dat$f] ),
  times=50
  )

                              expr     min      lq  median      uq      max
1  as.numeric(as.character(dat$f)) 7986423 8036895 8101480 8202850 12522842
2 as.numeric(levels(dat$f))[dat$f] 7815335 7866661 7949640 8102764 15809456
3 as.numeric(levels(dat$f)[dat$f]) 7989845 8040316 8122012 8330312 10420161

【讨论】:

  • 虽然,R 在因式分解之前进行排序很聪明,所以如果它们是整数,这个问题就无关紧要了。
  • @Brandon:除非有人用过relevel 或者整数序列不连续。假设关卡索引与关卡内容相同似乎是一个危险的假设。
  • 提示:使用 rbenchmark 代替 microbenchmark 以获得更易读的输出和相对速度。
  • @Joris:我喜欢 rbenchmark 的输出,但我认为 microbenchmark 更准确,因为它不包括 system.time() 引起的一些调用开销......
  • 好吧,准确在这里是一个相对的概念。重复分析三次,每次得到不同的数字。精确的毫秒是一件好事,但除此之外你会陷入随机性......
【解决方案2】:

您可以定义自己的运算符来执行此操作,请参阅? Arith。没有组泛型,你可以定义自己的二元运算符%operator%:

%-% <- function (factor1, factor2){
  # put in the code here to calculate difference 
  # of two factors (e.g. facor1 level cat - factor2 level mouse = ?)
}

【讨论】:

    【解决方案3】:

    您应该首先仔细检查您是如何提取数据的。如果这些是真正的数字列,R 应该认识到这一点(Excel 有时会搞砸)。无论哪种方式,它都可能被强制转换为一个因素,因为列中还有其他不受欢迎的因素。到目前为止,您收到的回复没有提到 as.numeric() 只返回级别编号。这意味着您不会对已转换为因子的实际数字执行操作,而是对与每个因子关联的级别数字执行操作。

    【讨论】:

      【解决方案4】:

      您需要将因子转换为数值数组。

      a <- factor(c(5,6,5))
      b <- factor(c(3,2,1))
      df <- data.frame(a, b)
      
      # WRONG: Factors can't be subtracted.
      df$a - df$b
      
      # CORRECT: Get the levels and substract
      as.numeric(levels(df$a)[df$a]) - as.numeric(levels(df$b)[df$b])
      

      【讨论】:

      • -1 这假设 a) 您的因子是有序的并且 b) 数据是区间缩放的。如果是这种情况,那么数据首先不应该是一个因素。
      • +1 因为这是一种比其他解决方案中给出的 as.numeric(as.character()) 更好的转换因子的方法。
      • Andrie:如果向量没有排序,减法是否有有意义的解释(当然,人们可能想做一组交集)?我怀疑数据导入存在问题,导致数据首先被考虑在内。它发生在我身上好几次了。然后,当然,正确的方法是分解数据并修复导入。
      • @Joris:这不是正确的做法,但看起来与正确的做法相似。如果您希望获得效率提升,对as.numeric 的调用应该只包含级别。请参阅我的基准答案。
      • @gsk3:谢谢,不知道所涉及的性能问题。当然,你的方式更有效率。
      猜你喜欢
      • 2019-03-04
      • 2017-10-31
      • 1970-01-01
      • 2012-02-12
      • 1970-01-01
      • 2016-03-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多