Заменить значение столбца во фрейме данных на основе других столбцов - PullRequest
0 голосов
/ 02 ноября 2018

У меня есть следующий фрейм данных, упорядоченный по имени и времени.

set.seed(100)
df <- data.frame('name' = c(rep('x', 6), rep('y', 4)), 
                 'time' = c(rep(1, 2), rep(2, 3), 3, 1, 2, 3, 4),
                 'score' = c(0, sample(1:10, 3), 0, sample(1:10, 2), 0, sample(1:10, 2))
                 )
> df
   name time score
1     x    1     0
2     x    1     4
3     x    2     3
4     x    2     5
5     x    2     0
6     x    3     1
7     y    1     5
8     y    2     0
9     y    3     5
10    y    4     8

В df$score есть нули, за которыми следует неизвестное количество фактических значений, т. Е. df[1:4,], а иногда есть перекрывающиеся df$name между двумя df$score == 0, т.е. df[6:7,].

Я хочу изменить df$time, где df$score != 0. В частности, я хочу присвоить значение времени ближайшей верхней строки с помощью df$score == 0, если df$name соответствует.

Следующий код дает хорошие результаты, но мои данные содержат миллионы строк, поэтому это решение очень неэффективно.

score_0 <- append(which(df$score == 0), dim(df)[1] + 1)

for(i in 1:(length(score_0) - 1)) {
  df$time[score_0[i]:(score_0[i + 1] - 1)] <-
    ifelse(df$name[score_0[i]:(score_0[i + 1] - 1)] == df$name[score_0[i]], 
           df$time[score_0[i]], 
           df$time[score_0[i]:(score_0[i + 1] - 1)])
 }

> df
   name time score
1     x    1     0
2     x    1     4
3     x    1     3
4     x    1     5
5     x    2     0
6     x    2     1
7     y    1     5
8     y    2     0
9     y    2     5
10    y    2     8

Где score_0 дает индекс, где df$score == 0. Мы видим, что df$time[2:4] теперь все равны 1, что в df$time[6:7] изменился только первый, потому что второй имеет df$name == 'y', а ближайший верхний ряд с df$score == 0 имеет df$name == 'x'. Последние две строки также изменились правильно.

Ответы [ 2 ]

0 голосов
/ 02 ноября 2018

Вы можете сделать это так:

library(dplyr)
df %>% group_by(name) %>% mutate(ID=cumsum(score==0)) %>% 
       group_by(name,ID) %>% mutate(time = head(time,1)) %>% 
       ungroup() %>%  select(name,time,score) %>% as.data.frame()

#       name time  score
# 1     x    1     0
# 2     x    1     8
# 3     x    1    10
# 4     x    1     6
# 5     x    2     0
# 6     x    2     5
# 7     y    1     4
# 8     y    2     0
# 9     y    2     5
# 10    y    2     9
0 голосов
/ 02 ноября 2018

Решение с использованием dplyr и data.table:

library(data.table)
library(dplyr)

df %>%
  mutate(
    chck = score == 0,
    chck_rl = ifelse(score == 0, lead(rleid(chck)), rleid(chck))) %>% 
  group_by(name, chck_rl) %>% mutate(time = first(time)) %>% 
  ungroup() %>% 
  select(-chck_rl, -chck)

Выход:

# A tibble: 10 x 3
   name   time score
   <chr> <dbl> <int>
 1 x         1     0
 2 x         1     2
 3 x         1     9
 4 x         1     7
 5 x         2     0
 6 x         2     1
 7 y         1     8
 8 y         2     0
 9 y         2     2
10 y         2     3

Решение только с использованием data.table:

library(data.table)

setDT(df)[, chck_rl := ifelse(score == 0, shift(rleid(score == 0), type = "lead"), 
    rleid(score == 0))][, time := first(time), by = .(name, chck_rl)][, chck_rl := NULL]

Выход:

   name time score
 1:    x    1     0
 2:    x    1     2
 3:    x    1     9
 4:    x    1     7
 5:    x    2     0
 6:    x    2     1
 7:    y    1     8
 8:    y    2     0
 9:    y    2     2
10:    y    2     3
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...