如何在另一个索引中使用一个索引来定位变量的变化 - R

How to use an index within another index to locate a change in a variable - R

我有以下数据集。

id<-c(1001,1001,1001,1002,1002,1003,1004,1005,1005,1005)
year<-c(2010,2013,2016, 2013,2010,2010,2016,2016,2010,2013)
status<-c(2,2,2,3,4,2,1,1,1,5)
df<-data.frame(id, year, status)
df <- df[order(df$id, df$year), ]

我的目标是创建一个带有两个索引的 for 循环,一个用于 id,另一个用于 year,以便它首先运行 id,然后在每个 id 它查看 years,其中 status 发生了变化。为了记录这个循环的变化,我想要另一个变量来显示变化发生的地方。 例如,在下面的数据框中,变量 change 在所有三年中都为 id 1001 记录了 0。但是对于 1002,状态变化在 2013 年记录为 1。对于 1005,状态变化两次,分别在 2013 年和 2016 年,这就是为什么 1 被记录两次的原因。顺便说一句,id 是一个字符变量,因为我正在处理的真实数据有字母数字 id。

     id year status change
1  1001 2010      2   0
2  1001 2013      2   0
3  1001 2016      2   0
5  1002 2010      4   0
4  1002 2013      3   1
6  1003 2010      2   0
7  1004 2016      1   0
9  1005 2010      1   0
10 1005 2013      2   1
8  1005 2016      1   1

实际数据框有超过 60 万个观察值。循环需要很多时间 运行。我也愿意接受更快的解决方案。

我的代码如下:

df$change<-NA df$id<-as.character(df$id) for(id in unique(df$id)) {
    tau<-df$year[df$id==id]   if (length(tau)>1) {
    for( j in 1:(length(tau)-1)){ 
      if (df$status[df$year==tau[j] & df$id==id] != df$status[df$year==tau[j+1]& df$id==id]) {
       df$change[df$year==tau[j]    & df$id==id]<-0
       df$change[df$year==tau[j+1]  & df$id==id]<-1
    } else {
       df$change[df$year==tau[j]    & df$id==id]<-0
       df$change[df$year==tau[j+1]  & df$id==id]<-0
    }}}

这会产生正确的结果吗?

library(dplyr)

id<-c(1001,1001,1001,1002,1002,1003,1004,1005,1005,1005)
year<-c(2010,2013,2016, 2013,2010,2010,2016,2016,2010,2013)
status<-c(2,2,2,3,4,2,1,1,1,5)
df<-data.frame(id, year, status)
df <- df[order(df$id, df$year), ]

df %>%
  group_by(id) %>%
  mutate(change = as.numeric(status != lag(status,
                                           default = first(status))))
#> # A tibble: 10 x 4
#>       id  year status change
#>    <dbl> <dbl>  <dbl>  <dbl>
#>  1  1001  2010      2      0
#>  2  1001  2013      2      0
#>  3  1001  2016      2      0
#>  4  1002  2010      4      0
#>  5  1002  2013      3      1
#>  6  1003  2010      2      0
#>  7  1004  2016      1      0
#>  8  1005  2010      1      0
#>  9  1005  2013      5      1
#> 10  1005  2016      1      1

注意:我将“NA 替换”放在第二个 mutate 中,因为这一步不必在分组数据上进行,这对于大型数据集来说更快

你可以这样做:

基数 R:

df |> 
 transform(change = ave(status, id, FUN = \(x)c(0, diff(x))!=0))

在 tidyverse 中:

library(tidyverse)
df %>%
  group_by(id) %>%
  mutate(change = c(0, diff(status)!=0))

      id  year status change
   <dbl> <dbl>  <dbl>  <dbl>
 1  1001  2010      2      0
 2  1001  2013      2      0
 3  1001  2016      2      0
 4  1002  2010      4      0
 5  1002  2013      3      1
 6  1003  2010      2      0
 7  1004  2016      1      0
 8  1005  2010      1      0
 9  1005  2013      5      1
10  1005  2016      1      1

我们可以使用 ifelsestatuslag(status) 进行逻辑比较。关键是参数 default = first(status),它消除了输出中 NA 的常见问题。

df %>% group_by(id) %>%
mutate(change=ifelse(status==lag(status, default = first(status)), 0, 1))

# A tibble: 10 x 4
# Groups:   id [5]
      id  year status change
   <dbl> <dbl>  <dbl>  <dbl>
 1  1001  2010      2      0
 2  1001  2013      2      0
 3  1001  2016      2      0
 4  1002  2010      4      0
 5  1002  2013      3      1
 6  1003  2010      2      0
 7  1004  2016      1      0
 8  1005  2010      1      0
 9  1005  2013      5      1
10  1005  2016      1      1