R 拆分并运行线性回归-使用data.table

R 拆分并运行线性回归-使用data.table,r,data.table,regression,R,Data.table,Regression,我问了前面的一个问题(),使用tidyverse和pipes提供了一个很好的解决方案。我正在为数据中的4000个“键”保存参数的p值,将它们放在单独的数据框中,然后运行一些直方图和其他视觉效果来检查4000个键中每个键的参数的重要性。在我运行它的前几次,这是有效的,但是在不同的参数/预测值上运行完全相同的代码,我不断收到一个错误,说: 汇总错误(lm(y1~x1+x2))$系数['x1',:下标超出范围 如果我在该键上运行单个模型并查看摘要,那么pvalue肯定存在于[x1,4]或[2,4]位置

我问了前面的一个问题(),使用tidyverse和pipes提供了一个很好的解决方案。我正在为数据中的4000个“键”保存参数的p值,将它们放在单独的数据框中,然后运行一些直方图和其他视觉效果来检查4000个键中每个键的参数的重要性。在我运行它的前几次,这是有效的,但是在不同的参数/预测值上运行完全相同的代码,我不断收到一个错误,说:

汇总错误(lm(y1~x1+x2))$系数['x1',:下标超出范围

如果我在该键上运行单个模型并查看摘要,那么pvalue肯定存在于
[x1,4]
[2,4]
位置,但它不会返回到模型中。有时它会运行,但随后会在
[3,4]
[4,4]
位置爆炸,等等

有人认为tidyverse正在杀死内存,从而将其炸飞。我知道这不是代码,因为它有时会工作,有时会比其他时间进步得更快,但这似乎很奇怪。因此,
data.table
是循环整个数据集的更好解决方案吗?我不熟悉如何将它们链接在一起
data.table
,那么如何使用
data.table
在我拥有的4000个键中的每一个键上运行一个模型,并使用我拥有的10个以上的参数重新创建以下代码

df
Key y1 x1 x2
A   10 1  3
A   11 2  4 
A   12 3  5
B   13 4  6 
B   14 5  7
B   15 6  8
C   16 7  9 
C   17 8  1
C   18 9  2

df %>% group_by(Key) %>%
  summarise(Intercept = lm(y1 ~ x1 + x2)$coefficients[1],
            Coeff_x1 = lm(y1 ~ x1 + x2)$coefficients[2],
            Coeff_x2 = lm(y1 ~ x1 + x2)$coefficients[3],
            R2 = summary(lm(y1 ~ x1 + x2))$r.squared,
            pvalue = summary(lm(y1 ~ x1 + x2))$coefficients["x1",4])

# A tibble: 3 x 6
  Key   Intercept Coeff_x1  Coeff_x2    R2     pvalue
  <chr>     <dbl>    <dbl>     <dbl> <dbl>      <dbl>
1 A            9.     1.00 NA            1   8.00e-16
2 B            9.     1.00 NA            1   7.00e-16
3 C            9.     1.00  7.86e-16     1 NaN

df
图例y1 x1 x2
A 1013
A 112 4
A 1235
B 13 4 6
B 14 5 7
B 15 6 8
C 16 7 9
C 17 8 1
C 18 9 2
df%%>%分组依据(键)%%>%
总结(截距=lm(y1~x1+x2)$系数[1],
Coeff_x1=lm(y1~x1+x2)$系数[2],
Coeff_x2=lm(y1~x1+x2)$系数[3],
R2=汇总(lm(y1~x1+x2))$r.平方,
pvalue=汇总(lm(y1~x1+x2))$系数[“x1”,4])
#一个tibble:3x6
键截距系数x1系数x2 R2 pvalue
1 A 9.1.00 NA 18.00e-16
2 B 9.1.00 NA 17.00e-16
3 C 9.1.00 7.86e-16 1 NaN

使用开发版本的
dplyr
(即将在CRAN上发布为1.0版),您可以执行以下操作:

# devtools::install_github("tidyverse/dplyr")

library(tidyverse)

res = df %>% 
  nest_by(Key) %>%
  mutate(model=list(lm(y1 ~ x1 + x2, data=data)))

res %>% 
  summarise(broom::tidy(model))
键r.squared p.value`(截距)`x1 x2
1A 18.00e-16 9.1.00NA
2B17.00e-169.1.00NA
3 C 1 NaN 9.1.00 7.86e-16

我在100000行数据帧上运行了上面的代码,其中包含4000个级别的
,在2018年的Macbook Pro上没有遇到任何内存问题。

我做了一个小测试,将OP的当前方法与
Lappy
+
data.table方法进行了比较。在1000行
数据上执行了1000次操作。table
具有26个唯一键(
keycol
):

这两种方法的执行时间有很大的差距,
lappy
方法在这个测试中更快


注意:我无法测试
tidyverse
的开发版本,该版本具有
nest\u by
功能(我的MacOS安装中存在Xcode问题)但是,由于OP的数据集有4000个键,因此也值得在测试中包含这些键。

这是一种相对直接的方法,它将中间结果(例如,
lm(…)
summary(lm(…)
分配给临时变量:

dt[, {LM = lm(y1 ~ x1 + x2, data = .SD)
      LM.summary = summary(LM)
    list(Intercept = LM$coefficients[1],
         Coeff_x1 = LM$coefficients[2],
         Coeff_x2 = LM$coefficients[3],
         R2 = LM.summary$r.squared,
         pvalue.x1 = LM.summary$coefficients["x1", 4],
         pvalue.x2 = LM.summary$coefficients["x2", 4])
    },
   by = Key]
{}
允许创建中间对象,然后我们只返回一个实际需要的列表

数据:

library(data.table)
dt = fread('Key y1 x1 x2
A   10 1  3
A   11 2  4 
A   12 3  5
A   13 4  5
B   13 4  6 
B   14 5  7
B   15 6  8
B   15 5  9
C   16 7  9 
C   17 8  1
C   18 9  2
C   18 9  2')

您可以使用
lappy
循环
key
的唯一值,并让函数返回一个
data.frame
和所需的系数,这是前一篇文章中的建议,但我得到一个错误
错误:无法分配大小为XX.x Mb的向量
,因此我认为我的机器可能太弱,即使对于t、 …这太疯狂了,因为18个月前它还是相当高端的…好吧,让我测试一下。我在[.tbl_df(dat,{:未使用的参数(by=Key))中得到一个错误
error
我需要做些什么来初始化data.table?我假设
dt
是您正在引用的数据帧?我使用了
fread
直接将您的表作为
data.table
读取。您需要添加
库(data.table);setDT(dt)
你的代码哇,它运行得非常快。非常感谢你的建议。但是,当我再添加一行来获取x2的pvalue时,它告诉我它超出了范围。即使我尝试说
pvalue=LM.summary$coverties[3,4])
它说它超出了范围。但是,如果我运行
pvalue=LM.summary$coverties[2,4])
它将正确地创建一个重复的my pValue列(与'x1'相比)。我不明白为什么除了截距和第一个参数之外,我无法从线性模型摘要中提取任何其他的PV值?我认为这与每个分组只有3个记录这一事实有关,并且拟合是完美的。请参见
摘要(lm(y1~x1+x2,数据=列表(y1=10:12,x1=1:3,x2=3:5)))$conferences
-它也只提供两个系数。
  Key   r.squared adj.r.squared      sigma statistic    p.value    df logLik   AIC   BIC deviance df.residual
  <fct>     <dbl>         <dbl>      <dbl>     <dbl>      <dbl> <int>  <dbl> <dbl> <dbl>    <dbl>       <int>
1 A             1             1   1.78e-15   6.34e29   8.00e-16     2   99.3 -193. -195. 3.16e-30           1
2 B             1             1   1.55e-15   8.28e29   7.00e-16     2   99.7 -193. -196. 2.42e-30           1
3 C             1           NaN NaN        NaN       NaN            3  Inf   -Inf  -Inf  0.                 0
library(broom)

res %>% 
  summarise(tidy(model), glance(model)) %>% 
  select(Key, term, estimate, r.squared, p.value) %>% 
  pivot_wider(names_from=term, values_from=estimate)
  Key   r.squared    p.value `(Intercept)`    x1        x2
  <fct>     <dbl>      <dbl>         <dbl> <dbl>     <dbl>
1 A             1   8.00e-16            9.  1.00 NA       
2 B             1   7.00e-16            9.  1.00 NA       
3 C             1 NaN                   9.  1.00  7.86e-16
set.seed(28)
dat <- data.table(keycol = sample(x = LETTERS, size = 1000, replace = T), 
                  x = rnorm(n = 1000, mean = 30, sd = 2), 
                  y = rnorm(n = 1000, mean = 20, sd = 2), 
                  z = rnorm(n = 1000, mean = 10, sd = 2))

speed_test <- benchmark(
  'data_table' = {
    model_list <- lapply(X = 1:26, function(z){  #X could be the unique keys or the 1:length(unique(keys))
      m <- lm(data = dat[keycol == LETTERS[z], ], formula = x ~ y + z)
      smry <- summary(m)
      ret_tbl <- data.table(intercept = smry$coefficients[1],
                            coef_y = smry$coefficients[2], 
                            coef_z = smry$coefficients[3],
                            r_squared = smry$adj.r.squared, 
                            pvale = smry$coefficients[2,4], 
                            keycol = z) 
      return(ret_tbl)

    })
    desired_tbl <- rbindlist(l = model_list, use.names = T, fill = T)
  }, 

  'tidyverse1' = {
    dat %>% group_by(keycol) %>%
      summarise(Intercept = lm(x ~ y + z)$coefficients[1],
                Coeff_y = lm(x ~ y + z)$coefficients[2],
                Coeff_z = lm(x ~ y + z)$coefficients[3],
                R2 = summary(lm(x ~ y + z))$r.squared,
                pvalue = summary(lm(x ~ y + z))$coefficients["y",4])
  }, 
  replications = 1000,
  columns = c("test", "replications", "elapsed")
)
> speed_test
        test replications elapsed
1 data_table         1000  29.477
2 tidyverse1         1000  88.781
dt[, {LM = lm(y1 ~ x1 + x2, data = .SD)
      LM.summary = summary(LM)
    list(Intercept = LM$coefficients[1],
         Coeff_x1 = LM$coefficients[2],
         Coeff_x2 = LM$coefficients[3],
         R2 = LM.summary$r.squared,
         pvalue.x1 = LM.summary$coefficients["x1", 4],
         pvalue.x2 = LM.summary$coefficients["x2", 4])
    },
   by = Key]
library(data.table)
dt = fread('Key y1 x1 x2
A   10 1  3
A   11 2  4 
A   12 3  5
A   13 4  5
B   13 4  6 
B   14 5  7
B   15 6  8
B   15 5  9
C   16 7  9 
C   17 8  1
C   18 9  2
C   18 9  2')