如何根据条件创建一个累计添加前两行总和的列?

时间:2017-12-06 15:44:31

标签: r for-loop cumulative-sum

我之前尝试过问过这个问题,但说得不好。这是一个新尝试,因为我还没有解决它。

我有一个包含赢家,输家,日期,赢家点数和输家点数的数据集。

对于每一行,我想要两个新列,一个用于获胜者,一个用于失败者,用于显示他们到目前为止得分的数量(作为赢家和输家)。

示例数据:

winner <- c(1,2,3,1,2,3,1,2,3)
loser <-  c(3,1,1,2,1,1,3,1,2)
date <- c("2017-10-01","2017-10-02","2017-10-03","2017-10-04","2017-10-05","2017-10-06","2017-10-07","2017-10-08","2017-10-09")
winner_points <- c(2,1,2,1,2,1,2,1,2)
loser_points <- c(1,0,1,0,1,0,1,0,1)
test_data <- data.frame(winner, loser, date = as.Date(date), winner_points, loser_points)

我希望输出为:

winner_points_sum <- c(0, 0, 1, 3, 1, 3, 5, 3, 5)
loser_points_sum <- c(0, 2, 2, 1, 4, 5, 4, 7, 4)
test_data <- data.frame(winner, loser, date = as.Date(date), winner_points, loser_points, winner_points_sum, loser_points_sum)

到目前为止我是如何解决它的方法是做一个for循环,例如:

library(dplyr)
test_data$winner_points_sum_loop <- 0
test_data$loser_points_sum_loop <- 0

for(i in row.names(test_data)) {
  test_data[i,]$winner_points_sum_loop <-
    (
    test_data %>%
      dplyr::filter(winner == test_data[i,]$winner & date < test_data[i,]$date) %>%
      dplyr::summarise(points = sum(winner_points, na.rm = TRUE))
  +
    test_data %>%
      dplyr::filter(loser == test_data[i,]$winner & date < test_data[i,]$date) %>%
      dplyr::summarise(points = sum(loser_points, na.rm = TRUE))
    )
}

test_data$winner_points_sum_loop <- unlist(test_data$winner_points_sum_loop)

有任何建议如何解决这个问题?当行号加起来时,查询需要相当长的时间。我已尝试用AVE功能进行详细说明,我可以在一列中将玩家点数作为赢家,但无法弄清楚如何将他们的点数添加为输家。

This is the end result (except last column)

3 个答案:

答案 0 :(得分:2)

winner <- c(1,2,3,1,2,3,1,2,3)
loser <-  c(3,1,1,2,1,1,3,1,2)
date <- c("2017-10-01","2017-10-02","2017-10-03","2017-10-04","2017-10-05","2017-10-06","2017-10-07","2017-10-08","2017-10-09")
winner_points <- c(2,1,2,1,2,1,2,1,2)
loser_points <- c(1,0,1,0,1,0,1,0,1)
test_data <- data.frame(winner, loser, date = as.Date(date), winner_points, loser_points)


library(dplyr)
library(tidyr)

test_data %>%
  unite(winner, winner, winner_points) %>%                    # unite winner columns
  unite(loser, loser, loser_points) %>%                       # unite loser columns
  gather(type, pl_pts, winner, loser, -date) %>%              # reshape
  separate(pl_pts, c("player","points"), convert = T) %>%     # separate columns
  arrange(date) %>%                                           # order dates (in case it's not)
  group_by(player) %>%                                        # for each player
  mutate(sum_points = cumsum(points) - points) %>%            # get points up to that date
  ungroup() %>%                                               # forget the grouping
  unite(pl_pts_sumpts, player, points, sum_points) %>%        # unite columns
  spread(type, pl_pts_sumpts) %>%                             # reshape
  separate(loser, c("loser", "loser_points", "loser_points_sum"), convert = T) %>%                # separate columns and give appropriate names
  separate(winner, c("winner", "winner_points", "winner_points_sum"), convert = T) %>%
  select(winner, loser, date, winner_points, loser_points, winner_points_sum, loser_points_sum)   # select the order you prefer


# # A tibble: 9 x 7
#   winner loser       date winner_points loser_points winner_points_sum loser_points_sum
# *  <int> <int>     <date>         <int>        <int>             <int>            <int>
# 1      1     3 2017-10-01             2            1                 0                0
# 2      2     1 2017-10-02             1            0                 0                2
# 3      3     1 2017-10-03             2            1                 1                2
# 4      1     2 2017-10-04             1            0                 3                1
# 5      2     1 2017-10-05             2            1                 1                4
# 6      3     1 2017-10-06             1            0                 3                5
# 7      1     3 2017-10-07             2            1                 5                4
# 8      2     1 2017-10-08             1            0                 3                7
# 9      3     2 2017-10-09             2            1                 5                4

答案 1 :(得分:1)

我终于理解了你想要的东西。我采取了一种方法,在每个时间点获得每个玩家的累积积分,然后将其加入原始的winner <- c(1,2,3,1,2,3,1,2,3) loser <- c(3,1,1,2,1,1,3,1,2) date <- c("2017-10-01","2017-10-02","2017-10-03","2017-10-04","2017-10-05","2017-10-06","2017-10-07","2017-10-08","2017-10-09") winner_points <- c(2,1,2,1,2,1,2,1,2) loser_points <- c(1,0,1,0,1,0,1,0,1) test_data <- data.frame(winner, loser, date = as.Date(date), winner_points, loser_points) library(dplyr) library(tidyr) cum_points <- test_data %>% gather(end_game_status, player_id, winner, loser) %>% gather(which_point, how_many_points, winner_points, loser_points) %>% filter( (end_game_status == "winner" & which_point == "winner_points") | (end_game_status == "loser" & which_point == "loser_points")) %>% arrange(date = as.Date(date)) %>% group_by(player_id) %>% mutate(cumulative_points = cumsum(how_many_points)) %>% mutate(cumulative_points_sofar = lag(cumulative_points, default = 0)) select(player_id, date, cumulative_points) output <- test_data %>% left_join(cum_points, by = c('date', 'winner' = 'player_id')) %>% rename(winner_points_sum = cumulative_points_sofar) %>% left_join(cum_points, by = c('date', 'loser' = 'player_id')) %>% rename(loser_points_sum = cumulative_points_sofar) output 数据框。

pip install https://github.com/nithinmurali/pygsheets/archive/staging.zip

答案 2 :(得分:0)

previous question of the OP的区别在于OP现在要求每个玩家到目前为止得分的累积总和,即在实际日期之前。此外,样本数据集现在包含唯一标识每一行的date列。

因此,my previous approach也可以在这里使用,并进行一些修改。下面的解决方案将数据从宽格式转换为长格式,从而同时重塑两个值变量,计算每个玩家ID的累积总和,最后再次从长格式再格式化为宽格式。为了只计算在实际日期之前得分的点数,这些行会滞后一行。

请注意,winnerloser列包含相应的播放器ID。

library(data.table)
cols <- c("winner", "loser")
setDT(test_data)[
  # reshape multiple value variables simultaneously from wide to long format
  , melt(.SD, id.vars = "date", 
         measure.vars = list(cols, paste0(cols, "_points")), 
         value.name = c("id", "points"))][
           # rename variable column
           , variable := forcats::lvls_revalue(variable, cols)][
             # order by date and cumulate the lagged points by id
             order(date), points_sum := cumsum(shift(points, fill = 0)), by = id][
               # reshape multiple value variables simultaneously from long to wide format
               , dcast(.SD, date ~ variable, value.var = c("id", "points", "points_sum"))]
         date id_winner id_loser points_winner points_loser points_sum_winner points_sum_loser
1: 2017-10-01         1        3             2            1                 0                0
2: 2017-10-02         2        1             1            0                 0                2
3: 2017-10-03         3        1             2            1                 1                2
4: 2017-10-04         1        2             1            0                 3                1
5: 2017-10-05         2        1             2            1                 1                4
6: 2017-10-06         3        1             1            0                 3                5
7: 2017-10-07         1        3             2            1                 5                4
8: 2017-10-08         2        1             1            0                 3                7
9: 2017-10-09         3        2             2            1                 5                4