Новая переменная, которая указывает на первое вхождение определенного значения - PullRequest
0 голосов
/ 26 ноября 2018

Я хочу создать новую переменную, которая указывает первое конкретное наблюдение значения для переменной.

В следующем наборе данных примера я хочу иметь новую переменную "firstna", которая равна "1" дляпервое наблюдение «NA» для этого игрока.

game_data <- data.frame(player = c(1,1,1,1,2,2,2,2), level = c(1,2,3,4,1,2,3,4), points = c(20,NA,NA,NA,20,40,NA,NA))

game_data
  player level points
1      1     1     20
2      1     2     NA
3      1     3     NA
4      1     4     NA
5      2     1     20
6      2     2     40
7      2     3     NA
8      2     4     NA

Полученный кадр данных должен выглядеть следующим образом:

game_data_new <- data.frame(player = c(1,1,1,1,2,2,2,2), level = c(1,2,3,4,1,2,3,4), points = c(20,NA,NA,NA,20,40,NA,NA), firstna = c(0,1,0,0,0,0,1,0))

game_data_new
  player level points firstna
1      1     1     20       0
2      1     2     NA       1
3      1     3     NA       0
4      1     4     NA       0
5      2     1     20       0
6      2     2     40       0
7      2     3     NA       1
8      2     4     NA       0

Если честно, я не знаю, как это сделать.Было бы идеально, если бы для этого была опция dplyr.

Ответы [ 8 ]

0 голосов
/ 04 декабря 2018

Опция, использующая diff

transform(game_data, firstna = ave(is.na(points), player, FUN = function(x) c(0,diff(x))))
#   player level points firstna
# 1      1     1     20       0
# 2      1     2     NA       1
# 3      1     3     NA       0
# 4      1     4     NA       0
# 5      2     1     20       0
# 6      2     2     40       0
# 7      2     3     NA       1
# 8      2     4     NA       0

И ее dplyr эквивалент:

library(dplyr)
game_data %>% group_by(player) %>% mutate(firstna = c(0,diff(is.na(points))))
# # A tibble: 8 x 4
# # Groups:   player [2]
#   player level points firstna
#    <dbl> <dbl>  <dbl>   <dbl>
# 1      1     1     20       0
# 2      1     2     NA       1
# 3      1     3     NA       0
# 4      1     4     NA       0
# 5      2     1     20       0
# 6      2     2     40       0
# 7      2     3     NA       1
# 8      2     4     NA       0
0 голосов
/ 26 ноября 2018

Еще одна опция ave, чтобы узнать сначала NA по группе (player).

game_data$firstna <- ave(game_data$points, game_data$player, 
                         FUN = function(x) cumsum(is.na(x)) == 1)

game_data
#  player level points firstna
#1      1     1     20       0
#2      1     2     NA       1
#3      1     3     NA       0
#4      1     4     NA       0
#5      2     1     20       0
#6      2     2     40       0
#7      2     3     NA       1
#8      2     4     NA       0
0 голосов
/ 26 ноября 2018

Другой способ использования базы:

game_data$firstna <-
unlist(
tapply(game_data$points, game_data$player, function(x) {i<-which(is.na(x))[1];x[]<-0;x[i]<-1;x})
)

или в качестве другого клона ?ave:

ave(game_data$points, game_data$player, FUN = function(x) {
    i<-which(is.na(x))[1];x[]<-0;x[i]<-1;x
})
0 голосов
/ 26 ноября 2018

База R решение:

ave(game_data$points, game_data$player,
    FUN = function(x) seq_along(x) == match(NA, x, nomatch = 0))
0 голосов
/ 26 ноября 2018
library(tidyverse)
library(data.table)

data.frame(
  player = c(1,1,1,1,2,2,2,2),
  level = c(1,2,3,4,1,2,3,4),
  points = c(20,NA,NA,NA,20,40,NA,NA)
) -> game_data

game_data_base1 <- game_data
game_data_dt <- data.table(game_data)

microbenchmark::microbenchmark(

  better_base = game_data$first_na <- ave(
    game_data$points,
    game_data$player,
    FUN=function(x) seq_along(x)==match(NA,x,nomatch=0)
  ),

  brute_base = do.call(
    rbind.data.frame,
    lapply(
      split(game_data, game_data$player),
      function(x) {
        x$firstna <- 0
        na_loc <- which(is.na(x$points))
        if (length(na_loc) > 0) x$firstna[na_loc[1]] <- 1
        x
      }
    )
  ),

  tidy = game_data %>%
    group_by(player) %>%
    mutate(firstna=as.numeric(is.na(points) & !duplicated(points))) %>%
    ungroup(),

  dt = game_data_dt[, firstna:=as.integer(is.na(points) & !is.na(shift(points))), player]

)
## Unit: microseconds
##         expr     min       lq      mean    median        uq        max neval
##  better_base 125.188  156.861  362.9829  191.6385  355.6675   3095.958   100
##   brute_base 366.642  450.002 2782.6621  658.0380 1072.6475 174373.974   100
##         tidy 998.924 1119.022 2528.3687 1509.0705 2516.9350  42406.778   100
##           dt 330.428  421.211 1031.9978  535.8415 1042.1240   9671.991   100
0 голосов
/ 26 ноября 2018
game_data %>% 
  group_by(player) %>% 
  mutate(firstna=as.numeric(is.na(points) & !duplicated(points)))

Группировка по игроку, затем создание логического вектора для случаев, которые являются как NA, так и не являются дубликатами для предыдущих строк.

# A tibble: 8 x 4
# Groups:   player [2]
  player level points firstna
   <dbl> <dbl>  <dbl>   <dbl>
1      1     1     20       0
2      1     2     NA       1
3      1     3     NA       0
4      1     4     NA       0
5      2     1     20       0
6      2     2     40       0
7      2     3     NA       1
8      2     4     NA       0

Если вы хотите, чтобы 1 в последней строке не-NA предшествовалиАН, замените строку мутирования следующим:

mutate(lastnonNA=as.numeric(!is.na(points) & is.na(lead(points))))

Первый ряд блока АН, который проходит до конца группы игроков:

game_data %>% 
  group_by(player) %>% 
  mutate(firstna=as.numeric(is.na(points) & !duplicated(cbind(points,cumsum(!is.na(points))))))
0 голосов
/ 26 ноября 2018

Вы можете сделать это, сгруппировавшись по игроку, а затем передвигаться, чтобы проверить, имеет ли строка значение NA, а предыдущая строка не

game_data %>%
  group_by(player) %>%
  mutate(firstna = ifelse(is.na(points) & lag(!is.na(points)),1,0)) %>%
  ungroup()

Результат:

# A tibble: 8 x 4
# Groups:   player [2]
  player level points firstna
   <dbl> <dbl>  <dbl>   <dbl>
1      1     1     20       0
2      1     2     NA       1
3      1     3     NA       0
4      1     4     NA       0
5      2     1     20       0
6      2     2     40       0
7      2     3     NA       1
8      2     4     NA       0
0 голосов
/ 26 ноября 2018

Вот решение с data.table:

library("data.table")

game_data <- data.table(player = c(1,1,1,1,2,2,2,2), level = c(1,2,3,4,1,2,3,4), points = c(20,NA,NA,NA,20,40,NA,NA))

game_data[, firstna:=is.na(points) & !is.na(shift(points)), player][]
# > game_data[, firstna:=is.na(points) & !is.na(shift(points)), player][]
#    player level points firstna
# 1:      1     1     20   FALSE
# 2:      1     2     NA    TRUE
# 3:      1     3     NA   FALSE
# 4:      1     4     NA   FALSE
# 5:      2     1     20   FALSE
# 6:      2     2     40   FALSE
# 7:      2     3     NA    TRUE
# 8:      2     4     NA   FALSE
...