3

我正在进行一项研究项目,研究使某人更有可能投票的因素,重点是人们居住与投票站之间的距离。我为数百万人提供完整的选民登记和选民历史。有人可以通过多种方式投票(亲自、缺席、提前或临时)或不投票(未登记、登记但未投票或没有资格投票)。我的数据附带一列 (29),说明某人在给定选举中的投票方式。NULL 表示未注册,V 表示本人等。

对于回归分析,我想为每种选民类型创建一个不同的列(1 表示是,0 表示否,第 68-74 列)和另一个 1/0 列(第 75 列)是否有人投票。我在下面编写的代码应该可以解决问题,但是它在我的计算机上运行得非常慢,一个小时后甚至无法到达第 1000 行。它工作得很好,除了速度。我已获准使用我大学的超级计算机*,但我想找出更快的算法。我的笔记本电脑和超级计算机* 上都有 R 和 STATA,我很乐意使用其中任何一个。

dcv.new <- read.csv("VoterHist.csv", header=TRUE)
# I previously set columns 68-75 to default to 0
for(i in 1:nrow(dcv.new))
{
  if(is.na(dcv.new[i,29]))
  {
    dcv.new[i,69] <- 1
  }
  else if(dcv.new[i,29]=="V")
  {
    dcv.new[i,68] <- 1
    dcv.new[i,75] <- 1
  }
  else if(dcv.new[i,29]=="A")
  {
    dcv.new[i,70] <- 1
    dcv.new[i,75] <- 1
  }
  else if(dcv.new[i,29]=="N")
  {
    dcv.new[i,71] <- 1
  }
  else if(dcv.new[i,29]=="E")
  {
    dcv.new[i,72] <- 1
  }
  else if(dcv.new[i,29]=="Y")
  {
    dcv.new[i,73] <- 1
  }
  else if(dcv.new[i,29]=="P")
  {
    dcv.new[i,74] <- 1
    dcv.new[i,75] <- 1
  }
  else if(dcv.new[i,29]=="X")
  {
    dcv.new[i,74] <- 1
    dcv.new[i,75] <- 1
  }
}

*技术上是“高性能计算集群”,但老实说,超级计算机听起来更酷。

4

2 回答 2

16

R 主要是矢量化的,因此请寻找矢量化操作来代替循环。在这种情况下,您可以对每个操作进行矢量化,使其适用于整个矩阵而不是单个行。

以下是您的前三个if else陈述:

dcv.new[is.na(dcv.new[,29]), 69] <- 1
dcv.new[dcv.new[,29]=="V", c(68,75)] <- 1
dcv.new[dcv.new[,29]=="A", c(70,75)] <- 1
....

你应该明白了。

一些解释:

我们正在做的是从dcv.new满足条件的某些列中选择行(例如== "V"),然后我们将值分配给单个操作中的1每个选定元素。dcv.newR 回收1我们分配的 ,使其长度与填充所有选定元素所需的长度相同。

请注意我们如何一次选择多个列进行更新:dcv.new[x , c(68,75)]更新行的第 68 和 75 列,其中是索引我们需要更新的行的逻辑向量。逻辑向量是由类似的语句产生的。这些返回一个if 一个等于的元素,如果不是。x xdcv.new[,29]=="V"TRUEdcv.new[,29]"V"FALSE

然而...!

在回归的情况下,我们可以让 R 为我们制作虚拟变量的矩阵,我们不需要手动完成。假设列dcv.new[, 29]名为voterType. 如果我们强迫它成为一个因素

dcv.new <- transform(dcv.new, voterType = factor(voterType))

当我们使用公式符号拟合模型时,我们可以这样做:

mod <- lm(response ~ voterType, data = dcv.new)

R 将创建适当的对比以voterType使用正确的自由度。默认情况下,R 使用因子的第一水平作为基准水平,因此模型系数表示与该参考水平的偏差。要查看voterType将其转换为因子后的参考水平是多少

with(dcv.new, levels(voterType)[1])

请注意,大多数采用公式的建模函数,如上图所示,按照我描述的方式工作,并在下面显示。您不仅限于lm()模型。

这是一个小例子

set.seed(42)
dcv.new <- data.frame(response = rnorm(20),
                      voterType = sample(c("V","A","N","E","Y","P","X",NA), 20, 
                                         replace = TRUE))
head(dcv.new)

> head(dcv.new)
    response voterType
1  1.3709584         E
2 -0.5646982         E
3  0.3631284         V
4  0.6328626      <NA>
5  0.4042683         E
6 -0.1061245      <NA>

然后可以将模型拟合为

mod <- lm(response ~ voterType, data = dcv.new)
summary(mod)

在这种情况下给予

> mod <- lm(response ~ voterType, data = dcv.new)
> summary(mod)

Call:
lm(formula = response ~ voterType, data = dcv.new)

Residuals:
    Min      1Q  Median      3Q     Max 
-2.8241 -0.4075  0.0000  0.5856  1.9030 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)  
(Intercept)   -2.656      1.425  -1.864   0.0952 .
voterTypeE     2.612      1.593   1.639   0.1356  
voterTypeN     3.040      1.646   1.847   0.0978 .
voterTypeP     2.742      1.646   1.666   0.1300  
voterTypeV     2.771      1.745   1.588   0.1468  
voterTypeX     2.378      2.015   1.180   0.2684  
voterTypeY     3.285      1.745   1.882   0.0925 .
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1 

Residual standard error: 1.425 on 9 degrees of freedom
  (4 observations deleted due to missingness)
Multiple R-squared: 0.3154, Adjusted R-squared: -0.1411 
F-statistic: 0.6909 on 6 and 9 DF,  p-value: 0.6635 

神奇的一切都发生在公式代码中,但基本上在幕后发生的事情是,一旦 R 找到了公式中命名的所有变量,它基本上最终会调用类似

model.matrix( ~ voterType, data = dcv.new)

它生成底层矩阵代数和 QR 分解所需的协变量矩阵。上面的代码,对于这个小例子给出了:

> model.matrix(~ voterType, data = dcv.new)
   (Intercept) voterTypeE voterTypeN voterTypeP voterTypeV voterTypeX
1            1          1          0          0          0          0
2            1          1          0          0          0          0
3            1          0          0          0          1          0
5            1          1          0          0          0          0
8            1          0          0          1          0          0
10           1          0          0          0          0          0
11           1          0          1          0          0          0
12           1          0          1          0          0          0
13           1          1          0          0          0          0
14           1          0          0          0          0          1
15           1          0          0          0          1          0
16           1          0          0          1          0          0
17           1          0          0          1          0          0
18           1          0          0          0          0          0
19           1          0          1          0          0          0
20           1          0          0          0          0          0
   voterTypeY
1           0
2           0
3           0
5           0
8           0
10          1
11          0
12          0
13          0
14          0
15          0
16          0
17          0
18          0
19          0
20          1
attr(,"assign")
[1] 0 1 1 1 1 1 1
attr(,"contrasts")
attr(,"contrasts")$voterType
[1] "contr.treatment"

这就是您想要对代码执行的操作。所以如果你真的需要它,你可以model.matrix()像我展示的那样使用它来生成矩阵——在你不需要它们时去掉属性。

在这种情况下,参考水平是"A"

> with(dcv.new, levels(voterType)[1])
[1] "A"

(Intercept)的输出中的列表示model.matrix。请注意,这些处理对比代码与参考水平的偏差。-1您可以通过添加(0r )来抑制公式中的截距来获得虚拟值+0

> model.matrix(~ voterType - 1, data = dcv.new)
   voterTypeA voterTypeE voterTypeN voterTypeP voterTypeV voterTypeX voterTypeY
1           0          1          0          0          0          0          0
2           0          1          0          0          0          0          0
3           0          0          0          0          1          0          0
5           0          1          0          0          0          0          0
8           0          0          0          1          0          0          0
10          0          0          0          0          0          0          1
11          0          0          1          0          0          0          0
12          0          0          1          0          0          0          0
13          0          1          0          0          0          0          0
14          0          0          0          0          0          1          0
15          0          0          0          0          1          0          0
16          0          0          0          1          0          0          0
17          0          0          0          1          0          0          0
18          1          0          0          0          0          0          0
19          0          0          1          0          0          0          0
20          0          0          0          0          0          0          1
attr(,"assign")
[1] 1 1 1 1 1 1 1
attr(,"contrasts")
attr(,"contrasts")$voterType
[1] "contr.treatment"
于 2013-03-19T22:22:25.710 回答
6

您应该矢量化您的代码。忘记那么多如果

dcv.new[is.na(dcv.new[,29]),69] <- 1
dcv.new[dcv.new[,29] == "V", c(68, 75)] <- 1

……enter code here

根据需要继续

于 2013-03-19T22:21:02.490 回答