Subset eine data.frame durch Liste und gilt Funktion für jeden Teil, durch Reihen
Frage
Dies kann als ein typisches plyr
Problem sein, aber ich habe etwas anderes im Sinne.
Hier ist die Funktion, dass ich optimieren möchten (überspringen Sie die for
Schleife).
# dummy data
set.seed(1985)
lst <- list(a=1:10, b=11:15, c=16:20)
m <- matrix(round(runif(200, 1, 7)), 10)
m <- as.data.frame(m)
dfsub <- function(dt, lst, fun) {
# check whether dt is `data.frame`
stopifnot (is.data.frame(dt))
# check if vectors in lst are "whole" / integer
# vector elements should be column indexes
is.wholenumber <- function(x, tol = .Machine$double.eps^0.5) abs(x - round(x)) < tol
# fall if any non-integers in list
idx <- rapply(lst, is.wholenumber)
stopifnot(idx)
# check for list length
stopifnot(ncol(dt) == length(idx))
# subset the data
subs <- list()
for (i in 1:length(lst)) {
# apply function on each part, by row
subs[[i]] <- apply(dt[ , lst[[i]]], 1, fun)
}
# preserve names
names(subs) <- names(lst)
# convert to data.frame
subs <- as.data.frame(subs)
# guess what =)
return(subs)
}
Und nun eine kurze Demonstration ... tatsächlich, ich bin zu erklären, was ich in erster Linie zu tun beabsichtigt. Ich wollte einen data.frame
von Vektoren in list
Objekt gesammelt Teilmenge. Da dies ein Teil des Codes aus einer Funktion, die Datenmanipulation in der psychologischen Forschung begleitet, können Sie m
als Ergebnisse von Persönlichkeit Fragebogen betrachten (10 Probanden, 20 VARs). Vektoren in der Liste halten Spalte Indizes, die Fragebogen subscales definieren (zum Beispiel Persönlichkeitsmerkmale). Jede Subskala wird durch mehrere Elemente (Spalten in data.frame
) definiert. Wenn wir, dass die Punktzahl auf jeder Subskala voraus ist nichts anderes als sum
(oder eine andere Funktion) von Zeilenwerten (Ergebnisse auf diesem Teil des Fragebogens für jedes Fach), könnten Sie laufen:
> dfsub(m, lst, sum)
a b c
1 46 20 24
2 41 24 21
3 41 13 12
4 37 14 18
5 57 18 25
6 27 18 18
7 28 17 20
8 31 18 23
9 38 14 15
10 41 14 22
habe ich einen Blick auf diese Funktion und ich muss zugeben, dass dieser kleine Schleife den Code überhaupt nicht zu verderben ... ABER, wenn es eine einfachere / effiziente Art und Weise, dies zu tun, bitte, lass es mich wissen!
Lösung
würde ich einen anderen Ansatz und alles als Datenrahmen halten, so dass Sie merge und ddply verwenden können. Ich denke, Sie finden diese Vorgehensweise ein wenig allgemeiner ist, und es ist einfacher, zu überprüfen, dass jeder Schritt korrekt durchgeführt wird.
# Convert everything to long data frames
m$id <- 1:nrow(m)
library(reshape)
obs <- melt(m, id = "id")
obs$variable <- as.numeric(gsub("V", "", obs$variable))
varinfo <- melt(lst)
names(varinfo) <- c("variable", "scale")
# Merge and summarise
obs <- merge(obs, varinfo, by = "variable")
ddply(obs, c("id", "scale"), summarise,
mean = mean(value),
sum = sum(value))
Andere Tipps
nach dem plyr Paket laden, ersetzen
subs <- list()
for (i in 1:length(lst)) {
# apply function on each part, by row
subs[[i]] <- apply(dt[ , lst[[i]]], 1, fun)
}
mit
subs <- llply(lst,function(x) apply(dt[,x],1,fun))
@Hadley, ich habe Ihre Antwort überprüft, da es recht einfach ist und leicht für die Buchhaltung (neben der Tatsache, es ist mehr Allzweck-Lösung). Aber hier ist mein nicht so lange Skript, das die Sache tut und erfordert nur base
Paket (das ist trivial, da ich plyr
und reshape
nur nach R Installation installieren). Nun, hier ist die Quelle:
dfsub <- function(dt, lst, fun) {
# check whether dt is `data.frame`
stopifnot (is.data.frame(dt))
# convert data.frame factors to numeric
dt <- as.data.frame(lapply(dt, as.numeric))
# check if vectors in lst are "whole" / integer
# vector elements should be column indexes
is.wholenumber <- function(x, tol = .Machine$double.eps^0.5) abs(x - round(x)) < tol
# fall if any non-integers in list
idx <- rapply(lst, is.wholenumber)
stopifnot(idx)
# check for list length
stopifnot(ncol(dt) == length(idx))
# subset the data
subs <- list()
for (i in 1:length(lst)) {
# apply function on each part, by row
subs[[i]] <- apply(dt[ , lst[[i]]], 1, fun)
}
names(subs) <- names(lst)
# convert to data.frame
subs <- as.data.frame(subs)
# guess what =)
return(subs)
}
Für Ihr spezielles Beispiel eine einzeilige Lösung ist sapply(lst,function(x) rowSums(m[,x]))
(obwohl einige weiteren Zeilen hinzufügen möchte für gültige Eingabe zu überprüfen und in den Spaltennamen setzen).
Haben Sie andere haben, allgemeinere Anwendungen im Sinn? Oder ist dies möglicherweise ein Fall von YAGNI ?