So verwenden Sie eine unbekannte Anzahl von Schlüsselspalten in einer Datentabelle

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

  •  12-12-2019
  •  | 
  •  

Frage

Ich möchte das Gleiche tun wie erklärt Hier, d.h.Hinzufügen fehlender Zeilen zu einer Datentabelle.Die einzige zusätzliche Schwierigkeit, mit der ich konfrontiert bin, besteht darin, dass ich die Anzahl der Schlüsselspalten, d. h.die Zeilen, die für den Self-Join verwendet werden, um flexibel zu sein.

Hier ist ein kleines Beispiel, das im Grunde wiederholt, was im oben genannten Link gemacht wird:

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]

Was mich stört, ist die letzte Zeile.Ich will idCols um flexibel zu sein (z. B. wenn ich es innerhalb einer Funktion verwende), möchte ich also nicht tippen unique(df$fundID), unique(df$cfType) manuell.Allerdings finde ich einfach keine Lösung dafür.Alle meine Versuche, die Teilmenge automatisch aufzuteilen df in Vektoren, je nach Bedarf von CJ, schlägt mit der Fehlermeldung fehl Fehler in setkeyv(x, cols, verbose = verbose):Spalte „V1“ ist vom Typ „Liste“, der (derzeit) nicht als Schlüsselspaltentyp zulässig ist.

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

Ich habe auch versucht, den Ausdruck selbst zu erstellen:

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))"

Aber dann weiß ich nicht, wie ich es verwenden soll str.Das alles schlägt fehl:

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

Kennt jemand einen guten Workaround?

War es hilfreich?

Lösung

Ich habe das Paket data.table noch nie verwendet, also verzeihen Sie mir, wenn ich hier das Ziel verfehle, aber ich glaube, ich habe es verstanden.Hier ist viel los.Lesen Sie zunächst weiter do.call, was es Ihnen ermöglicht, jede Funktion auf eine Art nicht-traditionelle Weise auszuwerten, bei der Argumente durch eine bereitgestellte Liste angegeben werden (wobei jedes Element in der Liste positionell mit den Funktionsargumenten übereinstimmt, sofern es nicht explizit benannt wird).Beachten Sie auch, dass ich angeben musste min(df$variable) statt nur min(variable).Lesen Hadleys Seite zum Scoping um hier einen Eindruck von der Problematik zu bekommen.

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]

Andere Tipps

Michaels Antwort ist großartig. do.call ist in der Tat erforderlich, um anzurufen CJ Auf diese Weise flexibel, afaik.

Um den Ansatz zur Ausdruckserstellung zu klären und mit Ihrem Code zu beginnen, aber den zu entfernen df$ Teile (nicht erforderlich und in der verlinkten Antwort nicht erledigt, da i wird im Rahmen von bewertet 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))" 

dann ist es :

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

oder alternativ die gesamte Abfrage dynamisch erstellen und auswerten:

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

Und wenn dies häufig geschieht, kann es sich lohnen, eine Hilfsfunktion zu erstellen:

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

um es zu reduzieren auf:

E("DT[CJ(",str,"),nomatch=NA")
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top