R — Вычисление скользящего среднего по значениям, соответствующим столбцу в нескольких столбцах

#r #mean

Вопрос:

Данные Испытаний

 library(data.table)
data<-data.table(
Date= c("2018-01-01", "2018-01-02", "2018-01-03", "2018-01-04", "2018-01-05", "2018-01-06"),
Winner= c("A","D","B","A","C","A"),
Loser=c("B","C","A","C","B","C"),
wpointp=c("52","58","51","61","59","55"),
lpointp=c("48","42","49","39","41","45"))
 

У меня есть таблица данных, в которой перечислены победители и проигравшие в играх, а также процент очков в игре, которые они выиграли. Я хотел бы иметь дополнительную колонку, которая вычисляет исторический процент очков победителей за прошлые игры(«wpoint.av»), независимо от того, были ли они победителями или проигравшими ранее.

Я знаю, что могу использовать следующий код, используя zoo и rollapply, чтобы получить среднее значение всех появлений победителя в столбце «Победитель», но я не могу также подсчитать, когда победитель также появлялся в столбце «проигравший».:

 data$wpointp<-as.numeric(data$wpointp)
data$lpointp<-as.numeric(data$lpointp)
data[,wpointp.av. := lag(rollapply(wpointp,mean,width=10000,align="right",partial=TRUE, fill=NA)),by="Winner"]
 

Таким образом, вместо того, чтобы «wpointp.av» возвращал 52 в строке 4, он должен возвращать 50,5 после усреднения wpointp в строке 1 и lpointp в строке 3, что соответствует двум играм А. Это то, что я могу сделать с помощью rollapply, или мне нужно найти отдельную функцию?

Спасибо

Комментарии:

1. Я не думаю, что понимаю, что именно вы хотите видеть в выходных данных?

Ответ №1:

Я понимаю, что вопрос заключается в том, чтобы для каждой строки найти более ранние строки, в которых победитель этой строки является либо победителем, либо проигравшим. Затем из этих строк возьмите wpointp для тех строк, для которых он является победителем, и lpointp для тех строк, для которых он является проигравшим, и усредните все эти числа.

1) sqldf Сначала мы делаем wpointp и lpointp числовыми, а затем слева соединяем результат с самим собой, используя указанное on условие, и получаем среднее значение, показанное с помощью avg .

 library(data.table)
library(sqldf)

data2 <- copy(data)
data2[, c("wpointp", "lpointp") := .(as.numeric(wpointp), as.numeric(lpointp))]

sqldf("select a.*, 
 avg((a.Winner = b.Winner)*b.wpointp   (a.Winner = b.Loser)*b.lpointp) 'wpoint.av',
 from data2 a
 left join data2 b
 on b.Date < a.Date and ((a.Winner = b.Winner) or (a.Winner = b.Loser))
 group by a.rowid")
 

дающий:

         Date Winner Loser wpointp lpointp wpoint.av
1 2018-01-01      A     B      52      48        NA
2 2018-01-02      D     C      58      42        NA
3 2018-01-03      B     A      51      49      48.0
4 2018-01-04      A     C      61      39      50.5
5 2018-01-05      C     B      59      41      40.5
6 2018-01-06      A     C      55      45      54.0
 

2) При этом используются только данные.таблица. Это может быть медленно, хотя я добавил несколько ускорений с начальной версии.

 Mean <- function(i) {
  w <- data2$Winner[i]
  data2[1:.N < i amp; (Winner == w | Loser == w),
     mean((Winner == w) * wpointp   (Loser == w) * lpointp)]
}
data2 <- copy(data)
data2[, c("wpointp", "lpointp") := .(as.numeric(wpointp), as.numeric(lpointp))]

data2[, wpoint.av := sapply(.I, Mean)]
data2
 

дающий:

          Date Winner Loser wpointp lpointp wpoint.av
1: 2018-01-01      A     B      52      48       NaN
2: 2018-01-02      D     C      58      42       NaN
3: 2018-01-03      B     A      51      49      48.0
4: 2018-01-04      A     C      61      39      50.5
5: 2018-01-05      C     B      59      41      40.5
6: 2018-01-06      A     C      55      45      54.0
 

3) dplyr/tidyr Преобразуйте указанные столбцы в числовые, преобразуйте в длинную форму, используйте cummean для вычисления скользящего среднего, извлеките строки-победители и соедините их с исходными данными.

 library(data.table)
library(dplyr)
library(tidyr)

data %>%
 mutate(wpointp = as.numeric(wpointp), lpointp = as.numeric(lpointp)) %>%
 pivot_longer(Winner:Loser) %>%
 group_by(value) %>%
 mutate(pointp.av = 
   lag(cummean((name=="Winner") * wpointp   (name=="Loser") * lpointp))) %>%
 ungroup %>%
 filter(name == "Winner") %>%
 select(Date, pointp.av) %>%
 right_join(data, by = "Date") %>%
 select(Date, Winner, Loser, wpointp, lpointp, pointp.av)
 

дающий:

 # A tibble: 6 x 6
  Date       Winner Loser wpointp lpointp pointp.av
  <chr>      <chr>  <chr> <chr>   <chr>       <dbl>
1 2018-01-01 A      B     52      48           NA  
2 2018-01-02 D      C     58      42           NA  
3 2018-01-03 B      A     51      49           48  
4 2018-01-04 A      C     61      39           50.5
5 2018-01-05 C      B     59      41           40.5
6 2018-01-06 A      C     55      45           54  
 

4) При этом используется только база R. Сначала преобразуйте в длинную форму с помощью изменения формы и отсортируйте ее в порядке дат. Они определяют функцию Кумма и применяют ее Победителем. В конце извлеките строки-победители.

 varying <- list(c("Winner", "Loser"), c("wpointp", "lpointp"))
long <- reshape(data, dir = "long", varying = varying)
long <- long[order(long$Date), ]
cumMean <- function(x) c(NA, head(cumsum(x), -1)) / (seq_along(x) - 1)
long2 <- transform(long, av = ave(as.numeric(wpointp), Winner, FUN = cumMean))
subset(long2, time == 1)
 

дающий:

          Date time Winner wpointp id   av
1: 2018-01-01    1      A      52  1   NA
2: 2018-01-02    1      D      58  2   NA
3: 2018-01-03    1      B      51  3 48.0
4: 2018-01-04    1      A      61  4 50.5
5: 2018-01-05    1      C      59  5 40.5
6: 2018-01-06    1      A      55  6 54.0
 

Комментарии:

1. Это отлично сработало, большое вам спасибо за такой исчерпывающий ответ. Я выбрал опцию таблицы данных просто для ознакомления, и я думаю, что мне было бы полезно узнать больше о настройке функций. Еще раз спасибо.