2012-05-19 16 views
15

Jak mogę użyć apply lub powiązanej funkcji do utworzenia nowej ramki danych, która zawiera wyniki średnich rzędów dla każdej pary kolumn w bardzo dużej ramce danych?stosuje funkcję nad grupami kolumn

Mam instrument, który przekazuje n powtórzenie pomiarów na dużej liczbie próbek, gdzie każdy pojedynczy pomiar jest wektorem (wszystkie pomiary są wektorami tej samej długości). Chciałbym obliczyć średnią (i inne statystyki) we wszystkich powtórzeniach pomiarów dla każdej próbki. Oznacza to, że muszę pogrupować kolejno następujące po sobie kolumny i wykonać obliczenia wierszowe.

przypadku prostego przykładu, z trzech powtórzonych pomiarów na dwóch próbkach, w jaki sposób można zakończyć się z ramką danych, który ma dwie kolumny (po jednym na próbkę), z których jedna jest średnią dla każdego rzędu powtórzenia w dat$a, dat$b i dat$c i jeden, który jest średnią z każdego rzędu dla dat$d, dat$e i dat$f.

Oto przykładowe dane

dat <- data.frame(a = rnorm(16), b = rnorm(16), c = rnorm(16), d = rnorm(16), e = rnorm(16), f = rnorm(16)) 

      a   b   c   d   e   f 
1 -0.9089594 -0.8144765 0.872691548 0.4051094 -0.09705234 -1.5100709 
2 0.7993102 0.3243804 0.394560355 0.6646588 0.91033497 2.2504104 
3 0.2963102 -0.2911078 -0.243723116 1.0661698 -0.89747522 -0.8455833 
4 -0.4311512 -0.5997466 -0.545381175 0.3495578 0.38359390 0.4999425 
5 -0.4955802 1.8949285 -0.266580411 1.2773987 -0.79373386 -1.8664651 
6 1.0957793 -0.3326867 -1.116623982 -0.8584253 0.83704172 1.8368212 
7 -0.2529444 0.5792413 -0.001950741 0.2661068 1.17515099 0.4875377 
8 1.2560402 0.1354533 1.440160168 -2.1295397 2.05025701 1.0377283 
9 0.8123061 0.4453768 1.598246016 0.7146553 -1.09476532 0.0600665 
10 0.1084029 -0.4934862 -0.584671816 -0.8096653 1.54466019 -1.8117459 
11 -0.8152812 0.9494620 0.100909570 1.5944528 1.56724269 0.6839954 
12 0.3130357 2.6245864 1.750448404 -0.7494403 1.06055267 1.0358267 
13 1.1976817 -1.2110708 0.719397607 -0.2690107 0.83364274 -0.6895936 
14 -2.1860098 -0.8488031 -0.302743475 -0.7348443 0.34302096 -0.8024803 
15 0.2361756 0.6773727 1.279737692 0.8742478 -0.03064782 -0.4874172 
16 -1.5634527 -0.8276335 0.753090683 2.0394865 0.79006103 0.5704210 

jestem po coś takiego

  X1   X2 
1 -0.28358147 -0.40067128 
2 0.50608365 1.27513471 
3 -0.07950691 -0.22562957 
4 -0.52542633 0.41103139 
5 0.37758930 -0.46093340 
6 -0.11784382 0.60514586 
7 0.10811540 0.64293184 
8 0.94388455 0.31948189 
9 0.95197629 -0.10668118 
10 -0.32325169 -0.35891702 
11 0.07836345 1.28189698 
12 1.56269017 0.44897971 
13 0.23533617 -0.04165384 
14 -1.11251880 -0.39810121 
15 0.73109533 0.11872758 
16 -0.54599850 1.13332286 

który zrobiłem z tym, ale to oczywiście nie jest dobre dla mojego znacznie większej ramki danych ...

data.frame(cbind(
apply(cbind(dat$a, dat$b, dat$c), 1, mean), 
apply(cbind(dat$d, dat$e, dat$f), 1, mean) 
)) 

Próbowałem apply i pętli i nie całkiem mogę go połączyć. Moje rzeczywiste dane mają setki kolumn.

+0

Czy zawsze co trzy kolumny? Czy karmisz wektor wektorów nazw lub wektor wektora indeksów? Jeśli odpowiedź użytkownika user1317221_G nie jest tym, czego szukasz, być może musisz podać więcej informacji. –

+1

Dla potomności powyższe pytanie wydaje się być transpozycją tego nowszego pytania na temat stosowania funkcji do grup wierszy (i ma różne podejścia): http://stackoverflow.com/q/10837258/1036500 – Ben

Odpowiedz

14

To może być bardziej uogólnić do sytuacji, w które przechodzą listę indeksów. Jeśli prędkość jest problemem (duża ramka danych) bym zdecydować lapply z do.call zamiast sapply:

x <- list(1:3, 4:6) 
do.call(cbind, lapply(x, function(i) rowMeans(dat[, i]))) 

Works, jeśli tylko mają col nazwy TOO:

x <- list(c('a','b','c'), c('d', 'e', 'f')) 
do.call(cbind, lapply(x, function(i) rowMeans(dat[, i]))) 

EDIT

Po prostu pomyślałem, że może chcesz zautomatyzować to, aby zrobić co trzy kolumny.Wiem, że istnieje lepszy sposób, ale tutaj jest na danych zestawu 100 kolumny:

dat <- data.frame(matrix(rnorm(16*100), ncol=100)) 

n <- 1:ncol(dat) 
ind <- matrix(c(n, rep(NA, 3 - ncol(dat)%%3)), byrow=TRUE, ncol=3) 
ind <- data.frame(t(na.omit(ind))) 
do.call(cbind, lapply(ind, function(i) rowMeans(dat[, i]))) 

EDIT 2 Nadal nie zadowolony z indeksowania. Myślę, że istnieje lepszy/szybszy sposób przekazywania indeksów. oto drugi choć niespełniających metody:

n <- 1:ncol(dat) 
ind <- data.frame(matrix(c(n, rep(NA, 3 - ncol(dat)%%3)), byrow=F, nrow=3)) 
nonna <- sapply(ind, function(x) all(!is.na(x))) 
ind <- ind[, nonna] 

do.call(cbind, lapply(ind, function(i)rowMeans(dat[, i]))) 
+1

To pomija ostatnią kolumnę, ponieważ nie ma trzech kolumn do połączenia. –

+0

Tak, twoja edycja robi dokładnie to, czego szukam, dziękuję bardzo. Przepraszam, że moje pytanie było źle sformułowane, zrodziło się z długich godzin bezowocnych prób ... – Ben

+1

Zamierzam poprosić o lepszy sposób na stworzenie indeksów i link tutaj. –

7

oznacza wiersze wektory a, b, c

rowMeans(dat[1:3]) 

oznacza wiersze wektory d, e, f

rowMeans(dat[4:6]) 

w jednym połączenia można uzyskać

results<-cbind(rowMeans(dat[1:3]),rowMeans(dat[4:6])) 

jeśli znasz tylko nazwy kolumn, a nie kolejność, wtedy możesz użyć:

rowMeans(cbind(dat["a"],dat["b"],dat["c"])) 
rowMeans(cbind(dat["d"],dat["e"],dat["f"])) 

#I dont know how much damage this does to speed but should still be quick 
+0

A co z dla ramki danych z setkami kolumn? Jak możesz to uogólnić? – Ben

+0

@joran, masz rację, zbyt pochopnie przygotowywałem moje pytanie, przepraszam za niejednoznaczność. Redakcja Tylera Rinkera ma kod, który robi to, co robię. – Ben

5

Rozwiązanie rowMeans będzie szybciej, ale dla kompletności oto jak można to zrobić z apply:

t(apply(dat,1,function(x){ c(mean(x[1:3]),mean(x[4:6])) })) 
+1

Co oznaczają elementy wiersza dla każdego kolejnego zestawu trzech kolumn w ramce danych z kilkoma setkami kolumn? – Ben

+2

@Ben Zredukuj go do problemu, który już rozwiązałeś: (1) transponuj (2) użyj ** plyr ** lub ** data.table **, (3) transponuj z powrotem. (Zakładając, że wszystko jest numeryczne.) – joran

+0

Dam ci szansę i zobaczę, czy uda mi się wymyślić coś bardziej skutecznego niż rozwiązanie Tylera powyżej (mało prawdopodobne, ale warto spróbować!) – Ben

2

Zainspirowany użytkownika @ Jøran sugestią wymyśliłem ten (faktycznie nieco inny od tego, co zasugerował, chociaż propozycja transpozycji było szczególnie przydatne):

Zrób ramka danych przykładowych danych z p przełęcze symulować realistyczne zestaw danych (po @ odpowiedź TylerRinker w powyższym iw przeciwieństwie do mojej biednej przykład w pytaniu)

p <- 99 # how many columns? 
dat <- data.frame(matrix(rnorm(4*p), ncol = p)) 

Zmiana nazwy kolumny w tej ramce danych do tworzenia grup z n kolejnych kolumn, więc jeśli interesują mnie grupy z trzema kolumnami, otrzymam nazwy kolumn, takie jak 1,1,1,2,2,2,3,3,3 itd. lub jeśli chcę grupy czterech kolumn byłby 1,1,1,1,2,2,2,2,3,3,3,3 itd. Idę teraz z trzema (myślę, że to rodzaj indeksowania dla ludzi takich jak ja którzy nie wiedzą zbyt wiele o indeksacji)

n <- 3 # how many consecutive columns in the groups of interest? 
names(dat) <- rep(seq(1:(ncol(dat)/n)), each = n, len = (ncol(dat))) 

teraz użyj zastosowania i Tapply uzyskać wiersz oznacza dla każdej z grup

dat.avs <- data.frame(t(apply(dat, 1, tapply, names(dat), mean))) 

Główne wady to, że nazwy kolumn w oryginalnych danych są zastąpione (chociaż można to było pokonać, umieszczając numery grupowania w ne) w wierszu zamiast nazw kolumn) i że nazwy kolumn są zwracane przez funkcję apply-tapply w niepomyślnej kolejności.

nawiązaniu do sugestii @ Jøran, oto jest data.table rozwiązanie:

p <- 99 # how many columns? 
dat <- data.frame(matrix(rnorm(4*p), ncol = p)) 
dat.t <- data.frame(t(dat)) 

n <- 3 # how many consecutive columns in the groups of interest? 
dat.t$groups <- as.character(rep(seq(1:(ncol(dat)/n)), each = n, len = (ncol(dat)))) 

library(data.table) 
DT <- data.table(dat.t) 
setkey(DT, groups) 
dat.av <- DT[, lapply(.SD,mean), by=groups] 

Dzięki wszystkim za szybką i pacjentów wysiłki!

+2

Wystarczy, że dodasz wskaźnik, który powinien mieć 'lapply (.SD, mean)' idiom znacznie szybciej w wersji 1.8.1 dzięki: i) odkryciu w [tym pytaniu] (http://stackoverflow.com/questions/10584993/r-loop-over-columns-in-data-table) i ii) automatycznym .Internal() z 'mean()' (wiki point 3 nie jest już potrzebny). Również ".Scols" jest często użyteczne, ale nie jest tu potrzebne. –

+0

@MatthewDowle dzięki za twoją notatkę! Dobrze wiedzieć o '.Scols', nie tym, który znałem, i świetnie słyszeć, że' data.table' po prostu robi się coraz szybszy! – Ben

6

Podobne pytanie zadano tutaj przez @david: averaging every 16 columns in r (obecnie zamknięte), na które odpowiedziałem, dostosowując odpowiedź @ TylerRinker powyżej, kierując się sugestią @joran i @Ben. Ponieważ wynikowa funkcja może być pomocna dla OP lub przyszłych czytelników, kopiuję tę funkcję tutaj, wraz z przykładem dla danych OP.

# Function to apply 'fun' to object 'x' over every 'by' columns 
# Alternatively, 'by' may be a vector of groups 
byapply <- function(x, by, fun, ...) 
{ 
    # Create index list 
    if (length(by) == 1) 
    { 
     nc <- ncol(x) 
     split.index <- rep(1:ceiling(nc/by), each = by, length.out = nc) 
    } else # 'by' is a vector of groups 
    { 
     nc <- length(by) 
     split.index <- by 
    } 
    index.list <- split(seq(from = 1, to = nc), split.index) 

    # Pass index list to fun using sapply() and return object 
    sapply(index.list, function(i) 
      { 
       do.call(fun, list(x[, i], ...)) 
      }) 
} 

Następnie, aby znaleźć średnią z powtórzeń:

byapply(dat, 3, rowMeans) 

A może odchylenie standardowe powtórzeń:

byapply(dat, 3, apply, 1, sd) 

Aktualizacja

by puszka również należy podać jako wektor gr oups:

byapply(dat, c(1,1,1,2,2,2), rowMeans) 
+0

+1 dzięki, jest to pomocne również. – Ben

0

Jest pięknie proste rozwiązanie, jeśli jesteś zainteresowany w stosowaniu funkcji do każdej unikalnej kombinacji kolumn, w tym, co znane jako kombinatoryki.

combinations <- combn(colnames(df),2,function(x) rowMeans(df[x])) 

Aby obliczyć statystyki dla każdej unikalnej kombinacji trzech kolumn, itp, po prostu zmień 2 do 3. Operacja jest wektorowy, a więc szybciej niż pętli, takich jak funkcje apply rodziny wykorzystywanych powyżej. Jeśli kolejność kolumn ma znaczenie, to potrzebujesz algorytmu permutacji przeznaczonego do odtworzenia uporządkowanych zestawów: combinat::permn

+0

co masz na myśli mówiąc "jeśli porządek ma znaczenie" i jaka jest funkcja combinat :: permn? Czy możesz edytować kod? – user3495945

+0

Kombinacje to nie to samo co permutacje: https://www.youtube.com/watch?v=s2W6Bce_T30 Jeśli kolejność wejść ma znaczenie, to jest to permutacja, której szukasz. W tym przypadku "porządek" odnosi się do kolejności kolumn. –

Powiązane problemy