为什么R中的as.factor()速度如此慢,它可以改进吗?

huwehgph  于 2023-06-19  发布在  其他
关注(0)|答案(2)|浏览(176)

我最近发现as. factor()的运行速度非常慢,特别是在具有长字符串的字符向量上。这似乎是dplyr::mutate()语句中的一个特殊问题;在mutate()语句之外对vector的操作似乎要快得多。有没有什么方法可以加快这个函数的执行速度,或者用一个更快的函数来代替它?

mmvthczy

mmvthczy1#

使用factor()函数和“levels”参数要快得多。使用“层次”论点是关键;没有它,factor()和.factor()一样慢。示例如下:

require(microbenchmark)
require(tidyverse)

#Generate a random vector of 22-character long strings consisting of numeric characters
random_char_vec = sprintf("%022.0f", runif(1e7)*1e22)

#Put it into a tibble
random_num_tibble = tibble(random_char_vec = random_char_vec)

#The problem seems to be when the character string is very long; 
#if each element of random_char_vec is only five characters 
#this takes no time at all; 
#at 22 digits it takes over two minutes.

microbenchmark(
  {
    factor_random_num = as.factor(random_char_vec)
  },
  times=1)
Unit: seconds
                                                   expr      min       lq     mean
 {     factor_random_num = as.factor(random_char_vec) } 146.2098 146.2098 146.2098
   median       uq      max neval
 146.2098 146.2098 146.2098     1

#This takes two seconds.
microbenchmark(
  {
    factor_random_num = factor(random_char_vec, levels = unique(random_char_vec))
  },
  times=1)
Unit: seconds
                                                                                  expr
 {     factor_random_num = factor(random_char_vec, levels = unique(random_char_vec)) }
      min       lq     mean   median       uq      max neval
 1.796813 1.796813 1.796813 1.796813 1.796813 1.796813     1

#The key to the speedup is precomputing the levels; without setting levels, no speedup.
microbenchmark(
  {
    factor_random_num = factor(random_char_vec)
  },
  times=1)
Unit: seconds
                                                expr      min       lq     mean   median
 {     factor_random_num = factor(random_char_vec) } 123.8821 123.8821 123.8821 123.8821
       uq      max neval
 123.8821 123.8821     1

我不知道为什么在mutate()语句中调用factor()有时会导致如此缓慢的性能。
希望这对遇到同样问题的人有所帮助!

pn9klfpd

pn9klfpd2#

tl;dr基本函数通常可以通过执行更少的操作来加速,但通常不值得付出这种努力。data.table提供了as.factor的替代方案。

一般来说,如果函数体(your_func,在控制台中没有括号)没有像.Primitive.Internal这样的内容,那么可能会有显著的速度提升。问题是,这值得努力吗?
在我的机器上,一个基本情况,长度为1亿的200级因子,基本as.factor需要3秒。

library(data.table)
library(stringi)
s <- stri_rand_strings(200, 20)
set.seed(1)
chr <- sample(s, 1e8, TRUE)
dt <- data.table(chr = chr)

bench::mark(
  transform(dt, chr = as.factor(chr)),
  dplyr::mutate(dt, chr = as.factor(chr)), 
  check = FALSE
)

切换到data.table方法,使用(不可取的)包内部函数,这可以大致减半。

start = proc.time()
dt[,chr := data.table:::as_factor(chr)]
timetaken(start)

除非经常运行,否则转换不值得花时间键入它。

相关问题