cómo agregar valores acumulativamente en un vector en R

Tengo un conjunto de datos que se parece a esto

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 

Aquí, job2 denota una variable ficticia que indica si una persona fue Manager durante ese año o no. Quiero hacer dos cosas con este conjunto de datos: primero, solo quiero preservar la fila cuando la persona se convierte en Boss por primera vez. En segundo lugar, me gustaría ver los años acumulados que una persona trabajó como Manager y almacenar esta información en la variable cumu_job2 . Por lo tanto, me gustaría tener:

 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 

Cambié mis ejemplos e incluí la posición Trabajador porque esto refleja más lo que quiero hacer con el conjunto de datos original. Las respuestas en este hilo solo funcionan cuando solo hay administradores y jefe en el conjunto de datos, por lo que cualquier sugerencia para hacer que esto funcione sería genial. ¡Estaré muy agradecido!

Aquí está la solución sucinta dplyr para el mismo problema.

NOTA: asegúrese de que stringsAsFactors = FALSE mientras lee los datos.

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

Salida:

  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 

Explicación

  1. Tome el conjunto de datos
  2. Agrupe por nombre y trabajo
  3. Filtra cada grupo según la condición
  4. Agregue la columna cumu_job2 .

Contribuido por Matthew Dowle:

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

Explicación

  1. Tome el conjunto de datos
  2. Ejecute un filtro y agregue una columna dentro de cada S ubset de D ata ( .SD )
  3. Agrupado por nombre y trabajo

Versiones anteriores:

Aquí tiene dos combinaciones diferentes de aplicación dividida. Uno para obtener los trabajos acumulados y el otro para obtener la primera fila del estado del jefe. Aquí hay una implementación en data.table donde básicamente hacemos cada análisis por separado (bueno, más o menos), y luego recolectamos todo en un solo lugar con rbind . Lo principal a tener en cuenta es la pieza by=id , que básicamente significa que las otras expresiones se evalúan para cada agrupación de id en los datos, que fue lo que notó correctamente que faltaba en su bash.

 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 

Tenga en cuenta que esto supone que la tabla está ordenada por año dentro de cada id , pero si no es lo suficientemente fácil de corregir.


Alternativamente, también podría lograr lo mismo con:

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

La idea es obtener básicamente los números de fila donde la condición coincide (con .I - variable interna) y luego subconjunto dt en esos números de fila (la parte $v1 ), luego solo realizar la sum acumulativa.

Aquí hay una solución base usando within y ave . Suponemos que la entrada es DF y que los datos están ordenados como en la pregunta.

 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) 

REVISIÓN: Ahora usa within .

Creo que esto hace lo que quieres, aunque los datos se deben ordenar como lo has presentado.

 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’s es mucho mejor:

Los datos

 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) 

#El código:

 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 
Intereting Posts