Замена значения столбца при изменении значения другого столбца

#r #dataframe

#r #фрейм данных

Вопрос:

В приведенных ниже данных я хочу отслеживать U и Value столбец. Как только значение столбца Value изменится для строк, имеющих одинаковое значение в U столбце, я хочу присвоить U столбцу значение NA .

Есть предложения о том, как эффективно подойти к этому?

Входные данные

 data <- read.table(header = TRUE, text="
U   Value   Debug
A   1     1231
A   1     41
A   2     -1149
A   2     -2339
B   3     -3529
B   4     -4719
C   5     -5909
C   5     -7099
C   5     -8289
C   6     -9479
C   6     -10669
C   6     -11859
D   7     -13049
D   7     -14239
D   8     -15429
D   8     -16619")
  

Вывод текущей таблицы

 U   Value   Debug
A   1   1231
A   1   41
A   2   -1149
A   2   -2339
B   3   -3529
B   4   -4719
C   5   -5909
C   5   -7099
C   5   -8289
C   6   -9479
C   6   -10669
C   6   -11859
D   7   -13049
D   7   -14239
D   8   -15429
D   8   -16619
  

Ожидаемый вывод таблицы

 U   Value   Debug
A   1   1231
A   1   41
NA  2   -1149
NA  2   -2339
B   3   -3529
NA  4   -4719
C   5   -5909
C   5   -7099
C   5   -8289
NA  6   -9479
NA  6   -10669
NA  6   -11859
D   7   -13049
D   7   -14239
NA  8   -15429
NA  8   -16619

  

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

1. Я не понимаю, почему существует несколько групп из нескольких NA . Мне кажется, что только первое U значение в группе будет присвоено NA . Можете ли вы объяснить свой вывод?

2. .@TimBiegeleisen — Да. Рассмотрим первые четыре строки Current Table Output . Все они имеют одинаковое значение для столбца U . Я хочу назначить NA всем строкам после первого изменения значения в столбце Value . Затем та же операция будет применена к следующему подмножеству данных, имеющих значения столбца как B , а затем C и затем D .

Ответ №1:

Мы можем использовать data.table . Преобразуйте data.frame в data.table ( setDT(data) ), сгруппируйте по U , получите идентификатор длины выполнения Value столбца (на основе изменения значений, значение rleid инкрементов), преобразуйте его в двоичный файл с помощью оператора mod ( %% ), который преобразуется в логический путем отрицания ( ! ), так что 0 становятся TRUE и 1 FALSE , получите индекс строки TRUE значений ( .I ), извлеките этот столбец ( $V1 ) и используйте его как i для присвоения ( := ) значений U NA

 library(data.table)
setDT(data)[data[, .I[!rleid(Value) %%2], U]$V1, U := NA]
data
#       U Value  Debug
# 1:    A     1   1231
# 2:    A     1     41
# 3: <NA>     2  -1149
# 4: <NA>     2  -2339
# 5:    B     3  -3529
# 6: <NA>     4  -4719
# 7:    C     5  -5909
# 8:    C     5  -7099
# 9:    C     5  -8289
#10: <NA>     6  -9479
#11: <NA>     6 -10669
#12: <NA>     6 -11859
#13:    D     7 -13049
#14:    D     7 -14239
#15: <NA>     8 -15429
#16: <NA>     8 -16619
  

Обновить

Основываясь на обсуждении с OP, нам нужно назначить NA ‘U’, где ‘Value’ не является first ‘Значением’ для каждого ‘U’

 setDT(data)[data[,  .I[Value != first(Value)], .(U)]$V1, U := NA]
  

или та же логика в dplyr

 library(dplyr)
data %>% 
   group_by(U1 = U) %>%
   mutate(U = replace(U, Value != first(Value), NA)) %>%
   ungroup %>% 
   select(-U1)
  

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

1. .@akrun — Спасибо. Что V1 ? Могу ли я использовать ваше решение, если Value столбец нечисловой?

2. @ChetanArvindPatil Да, вы также можете использовать для нечисловых значений, rleid возвращает идентификатор длины выполнения на основе изменений в соседнем элементе. Здесь V1 является выводом столбца по умолчанию .I , т.е. извлеченный индекс

3. @ChetanArvindPatil Вы можете проверить вывод rleid(c("A", "A", "B", "A", "A"))

4. .@akrun — В больших данных, которые у меня есть, это только замена первого изменения, а затем сохраняет в остальных строках те же U значения, что и у него. Есть предположения, почему это так? Данные слишком велики для совместного использования.

5. @ChetanArvindPatil если шаблон данных похож на тот, который вы упомянули, он должен работать v1 <- c("A", "B", "A", "A", "B", "B");!rleid(v1) %% 2# [1] FALSE TRUE FALSE FALSE TRUE TRUE , если вы проверите здесь, это не только первое изменение, которое становится ИСТИННЫМ

Ответ №2:

Что-то вроде этого?

 data %>%
    group_by(U) %>%
    mutate(
        grp = cumsum(!(lag(Value, default = F) == Value)),
        U.new = ifelse(grp == 1, as.character(U), NA))
## A tibble: 16 x 5
## Groups:   U [4]
#   U     Value  Debug   grp U.new
#   <fct> <int>  <int> <int> <chr>
# 1 A         1   1231     1 A
# 2 A         1     41     1 A
# 3 A         2  -1149     2 NA
# 4 A         2  -2339     2 NA
# 5 B         3  -3529     1 B
# 6 B         4  -4719     2 NA
# 7 C         5  -5909     1 C
# 8 C         5  -7099     1 C
# 9 C         5  -8289     1 C
#10 C         6  -9479     2 NA
#11 C         6 -10669     2 NA
#12 C         6 -11859     2 NA
#13 D         7 -13049     1 D
#14 D         7 -14239     1 D
#15 D         8 -15429     2 NA
#16 D         8 -16619     2 NA
  

U.new Здесь я создаю новый столбец, поскольку мы группируем по U .


В ответ на ваш комментарий, для замены U на U.new вы можете сделать

 data %>%
    group_by(U) %>%
    mutate(
        grp = cumsum(!(lag(Value, default = F) == Value)),
        U.new = if_else(grp == 1, as.character(U), "NA")) %>%
    ungroup() %>%
    select(U = U.new, Value, Debug)
## A tibble: 16 x 3
#   U     Value  Debug
#   <chr> <int>  <int>
# 1 A         1   1231
# 2 A         1     41
# 3 NA        2  -1149
# 4 NA        2  -2339
# 5 B         3  -3529
# 6 NA        4  -4719
# 7 C         5  -5909
# 8 C         5  -7099
# 9 C         5  -8289
#10 NA        6  -9479
#11 NA        6 -10669
#12 NA        6 -11859
#13 D         7 -13049
#14 D         7 -14239
#15 NA        8 -15429
#16 NA        8 -16619
  

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

1. .@MauritsEvers — Спасибо. Разве невозможно просто обновить тот же фрейм данных?

2. @ChetanArvindPatil Нет, я не думаю, что это возможно напрямую, потому что вы группируете по U . Вы могли бы добавить несколько дополнительных шагов к (1) ungroup , (2) заменить U на U.new и (3) удалить U.new . Смотрите мой обновленный ответ.

3. .@MauritsEvers — Большие данные, которые у меня есть, имеют имя столбца, подобное Data-1 для столбца U . Я пробовал это Data-1.new , но это не работает. Есть предложения о том, как обработать такое имя столбца для вашего кода?

4. @ChetanArvindPatil Это довольно плохое (не похожее на R) имя столбца. Но это все равно будет работать, если вы используете обратные ссылки при создании нового столбца.

5. .@MauritsEvers — Отмечено, улучшится. Если столбец Value нечисловой, будет ли работать ваш код?

Ответ №3:

Другой вариант с dplyr заключается в том, чтобы для каждой группы ( U ) найти первую строку, которая Value отличается от предыдущей, и после этого изменить эти строки на NA .

 library(dplyr)

data %>%
  group_by(U) %>%
  mutate(U1 = replace(U, row_number() > which.max(diff(Value) != 0), NA))

#   U     Value  Debug U1   
#   <fct> <int>  <int> <fct>
# 1 A         1   1231 A    
# 2 A         1     41 A    
# 3 A         2  -1149 NA   
# 4 A         2  -2339 NA   
# 5 B         3  -3529 B    
# 6 B         4  -4719 NA   
# 7 C         5  -5909 C    
# 8 C         5  -7099 C    
# 9 C         5  -8289 C    
#10 C         6  -9479 NA   
#11 C         6 -10669 NA   
#12 C         6 -11859 NA   
#13 D         7 -13049 D    
#14 D         7 -14239 D    
#15 D         8 -15429 NA   
#16 D         8 -16619 NA   
  

Если в Value столбце могут быть нечисловые значения, мы можем использовать lag вместо diff

 data %>%
  group_by(U) %>%
  mutate(U1 = replace(U, row_number() >= which.max(Value != lag(Value)), NA))