在 R 中创建一个 运行 计数变量?

Creating a running counting variable in R?

我有一个足球比赛结果数据集,我希望通过创建一组类似于 World Football Elo 公式的 运行 评分来学习 R。我 运行 遇到了一些在 Excel 中看似简单的事情在 R 中并不完全直观的问题。例如,4270 个观测值中的前 15 个具有必要的变量:

       date t.1  t.2 m.result
1  19960406  DC   SJ      0.0
2  19960413 COL   KC      0.0
3  19960413  NE   TB      0.0
4  19960413 CLB   DC      1.0
5  19960413 LAG NYRB      1.0
6  19960414 FCD   SJ      0.5
7  19960418 FCD   KC      1.0
8  19960420  NE NYRB      1.0
9  19960420  DC  LAG      0.0
10 19960420 CLB   TB      0.0
11 19960421 COL  FCD      1.0
12 19960421  SJ   KC      0.5
13 19960427 CLB NYRB      1.0
14 19960427  DC   NE      0.5
15 19960428 FCD   TB      1.0

我希望能够创建一个新变量,它将是 运行 t.1 和 t.2 的总比赛数(即截至相关日期的实例 "DC" 出现在列 t.1 或 t.2):

           date t.1  t.2 m.result  ##t.1m    ##t.2m
    1  19960406  DC   SJ      0.0       1         1
    2  19960413 COL   KC      0.0       1         1
    3  19960413  NE   TB      0.0       1         1
    4  19960413 CLB   DC      1.0       1         2
    5  19960413 LAG NYRB      1.0       1         1
    6  19960414 FCD   SJ      0.5       1         2
    7  19960418 FCD   KC      1.0       2         2
    8  19960420  NE NYRB      1.0       2         2
    9  19960420  DC  LAG      0.0       3         2
    10 19960420 CLB   TB      0.0       2         2
    11 19960421 COL  FCD      1.0       2         3
    12 19960421  SJ   KC      0.5       3         3
    13 19960427 CLB NYRB      1.0       3         3
    14 19960427  DC   NE      0.5       4         3
    15 19960428 FCD   TB      1.0       4         3

在Excel中,这是一个(相对)简单的=SUMPRODUCT方程,例如:

E4=SUMPRODUCT((A:A<=A4)*(B:B=B4))+SUMPRODUCT((A:A<=A4)*(C:C=B4))

其中 E4 是 obs # 4 的 t.1m,A:A 是日期,B:B 是 t.1,C:C 是 t.2,等等

但在 R 中,我可以获得总和积(即 "DC" 在我的数据集中玩了 576 场比赛),但出于某种原因(可能是我是新手,不耐烦,对试用感到不安和错误)我只是迷失了如何对观察数据进行 运行 计数,尤其是如何将 运行 计数为一个变量,这对任何游戏评级指数都至关重要。我知道 'PlayerRatings' 存在,我觉得对于我的 R 教育,我应该能够在没有那个包的情况下在 R 套件中做到这一点。 plyr 或 dplyr 当然可以。

作为参考,这是我的数据供您 copy/paste 进入您的 R。

date<-c(19960406,19960413,19960413,19960413,19960413,19960414,19960418,19960420,19960420,19960420,19960421,19960421,19960427,19960427,19960428)
t.1<-c("DC","COL","NE","CLB","LAG","FCD","FCD","NE","DC","CLB","COL","SJ","CLB","DC","FCD")
t.2<-c("SJ","KC","TB","DC","NYRB","SJ","KC","NYRB","LAG","TB","FCD","KC","NYRB","NE","TB")
m.result<-c(0.0,0.0,0.0,1.0,1.0,0.5,1.0,1.0,0.0,0.0,1.0,0.5,1.0,0.5,1.0)
mtable<-data.frame(date,t.1,t.2,m.result)
mtable

这是一个非常简单的解决方案,虽然不漂亮但可以完成工作。

首先,只需更改您的数据以使比较更容易:

mtable<-data.frame(date,t.1,t.2,m.result, stringsAsFactors = FALSE)

编辑于:

如果你想确保匹配项按日期排序,你可以使用 order 正如 @eipi10 所指出的:

mtable = mtable[order(mtable$date), ]

请注意,如果日期格式的时间顺序不是整数顺序,您可以先使用 as.Date().

将它们转换为日期格式

我们要做的是,对于每一行,使用列 t.1t.2 的数据帧子集,以及从 1 到所述行的所有行。所以 1:1、1:2、1:3 等。在每个 运行,我们计算该团队出现的次数,并将其用作新列的结果.

mtable$t.1m <- sapply(1:nrow(mtable),
             function(i) sum(mtable[1:i, c("t.1", "t.2")] == mtable$t.1[i]))

这是为 t.1 中的团队完成的,在 == 之后我们可以为 t.2 做一些小的争论:

mtable$t.2m <- sapply(1:nrow(mtable),
             function(i) sum(mtable[1:i, c("t.1", "t.2")] == mtable$t.2[i]))

现在我们的数据框如下所示:

> mtable
       date t.1  t.2 m.result t.1m t.2m
1  19960406  DC   SJ      0.0    1    1
2  19960413 COL   KC      0.0    1    1
3  19960413  NE   TB      0.0    1    1
4  19960413 CLB   DC      1.0    1    2
5  19960413 LAG NYRB      1.0    1    1
6  19960414 FCD   SJ      0.5    1    2
7  19960418 FCD   KC      1.0    2    2
8  19960420  NE NYRB      1.0    2    2
9  19960420  DC  LAG      0.0    3    2
10 19960420 CLB   TB      0.0    2    2
11 19960421 COL  FCD      1.0    2    3
12 19960421  SJ   KC      0.5    3    3
13 19960427 CLB NYRB      1.0    3    3
14 19960427  DC   NE      0.5    4    3
15 19960428 FCD   TB      1.0    4    3

在您的数据创建步骤中,确保 stringsAsFactors = FALSE 以避免出现问题。然后就很容易做到了。 (编辑:我将其设为全部 dplyr 示例)

library(dplyr)

cross_count <- function(id, var) {
  length(which(mtable[id, var] == mtable[1:id, ] %>% select(t.1, t.2) %>% unlist))
}

mtable  %>% 
  arrange(date) %>% # This makes sure the dates are in order
  mutate(id = 1:nrow(.)) %>% 
  rowwise() %>% 
  mutate(t.1m = cross_count(id, 2), t.2m = cross_count(id, 3))




 date t.1  t.2 m.result id t.1m t.2m
1  19960406  DC   SJ      0.0  1    1    1
2  19960413 COL   KC      0.0  2    1    1
3  19960413  NE   TB      0.0  3    1    1
4  19960413 CLB   DC      1.0  4    1    2
5  19960413 LAG NYRB      1.0  5    1    1
6  19960414 FCD   SJ      0.5  6    1    2
7  19960418 FCD   KC      1.0  7    2    2
8  19960420  NE NYRB      1.0  8    2    2
9  19960420  DC  LAG      0.0  9    3    2
10 19960420 CLB   TB      0.0 10    2    2
11 19960421 COL  FCD      1.0 11    2    3
12 19960421  SJ   KC      0.5 12    3    3
13 19960427 CLB NYRB      1.0 13    3    3
14 19960427  DC   NE      0.5 14    4    3
15 19960428 FCD   TB      1.0 15    4    3

要使用您提到的类似方式完成此操作:

sum(mtable$t.1 == 'DC', mtable$t.2 == 'DC')

貌似t.1m和t.2m这两个栏目是用来记账的,你真的只对玩过的场数感兴趣?我使用 with() 来处理 mtable 的列,而不必每次都写 mtable

mtable$games <- with(mtable, {

如果某支球队参加比赛,它将作为球队 1 或球队 2 参加比赛

    played <- t.1 == "DC" | t.2 == "DC"

比较是矢量化的,将列 t.1 的每个元素与 "DC" 等进行比较,逻辑比较也是矢量化的,因此只有一个 |

数据中一个棘手的部分是几支球队在同一天比赛,而在比赛当天(显然)只有焦点球队应该增加。我通过弄清楚如何对比赛进行排序来解决这个问题,以便焦点团队在比赛当天总是排在最后

    o <- order(date, played)

然后计算游戏累计总和

    games <- cumsum(played[o])

并将游戏恢复到原来的顺序

    games[order(o)]
})

这是结果

> head(mtable, 11)
       date t.1  t.2 m.result games
1  19960406  DC   SJ      0.0     1
2  19960413 COL   KC      0.0     1
3  19960413  NE   TB      0.0     1
4  19960413 CLB   DC      1.0     2
5  19960413 LAG NYRB      1.0     1
6  19960414 FCD   SJ      0.5     2
7  19960418 FCD   KC      1.0     2
8  19960420  NE NYRB      1.0     2
9  19960420  DC  LAG      0.0     3
10 19960420 CLB   TB      0.0     2
11 19960421 COL  FCD      1.0     3

这是一个实现此功能的函数,可以轻松指定焦点团队

gamesplayed <- function(date, t1, t2, focal="DC") {
    played <- t1 == focal | t2 == focal
    o <- order(date, played)
    cumsum(played[o])[order(o)]
}