Estoy tratando de crear una nueva columna, presumiblemente usando mutate, que identificará si la fila cumple con algunos criterios. Básicamente, para cada usuario, quiero identificar la fila final (por tiempo) para un determinado DataCode. Solo se aplican algunos DataCodes (1000 y 2000 en el ejemplo a continuación), y otros deben devolver NA (3000 aquí). He estado tratando de resolver esto en mi cabeza, y todo lo que puedo pensar es un elemento mutante realmente largo con varias declaraciones If. ¿Hay alguna forma más elegante?

La columna de IsFinal a continuación muestra cuál sería el producto.

 User Time  DataCode Data     IsFinal
 101  10    1000     50       0
 101  20    2000     300      1
 101  30    3000     150      NA
 101  40    1000     250      1
 101  50    3000     300      NA
 102  10    2000     50       0
 102  20    1000     150      0
 102  30    1000     150      0
 102  40    2000     350      1
 102  50    3000     150      NA
 102  60    1000     50       1
4
ADBryant 23 jun. 2017 a las 20:08

3 respuestas

La mejor respuesta

Esto desea lo que necesita utilizando merge y dplyr paquete :

library(dplyr)
new.tab <- query.tab %>%
            group_by(User, DataCode) %>%
            arrange(Time) %>%
            filter(DataCode != 3000) %>% 
            mutate(IsFinal = ifelse(row_number()==n(),1,0))

fin.tab  <- merge(new.tab, query.tab, all.x = FALSE, all.y = TRUE)

Si desea hacer todo dentro de dplyr , esta es su respuesta:

fin.tab <-
 query.tab %>%
  group_by(User, DataCode) %>%
   arrange(User,Time) %>%
    mutate(IsFinal = ifelse(DataCode == 3000 , NA, 
                             ifelse(row_number()==n(),1,0)))

Ambas soluciones darán:

> fin.tab

#    User Time DataCode Data IsFinal 
# 1   101   10     1000   50       0 
# 2   101   20     2000  300       1 
# 3   101   30     3000  150      NA 
# 4   101   40     1000  250       1 
# 5   101   50     3000  300      NA 
# 6   102   10     2000   50       0 
# 7   102   20     1000  150       0 
# 8   102   30     1000  150       0 
# 9   102   40     2000  350       1 
# 10  102   50     3000  150      NA 
# 11  102   60     1000   50       1

Datos :

query.tab <- structure(list(User = c(101L, 101L, 101L, 101L, 101L, 102L, 102L, 
102L, 102L, 102L, 102L), Time = c(10L, 20L, 30L, 40L, 50L, 10L, 
20L, 30L, 40L, 50L, 60L), DataCode = c(1000L, 2000L, 3000L, 1000L, 
3000L, 2000L, 1000L, 1000L, 2000L, 3000L, 1000L), Data = c(50L, 
300L, 150L, 250L, 300L, 50L, 150L, 150L, 350L, 150L, 50L)), .Names = c("User", 
"Time", "DataCode", "Data"), row.names = c(NA, -11L), class = "data.frame")

Nota: Lea el historial de ediciones. Puede darle una idea de cómo manejar problemas similares.

3
M-- 23 jun. 2017 a las 19:15

¿Es factible hacer una serie de códigos aprobados? Eso haría que la declaración if sea mucho más simple.

# Can you obtain list of viable codes?
codes <- c("2000", "1000")
# Can you put them in order?
goodcodes <- codes[order(codes)]
# last item in ordered goodcodes should be the end code
endcode <- goodcodes[length(goodcodes)]

testcodes <- c("0500", "1000", "2000", "3000")
n <- length(testcodes)
IsFinal <- rep(0, n)

for (i in 1:n) {
  if (testcodes[i] %in% goodcodes) {
    if (testcodes[i] == endcode) (IsFinal[i] = 1)
  } else (IsFinal[i] = NA)
}

> IsFinal
[1] NA  0  1 NA
> 
2
mmyoung77 23 jun. 2017 a las 17:33

En la base R, podemos usar ave junto con duplicated y su argumento fromLast para obtener los valores binarios. Luego reemplace los valores deseados con NA. Usando los datos en la respuesta de @ masoud.

# get binary values for final DataCode by user
query.tab$IsFinal <- with(query.tab,
                         ave(DataCode, User, FUN=function(x) !duplicated(x, fromLast=TRUE)))
# Fill in NA values
is.na(query.tab$IsFinal)  <- query.tab$DataCode %in% c(3000)

Esto vuelve

query.tab
   User Time DataCode Data IsFinal
1   101   10     1000   50       0
2   101   20     2000  300       1
3   101   30     3000  150      NA
4   101   40     1000  250       1
5   101   50     3000  300      NA
6   102   10     2000   50       0
7   102   20     1000  150       0
8   102   30     1000  150       0
9   102   40     2000  350       1
10  102   50     3000  150      NA
11  102   60     1000   50       1

Tenga en cuenta que esto supone que los datos están ordenados por tiempo de usuario. Esto se puede lograr con una llamada a order antes de usar el código anterior.

query.tab <- query.tab[order(query.tab$User, query.tab$Time),]
2
lmo 23 jun. 2017 a las 18:45