Изменить значение столбца на основе критериев и по группам

Мои данные выглядят так:

     year month flag group
 1: 1992     6    1     8
 2: 1992     7    0     8
 3: 1992     8    0     8
 4: 1992     9    0     8
 5: 1992    10    0     8
 6: 1992    11    0     8
 7: 1992    12    0     8
 8: 1995     6    0    10
 9: 1995     7    0    11
10: 1995     8    0    11
11: 1995     9    1    11
12: 1995    10    0    11
13: 1995    11    0    11
14: 1995    12    0    11
15: 1998     6    0    13
16: 1998     7    0    13
17: 1998     8    0    13
18: 1998     9    0    13
19: 1998    10    0    13
20: 1998    11    0    13
21: 1998    12    0    13

Что мне нужно сделать, так это присвоить значение 1 всем строкам, которые следуют за первым наблюдением 1 в столбце flag, однако это также должно быть сделано group.

В качестве конкретного примера я хочу этого:

     year month flag group
 1: 1992     6    1     8
 2: 1992     7    1     8
 3: 1992     8    1     8
 4: 1992     9    1     8
 5: 1992    10    1     8
 6: 1992    11    1     8
 7: 1992    12    1     8
 8: 1995     6    0    10
 9: 1995     7    0    11
10: 1995     8    0    11
11: 1995     9    1    11
12: 1995    10    1    11
13: 1995    11    1    11
14: 1995    12    1    11
15: 1998     6    0    13
16: 1998     7    0    13
17: 1998     8    0    13
18: 1998     9    0    13
19: 1998    10    0    13
20: 1998    11    0    13
21: 1998    12    0    13

Обратите внимание, что строки 1:7 теперь равны 1, а также строки 11:14, а также обратите внимание, что в строках 15:21 не было никаких изменений, поскольку изначально не было 1.

Большинство моих идей вращались вокруг использования which, чтобы узнать индекс первого 1 по группе, но я столкнулся с некоторыми проблемами.

Если у кого-то есть какие-либо решения на основе data.table(), это было бы здорово.

Я ценю любую помощь!

Вот dput() моих базовых данных, если это будет полезно:

library(data.table)

DT = setDT(structure(list(year = c(1992, 1992, 1992, 1992, 1992, 1992, 1992, 
1992, 1992, 1992, 1992, 1992, 1995, 1995, 1995, 1995, 1995, 1995, 
1995, 1995, 1995, 1995, 1995, 1995, 1998, 1998, 1998, 1998, 1998, 
1998, 1998, 1998, 1998, 1998, 1998, 1998), month = c(1, 2, 3, 
4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 
11, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12), flag = c(0, 0, 
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), group = c(8L, 8L, 8L, 
8L, 8L, 8L, 8L, 8L, 8L, 8L, 8L, 8L, 10L, 10L, 10L, 10L, 10L, 
10L, 11L, 11L, 11L, 11L, 11L, 11L, 13L, 13L, 13L, 13L, 13L, 13L, 
13L, 13L, 13L, 13L, 13L, 13L)), row.names = c(NA, -36L), 
class = c("data.table", "data.frame")))
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
773
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Вы можете использовать dplyr и cumsum:

library(dplyr)
df %>%
  group_by(group) %>%
  mutate(flag = ifelse(cumsum(flag) > 1, 1, 0))

Другой способ может заключаться в использовании lag:

df %>%
  group_by(group) %>%
  mutate(flag = ifelse(flag != 1 & row_number() > 1, lag(flag, 1), flag)) 

Или в data.table как:

df[, flag := ifelse(cumsum(flag) > 1, 1, 0), by=group]

Ваше data.table решение мне не подходит. Он просто присваивает каждому значению 0. Любые предложения?

Gin_Salmon 31.03.2019 08:38
Ответ принят как подходящий

Мы возвращаем 1 для строк из первого вхождения, где flag = 1 и в группе есть хотя бы один flag = 1

library(data.table)
dt[,flag := +(seq_len(.N)>= which.max(flag == 1) & any(flag == 1)),by = group]

dt

#    year month flag group
# 1: 1992     6    1     8
# 2: 1992     7    1     8
# 3: 1992     8    1     8
# 4: 1992     9    1     8
# 5: 1992    10    1     8
# 6: 1992    11    1     8
# 7: 1992    12    1     8
# 8: 1995     6    0    10
# 9: 1995     7    0    11
#10: 1995     8    0    11
#11: 1995     9    1    11
#12: 1995    10    1    11
#13: 1995    11    1    11
#14: 1995    12    1    11
#15: 1998     6    0    13
#16: 1998     7    0    13
#17: 1998     8    0    13
#18: 1998     9    0    13
#19: 1998    10    0    13
#20: 1998    11    0    13
#21: 1998    12    0    13
#    year month flag group

Что в dplyr было бы

library(dplyr)
dt %>%
   group_by(group) %>%
   mutate(flag = +(row_number() >= which.max(flag == 1) & any(flag == 1)))

а в базе R использование ave будет

dt$flag <- with(dt, +(ave(flag == 1, group, FUN = function(x) 
                     seq_along(x) >= which.max(x) & any(x))))

данные

dt <- structure(list(year = c(1992, 1992, 1992, 1992, 1992, 1992, 1992, 
1992, 1992, 1992, 1992, 1992, 1995, 1995, 1995, 1995, 1995, 1995, 
1995, 1995, 1995, 1995, 1995, 1995, 1998, 1998, 1998, 1998, 1998, 
1998, 1998, 1998, 1998, 1998, 1998, 1998), month = c(1, 2, 3, 
4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 
11, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12), flag = c(0, 0, 
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), group = c(8L, 8L, 8L, 
8L, 8L, 8L, 8L, 8L, 8L, 8L, 8L, 8L, 10L, 10L, 10L, 10L, 10L, 
10L, 11L, 11L, 11L, 11L, 11L, 11L, 13L, 13L, 13L, 13L, 13L, 13L, 
13L, 13L, 13L, 13L, 13L, 13L)), row.names = c(NA, -36L), class = 
c("data.table","data.frame"))

При использовании вашего ответа data.table() я получаю следующую ошибку: Error in [.data.table(stack_dat, , :=(flag, as.integer(seq_len(.N) >= : Type of RHS ('integer') must match LHS ('double'). To check and coerce would impact performance too much for the fastest cases. Either change the type of the target column, or coerce the RHS of := yourself (e.g. by using 1L instead of 1) Кроме того, похоже, что вы сгруппировали по столбцу year, а не по столбцу group. Или я неправильно понял ваш код?

Gin_Salmon 31.03.2019 13:32

@Gin_Salmon да, я сначала сгруппировал по year. Я изменил группу на group сейчас, хотя вывод не меняется, и я не получаю ошибку в примерах данных, которые я использовал. Похоже, есть несоответствие типов, попробуйте использовать as.numeric вместо +, попробуйте dt[,flag := as.numeric(seq_len(.N)>= which.max(flag == 1) & any(flag == 1)),by=group]

Ronak Shah 31.03.2019 13:48

Спасибо за это. У меня есть пара вопросов по коду: зачем нужен any() и что за ошибка, о которой я изначально говорил, и в чем разница между + и as.numeric?

Gin_Salmon 01.04.2019 00:44

@Gin_Salmon which.max вычисляет первое максимальное значение в переданном векторе. flag == 1 возвращает вектор значений TRUE/FALSE в зависимости от того, равно ли flag 1 или нет, когда мы делаем which.max(flag == 1), он возвращает индекс первого максимального значения. Таким образом, в случае, если есть какое-либо значение TRUE, он вернет индекс первого TRUE или, если все значения равны FALSE, тогда максимум будет FALSE, и он вернет 1. Таким образом, в этом случае он даст 1 всему group, чтобы избегайте использования any и проверьте, есть ли flag со значением 1 в group.

Ronak Shah 01.04.2019 02:13

Что касается + и as.numeric, + преобразует значения TRUE/FALSE в целые числа, тогда как в вашем столбце были числовые значения, поэтому было несоответствие типов, и, следовательно, вместо + мы используем as.numeric, чтобы тип соответствовал исходным столбцам.

Ronak Shah 01.04.2019 02:15

Используйте na.locf() из пакета zoo

Шаг 1: Отфильтруйте группы, содержащие хотя бы одну «1», и замените в них «0» на NA.

Шаг 2: Используйте na.locf(), чтобы перетащить самое последнее значение, не относящееся к NA, на все, что ниже.

library(zoo)
library(data.table)

temp[group %in% temp[,max(flag),.(group)][V1==1]$group & flag == 0,flag:= NA][,flag:=na.locf(flag,na.rm = FALSE)]

Входная таблица (темп)

structure(list(year = c(1992, 1992, 1992, 1992, 1992, 1992, 1992, 
1995, 1995, 1995, 1995, 1995, 1995, 1995, 1998, 1998, 1998, 1998, 
1998, 1998, 1998), month = c(6, 7, 8, 9, 10, 11, 12, 6, 7, 8, 
9, 10, 11, 12, 6, 7, 8, 9, 10, 11, 12), flag = c(1, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), group = c(8L, 
8L, 8L, 8L, 8L, 8L, 8L, 10L, 11L, 11L, 11L, 11L, 11L, 11L, 13L, 
13L, 13L, 13L, 13L, 13L, 13L)), row.names = c(NA, -21L), class = c("data.table", 
"data.frame"))

Выходная таблица

structure(list(year = c(1992, 1992, 1992, 1992, 1992, 1992, 1992, 
1995, 1995, 1995, 1995, 1995, 1995, 1995, 1998, 1998, 1998, 1998, 
1998, 1998, 1998), month = c(6, 7, 8, 9, 10, 11, 12, 6, 7, 8, 
9, 10, 11, 12, 6, 7, 8, 9, 10, 11, 12), flag = c(1, 1, 1, 1, 
1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0), group = c(8L, 
8L, 8L, 8L, 8L, 8L, 8L, 10L, 11L, 11L, 11L, 11L, 11L, 11L, 13L, 
13L, 13L, 13L, 13L, 13L, 13L)), row.names = c(NA, -21L), class = c("data.table", 
"data.frame"))

Вы можете выполнить неэквивалентное соединение с первым месяцем для каждой группы:

DT[unique(DT[flag==1], by = "group"), on=.(group, month >= month), flag := 1]

Это результат с выводом из OP:

    year month flag group
 1: 1992     1    0     8
 2: 1992     2    0     8
 3: 1992     3    0     8
 4: 1992     4    0     8
 5: 1992     5    0     8
 6: 1992     6    1     8
 7: 1992     7    1     8
 8: 1992     8    1     8
 9: 1992     9    1     8
10: 1992    10    1     8
11: 1992    11    1     8
12: 1992    12    1     8
13: 1995     1    0    10
14: 1995     2    0    10
15: 1995     3    0    10
16: 1995     4    0    10
17: 1995     5    0    10
18: 1995     6    0    10
19: 1995     7    0    11
20: 1995     8    0    11
21: 1995     9    1    11
22: 1995    10    1    11
23: 1995    11    1    11
24: 1995    12    1    11
25: 1998     1    0    13
26: 1998     2    0    13
27: 1998     3    0    13
28: 1998     4    0    13
29: 1998     5    0    13
30: 1998     6    0    13
31: 1998     7    0    13
32: 1998     8    0    13
33: 1998     9    0    13
34: 1998    10    0    13
35: 1998    11    0    13
36: 1998    12    0    13
    year month flag group

Другие вопросы по теме