Sottoinsieme A data.frame elenco e la funzione applicare su ciascuna parte, per righe
Domanda
Questo può sembrare come un tipico problema plyr
, ma ho qualcosa di diverso in mente.
Ecco la funzione che voglio per ottimizzare (saltare il ciclo for
).
# 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)
}
E ora una breve dimostrazione ... in realtà, sto per spiegare quello che principalmente destinato a fare. Volevo sottoinsieme A data.frame
da vettori raccolti in oggetto list
. Dal momento che questa è una parte di codice da una funzione che accompagna la manipolazione dei dati nella ricerca psicologica, si può considerare come un m
risultati dal questionario di personalità (10 soggetti, 20 Vars). Vettori in lista sostengono indici di colonna che definiscono le sottoscale del questionario (per esempio i tratti della personalità). Ogni sotto-scala è definito da più elementi (colonne in data.frame
). Se presupponiamo che il punteggio su ogni sottoscala non è altro che sum
(o qualche altra funzione) dei valori di riga (risultati su quella parte del questionario per ogni soggetto), è possibile eseguire:
> 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
Ho preso uno sguardo a questa funzione e devo ammettere che questo piccolo ciclo non è rovinare il codice a tutti ... ma, se c'è un modo più semplice / efficace di fare questo, per favore, fammi sapere!
Soluzione
mi piacerebbe prendere un approccio diverso e mantenere tutto come frame di dati in modo da poter utilizzare unione e ddply. Penso che troverete questo approccio è un po 'più generale, ed è più facile da controllare che ogni fase è stata eseguita correttamente.
# 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))
Altri suggerimenti
dopo aver caricato il pacchetto plyr, sostituire
subs <- list()
for (i in 1:length(lst)) {
# apply function on each part, by row
subs[[i]] <- apply(dt[ , lst[[i]]], 1, fun)
}
con
subs <- llply(lst,function(x) apply(dt[,x],1,fun))
@Hadley, ho controllato la vostra risposta dal momento che è abbastanza semplice e facile per la contabilità (oltre al fatto che è più general-purpose-soluzione). Tuttavia, ecco la mia non-così-lungo script che fa la cosa e richiede solo pacchetto base
(che è banale in quanto installo plyr
e reshape
subito dopo l'installazione di R). Ora, ecco la fonte:
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)
}
Per la vostra esempio specifico, una soluzione one-line è sapply(lst,function(x) rowSums(m[,x]))
(anche se si potrebbe aggiungere un po 'più linee per verificare la presenza di input valido e mettere nei nomi delle colonne).
Avete altri, più generali, applicazioni in mente? O è questo forse un caso di YAGNI ?