【问题标题】:R: Generate new columns based on nested loopsR:基于嵌套循环生成新列
【发布时间】:2015-08-04 00:09:24
【问题描述】:

我是一名应用研究人员,主要处理全国性的注册数据,他正在从 Stata 过渡到 R。dplyr 软件包使我的大部分日常数据管理任务顺利进行。尽管如此,我目前正在努力让 R 基于嵌套循环生成新变量。

假设我们有以下关于 1990-1992 年间出生的六名参与者的数据集,并衡量他们在 2001-2004 年间的平均成绩。

* Stata
clear all
input id byear gpa2000 gpa2001 gpa2002 gpa2003 gpa2004
1 1990 1.2 1.3 1.4 1.5 1.3
2 1990 2.3 2.5 2.2 2.1 2.6
3 1991 3.1 3.9 3.4 3.5 4.0
4 1991 2.6 3.1 2.4 1.9 3.1
5 1992 1.4 1.8 3.2 2.3 3.2
6 1992 3.5 4.0 4.0 4.0 3.9
end
list

     +--------------------------------------------------------------+
     | id   byear   gpa2000   gpa2001   gpa2002   gpa2003   gpa2004 |
     |--------------------------------------------------------------|
  1. |  1    1990       1.2       1.3       1.4       1.5       1.3 |
  2. |  2    1990       2.3       2.5       2.2       2.1       2.6 |
  3. |  3    1991       3.1       3.9       3.4       3.5         4 |
  4. |  4    1991       2.6       3.1       2.4       1.9       3.1 |
  5. |  5    1992       1.4       1.8       3.2       2.3       3.2 |
  6. |  6    1992       3.5         4         4         4       3.9 |
     +--------------------------------------------------------------+

或者在 R 中等效:

df <- read.table(header=T, text="id byear gpa2000 gpa2001 gpa2002 gpa2003 gpa2004
1 1990 1.2 1.3 1.4 1.5 1.3
2 1990 2.3 2.5 2.2 2.1 2.6
3 1991 3.1 3.9 3.4 3.5 4.0
4 1991 2.6 3.1 2.4 1.9 3.1
5 1992 1.4 1.8 3.2 2.3 3.2
6 1992 3.5 4.0 4.0 4.0 3.9
") 

我现在想生成三个新变量来衡量每个参与者在 10-12 岁之间的 GPA (gpa_age10 ... gpa_age12)。

在 Stata 中,我通常会通过嵌套的 for 循环来做到这一点:

forval i = 10/12 {
    gen gpa_age`i' = .
    forval j = 1990/1992 {
        replace gpa_age`i' = gpa`=`j'+`i'' if byear == `j'
    }
}

这将产生以下数据集:

     +-----------------------------------------------------------------------------------------------+
     | id   byear   gpa2000   gpa2001   gpa2002   gpa2003   gpa2004   gpa_a~10   gpa_a~11   gpa_a~12 |
     |-----------------------------------------------------------------------------------------------|
  1. |  1    1990       1.2       1.3       1.4       1.5       1.3        1.2        1.3        1.4 |
  2. |  2    1990       2.3       2.5       2.2       2.1       2.6        2.3        2.5        2.2 |
  3. |  3    1991       3.1       3.9       3.4       3.5         4        3.9        3.4        3.5 |
  4. |  4    1991       2.6       3.1       2.4       1.9       3.1        3.1        2.4        1.9 |
  5. |  5    1992       1.4       1.8       3.2       2.3       3.2        3.2        2.3        3.2 |
  6. |  6    1992       3.5         4         4         4       3.9          4          4        3.9 |
     +-----------------------------------------------------------------------------------------------+

我知道这个 Stata 代码可能不会直接翻译成 R,但是在 R 中复制这些结果的最佳方法是什么?

【问题讨论】:

  • 您的 Stata 数据集具有很好的可重现性(使用 input..end),但既然您想用 R 来回答,您可能应该为它做同样的事情。这是一个指南:stackoverflow.com/a/28481250/1191259 在我看来,最好的方法是以长格式(id、byear、year、gpa)存储您的数据,因为 var name 解析是一种容易出错的编码方法并且完全没有必要在 R 中。如果你这样做,你可以添加一个年龄列 = year - byear。

标签: r stata


【解决方案1】:

您可以使用 reshape2 包将 data.frame 重塑为每行代表学生一年的表格。然后计算年龄变得微不足道。以下是完成此任务的完整代码,假设您上面的 data.frame 位于名为 dat 的变量中:

mdat <- melt(dat, id.vars=c('id', 'byear'), value.name='gpa')
mdat %>%
    mutate(year=as.numeric(gsub('gpa', '', variable))) %>%
    select(id, byear, year, gpa) %>%
    mutate(age=year-byear)

另外,你可以通过铸造熔化的data.frame来获得你请求的data.frame:

dcast(mdat, id + byear ~ age, value.var='gpa')
> id byear  8    9    10   11   12   13   14
> 1  1990   NA   NA   1.2  1.3  1.4  1.5  1.3
> 2  1990   NA   NA   2.3  2.5  2.2  2.1  2.6
> 3  1991   NA   3.1  3.9  3.4  3.5  4.0   NA
> 4  1991   NA   2.6  3.1  2.4  1.9  3.1   NA
> 5  1992   1.4  1.8  3.2  2.3  3.2   NA   NA
> 6  1992   3.5  4.0  4.0  4.0  3.9   NA   NA

【讨论】:

  • 非常感谢您(和弗兰克)的好建议。我对重塑数据集的保留源于这样一个事实,即我的大多数项目都涉及数以百万计的人,这些人被跟踪了数十年。我假设,数据集的重塑需要很长时间,考虑替代方法可能会更好。
  • 假设在您的示例中,您希望为可能缺失的值计算这些值,从而避免包括NA 值,这可能大大超过对非常大的数据集进行整形的成本。此外,如果您的数据已经是一个 data.frame,那么重塑在计算上应该相对便宜。 as.numeric(gsub 调用很可能是您在大型数据集中的计算瓶颈。
【解决方案2】:

我知道@cr1msonB1ade 已经很好地处理了这个问题,但是为了向 OP 显示 R 中的嵌套 for 循环版本以匹配发布的 Stata 代码:

for (i in 10:12) {
  for (j in 1990:1992) {
    gpadf[[paste0("gpa_age", i)]][gpadf$byear==j] <- 
              gpadf[[paste0("gpa", j+i)]][gpadf$byear==j]   
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-08-24
    • 1970-01-01
    • 2017-10-05
    • 1970-01-01
    • 2015-07-01
    • 2022-10-14
    • 1970-01-01
    相关资源
    最近更新 更多