如何为大型矩阵快速计算第一个特征值?

时间:2018-01-26 14:26:29

标签: r

我有一个非常大的矩阵(20000 * 20000),我想计算它的最大特征值。 当我在Matlab中进行操作时,需要几秒钟,但在R中,计算需要一个多小时。目前我使用的是rARPACK,计算时间需要数小时。

library("rARPACK")
eigs_sym(cov(TS), k = 1, which = "LM", opts = list(retvec = FALSE))

任何替代方案或解决方案?

2 个答案:

答案 0 :(得分:1)

rARPACK曾经是库ARPACK的包装器,但现在是Spectra的包装器,是相同算法的改进和重新实现版本,在many tests中优于ARPACK。

由于matlab cov(TS)函数也是ARPACK包的包装器,如果在两种情况下都有相同的参数,则问题似乎不太可能是解算器。

花时间的步骤是否可能是协方差矩阵的计算(即{{1}})?

答案 1 :(得分:0)

令我惊讶的是,RSpectra::eigs_sym的速度还没有base::eigen快。我做了一个基准,比较了各种样本量和特征值数量。

通常,RSpectra::eigs_symbase::eigen快3-5倍。但是考虑到base::eigen将花费20000 * 20000的永恒值,RSpectra::eigs_sym仍然应该花费相当长的四分之一时间!

enter image description here

library(RSpectra)
library(tidyverse)
library(microbenchmark)
library(clusterGeneration)
library(tictoc)

## generate S
S_dat <- tibble(size = c(50, 100, 500, 1000, 2000, 5000)) %>% 
  mutate(S=map(size, ~genPositiveDefMat(.)$Sigma))


### benvhmark function
bench_oneS <- function(S, n_eigens=5){
  dim <- nrow(S)
  n_rep <- case_when(dim<=1000 ~20,
                     TRUE~10)
  microbenchmark("base::eigen" = eigen(S)$values[1:n_eigens],
                 "RSpectra::eigs_sym" =eigs_sym(S,n_eigens, opts = list(retvec = FALSE))$values,
                 times=n_rep) %>% 
    summary(a, unit="ms") %>% 
    as_tibble() %>% 
    mutate(expr = as.character(expr))
}

## do benchmark
tic()
bench_out <- S_dat %>% 
  mutate(bench_5=map(S, ~bench_oneS(.)),
         bench_1=map(S, ~bench_oneS(., n_eigens=1))) %>% 
  dplyr::select(-S)%>% 
  pivot_longer(starts_with("bench"), names_to="n_eigens", values_to="bench",
               names_prefix="bench_", names_transform=list(n_eigens=as.integer)) %>% 
  unnest(bench)
toc()

##
pl_ratio <- bench_out %>% 
  dplyr::select(size, n_eigens, expr, mean) %>% 
  spread(expr, mean) %>% 
  mutate(ratio=`base::eigen` /`RSpectra::eigs_sym`) %>% 
  ggplot(aes(x=size, y= ratio, linetype=factor(n_eigens))) +
  geom_line()+
  geom_point()+
  labs(color="Method:", linetype="N eigens")+
  theme(legend.position="bottom")+
  ylab("Ratio time (eigen/eigs_sym)")+
  xlab("Sample size")+
  ggtitle("Ratio of average timing, base::eigen vs RSpectra::eigs_sym")