Как кумулятивно добавлять значения в один вектор в R

У меня есть набор данных, который выглядит так

id  name    year    job    job2
1   Jane    1980    Worker  0
1   Jane    1981    Manager 1
1   Jane    1982    Manager 1
1   Jane    1983    Manager 1
1   Jane    1984    Manager 1
1   Jane    1985    Manager 1
1   Jane    1986    Boss    0
1   Jane    1987    Boss    0
2   Bob     1985    Worker  0
2   Bob     1986    Worker  0
2   Bob     1987    Manager 1
2   Bob     1988    Boss    0
2   Bob     1989    Boss    0
2   Bob     1990    Boss    0
2   Bob     1991    Boss    0
2   Bob     1992    Boss    0

Вот, job2 обозначает фиктивную переменную, указывающую, был ли человек Manager в течение этого года или нет. Я хочу сделать две вещи с этим набором данных: во-первых, я хочу сохранить строку только тогда, когда человек стал Boss в первый раз. Во-вторых, я хотел бы видеть совокупные годы, когда человек работал Manager и сохранить эту информацию в переменной cumu_job2, Таким образом, я хотел бы иметь:

id  name    year    job    job2 cumu_job2
1   Jane    1980    Worker  0   0
1   Jane    1981    Manager 1   1
1   Jane    1982    Manager 1   2
1   Jane    1983    Manager 1   3
1   Jane    1984    Manager 1   4
1   Jane    1985    Manager 1   5
1   Jane    1986    Boss    0   0
2   Bob     1985    Worker  0   0
2   Bob     1986    Worker  0   0
2   Bob     1987    Manager 1   1
2   Bob     1988    Boss    0   0

Я изменил свои примеры и включил положение "Рабочий", потому что это больше отражает то, что я хочу сделать с исходным набором данных. Ответы в этой теме работают только тогда, когда в наборе данных есть только менеджеры и боссы, поэтому любые предложения по выполнению этой работы были бы хорошими. Я буду очень благодарен!

5 ответов

Вот краткое dplyr Решение для той же проблемы.

ПРИМЕЧАНИЕ: убедитесь, что stringsAsFactors = FALSE при чтении данных.

library(dplyr)
dat %>%
  group_by(name, job) %>%
  filter(job != "Boss" | year == min(year)) %>%
  mutate(cumu_job2 = cumsum(job2))

Выход:

   id name year     job job2 cumu_job2
1   1 Jane 1980  Worker    0         0
2   1 Jane 1981 Manager    1         1
3   1 Jane 1982 Manager    1         2
4   1 Jane 1983 Manager    1         3
5   1 Jane 1984 Manager    1         4
6   1 Jane 1985 Manager    1         5
7   1 Jane 1986    Boss    0         0
8   2  Bob 1985  Worker    0         0
9   2  Bob 1986  Worker    0         0
10  2  Bob 1987 Manager    1         1
11  2  Bob 1988    Boss    0         0

объяснение

  1. Возьмите набор данных
  2. Группа по имени и работе
  3. Фильтровать каждую группу по условию
  4. добавлять cumu_job2 колонка.

Мэттью Доул:

dt[, .SD[job != "Boss" | year == min(year)][, cumjob := cumsum(job2)],
     by = list(name, job)]

объяснение

  1. Возьмите набор данных
  2. Запустите фильтр и добавьте столбец в каждый набор данных D ata (.SD)
  3. Сгруппированы по имени и работе

Старые версии:

Здесь у вас есть два разных комбайна. Один, чтобы получить совокупные задания, а другой, чтобы получить первый ряд статуса босса. Вот реализация в data.table где мы в основном делаем каждый анализ отдельно (ну, вроде), а затем собираем все в одном месте с rbind, Главное отметить это by=id кусок, который в основном означает, что другие выражения оцениваются для каждого id группировка в данных, что вы правильно отметили, что не было в вашей попытке.

library(data.table)
dt <- as.data.table(df)
dt[, cumujob:=0L]  # add column, set to zero
dt[job2==1, cumujob:=cumsum(job2), by=id]  # cumsum for manager time by person 
rbind(
  dt[job2==1],                     # this is just the manager portion of the data
  dt[job2==0, head(.SD, 1), by=id] # get first bossdom row
)[order(id, year)]                 # order by id, year
#       id name year     job job2 cumujob
#   1:  1 Jane 1980 Manager    1       1
#   2:  1 Jane 1981 Manager    1       2
#   3:  1 Jane 1982 Manager    1       3
#   4:  1 Jane 1983 Manager    1       4
#   5:  1 Jane 1984 Manager    1       5
#   6:  1 Jane 1985 Manager    1       6
#   7:  1 Jane 1986    Boss    0       0
#   8:  2  Bob 1985 Manager    1       1
#   9:  2  Bob 1986 Manager    1       2
#  10:  2  Bob 1987 Manager    1       3
#  11:  2  Bob 1988    Boss    0       0

Обратите внимание, что предполагается, что таблица отсортирована по годам в каждом id, но если это не так просто исправить


В качестве альтернативы вы также можете достичь того же с:

ans <- dt[, .I[job != "Boss" | year == min(year)], by=list(name, job)]
ans <- dt[ans$V1]
ans[, cumujob := cumsum(job2), by=list(name,job)] 

Идея состоит в том, чтобы в основном получить номера строк, где выполняется условие (с .I - внутренняя переменная), а затем подмножество dt на этих номерах строк ($v1 часть), а затем просто выполнить накопительную сумму.

Вот базовое решение с использованием within а также ave, Мы предполагаем, что вход DF и что данные отсортированы как в вопросе.

DF2 <- within(DF, {
    seq = ave(id, id, job, FUN = seq_along)
    job2 = (job == "Manager") + 0
    cumu_job2 = ave(job2, id, job, FUN = cumsum)
})
subset(DF2, job != 'Boss' | seq == 1, select = - seq)

ПЕРЕСМОТР: теперь использует within,

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

my.df <- read.table(text = '
id  name    year    job    job2
1   Jane    1980    Worker  0
1   Jane    1981    Manager 1
1   Jane    1982    Manager 1
1   Jane    1983    Manager 1
1   Jane    1984    Manager 1
1   Jane    1985    Manager 1
1   Jane    1986    Boss    0
1   Jane    1987    Boss    0
2   Bob     1985    Worker  0
2   Bob     1986    Worker  0
2   Bob     1987    Manager 1
2   Bob     1988    Boss    0
2   Bob     1989    Boss    0
2   Bob     1990    Boss    0
2   Bob     1991    Boss    0
2   Bob     1992    Boss    0
', header = TRUE, stringsAsFactors = FALSE)

my.seq <- data.frame(rle(my.df$job)$lengths)

my.df$cumu_job2 <- as.vector(unlist(apply(my.seq, 1, function(x) seq(1,x))))

my.df2 <- my.df[!(my.df$job=='Boss' & my.df$cumu_job2 != 1),]
my.df2$cumu_job2[my.df2$job != 'Manager'] <- 0

   id name year     job job2 cumu_job2
1   1 Jane 1980  Worker    0         0
2   1 Jane 1981 Manager    1         1
3   1 Jane 1982 Manager    1         2
4   1 Jane 1983 Manager    1         3
5   1 Jane 1984 Manager    1         4
6   1 Jane 1985 Manager    1         5
7   1 Jane 1986    Boss    0         0
9   2  Bob 1985  Worker    0         0
10  2  Bob 1986  Worker    0         0
11  2  Bob 1987 Manager    1         1
12  2  Bob 1988    Boss    0         0

@BrodieG лучше:

Данные

dat <- read.table(text="id  name    year    job    job2
1   Jane    1980    Manager 1
1   Jane    1981    Manager 1
1   Jane    1982    Manager 1
1   Jane    1983    Manager 1
1   Jane    1984    Manager 1
1   Jane    1985    Manager 1
1   Jane    1986    Boss    0
1   Jane    1987    Boss    0
2   Bob     1985    Manager 1
2   Bob     1986    Manager 1
2   Bob     1987    Manager 1
2   Bob     1988    Boss    0
2   Bob     1989    Boss    0
2   Bob     1990    Boss    0
2   Bob     1991    Boss    0
2   Bob     1992    Boss    0", header=TRUE)

#Код:

inds1 <- rle(dat$job2)
inds2 <- cumsum(inds1[[1]])[inds1[[2]] == 1] + 1

ends <- cumsum(inds1[[1]])
starts <- c(1, head(ends + 1, -1))
inds3 <- mapply(":", starts, ends)
dat$id <- rep(1:length(inds3), sapply(inds3, length))
dat <- do.call(rbind, lapply(split(dat[, 1:5], dat$id ), function(x) {
    if(x$job2[1] == 0){ 
        x$cumu_job2 <- rep(0, nrow(x))
    } else { 
        x$cumu_job2 <- 1:nrow(x)
    }
    x
}))


keeps <- dat$job2 > 0
keeps[inds2] <- TRUE
dat2 <- data.frame(dat[keeps, ], row.names = NULL)
dat2

##    id name year     job job2 cumu_job2
## 1   1 Jane 1980 Manager    1         1
## 2   1 Jane 1981 Manager    1         2
## 3   1 Jane 1982 Manager    1         3
## 4   1 Jane 1983 Manager    1         4
## 5   1 Jane 1984 Manager    1         5
## 6   1 Jane 1985 Manager    1         6
## 7   2 Jane 1986    Boss    0         0
## 8   3  Bob 1985 Manager    1         1
## 9   3  Bob 1986 Manager    1         2
## 10  3  Bob 1987 Manager    1         3
## 11  4  Bob 1988    Boss    0         0
Другие вопросы по тегам