Cómo utilizar un número desconocido de columnas clave en una tabla de datos

StackOverflow https://stackoverflow.com//questions/11677424

  •  12-12-2019
  •  | 
  •  

Pregunta

quiero hacer lo mismo como se explica aquí, es decir.agregar filas faltantes a una tabla de datos.La única dificultad adicional a la que me enfrento es que quiero el número de columnas clave, es deciraquellas filas que se utilizan para la autounión, para que sean flexibles.

A continuación os dejamos un pequeño ejemplo que básicamente repite lo que se hace en el enlace mencionado anteriormente:

df <- data.frame(fundID   = rep(letters[1:4], each=6),
                 cfType   = rep(c("D", "D", "T", "T", "R", "R"), times=4),
                 variable = rep(c(1,3), times=12),
                 value    = 1:24)
DT <- as.data.table(df)
idCols <- c("fundID", "cfType")
setkeyv(DT, c(idCols, "variable"))
DT[CJ(unique(df$fundID), unique(df$cfType), seq(from=min(variable), to=max(variable))), nomatch=NA]

Lo que me molesta es la última línea.Quiero idCols para ser flexible (por ejemplo, si lo uso dentro de una función), así que no quiero escribir unique(df$fundID), unique(df$cfType) a mano.Sin embargo, simplemente no encuentro ninguna solución para esto.Todos mis intentos de dividir automáticamente el subconjunto de df en vectores, según sea necesario CJ, falla con el mensaje de error Error en setkeyv(x, cols, detallado = detallado):La columna 'V1' es del tipo 'lista' que (actualmente) no está permitida como tipo de columna clave.

CJ(sapply(df[, idCols], unique))
CJ(unique(df[, idCols]))
CJ(as.vector(unique(df[, idCols])))
CJ(unique(DT[, idCols, with=FALSE]))

También intenté construir la expresión yo mismo:

str <- ""
for (i in idCols) {
  str <- paste0(str, "unique(df$", i, "), ")
}
str <- paste0(str, "seq(from=min(variable), to=max(variable))")
str
[1] "unique(df$fundID), unique(df$cfType), seq(from=min(variable), to=max(variable))"

Pero entonces no sé cómo usarlo. str.Todo esto falla:

CJ(eval(str))
CJ(substitute(str))
CJ(call(str))

¿Alguien conoce una buena solución?

¿Fue útil?

Solución

Nunca he usado el paquete data.table, así que perdónenme si me equivoco aquí, pero creo que lo tengo.Están sucediendo muchas cosas aquí.Comience leyendo sobre do.call, que le permite evaluar cualquier función de una manera no tradicional donde los argumentos se especifican mediante una lista proporcionada (donde cada elemento de la lista coincide posicionalmente con los argumentos de la función a menos que se nombre explícitamente).También observe que tuve que especificar min(df$variable) en lugar de solo min(variable).Leer La página de Hadley sobre el alcance para tener una idea del problema aquí.

CJargs <- lapply(df[, idCols], unique)
names(CJargs) <- NULL
CJargs[[length(CJargs) +1]] <- seq(from=min(df$variable), to=max(df$variable))
DT[do.call("CJ", CJargs),nomatch=NA]

Otros consejos

La respuesta de Michael es genial. do.call De hecho es necesario llamar CJ flexiblemente de esa manera, que yo sepa.

Para aclarar el enfoque de creación de expresiones y comenzar con su código, pero eliminando el df$ partes (no necesarias y no hechas en la respuesta vinculada, ya que i se evalúa dentro del alcance de DT) :

str <- "" 
for (i in idCols) { 
  str <- paste0(str, "unique(", i, "), ") 
} 
str <- paste0(str, "seq(from=min(variable), to=max(variable))") 
str 
[1] "unique(fundID), unique(cfType), seq(from=min(variable), to=max(variable))" 

Entonces es :

expr <- parse(text=paste0("CJ(",str,")"))
DT[eval(expr),nomatch=NA]

o, alternativamente, crear y evaluar toda la consulta dinámicamente:

eval(parse(text=paste0("DT[CJ(",str,"),nomatch=NA")))

Y si esto se hace con frecuencia, puede que valga la pena crear una función auxiliar:

E = function(...) eval(parse(text=paste0(...)))

para reducirlo a:

E("DT[CJ(",str,"),nomatch=NA")
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top