根据其他列替换数据框中的列值

时间:2018-11-02 19:57:58

标签: r dataframe

我有以下按名称和时间排序的数据框。

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,即{{1 }}。

我要更改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现在都等于1,在df$time[2:4]中只有第一个改变了,因为第二个具有df$time[6:7],而最接近的具有df$name == 'y'的上一行具有{{ 1}}。最后两行也已正确更改。

2 个答案:

答案 0 :(得分:3)

您可以这样做:

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

答案 1 :(得分:1)

使用dplyrdata.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