W R jak szybko zapętlać wiersze ramki danych?

Załóżmy, że masz ramkę danych z wieloma wierszami i wieloma kolumnami.

Kolumny mają nazwy. Chcesz uzyskać dostęp do wierszy według numeru i kolumn według nazwy.

Na przykład jednym (prawdopodobnie wolnym) sposobem pętli nad wierszami jest

for (i in 1:nrow(df)) {
  print(df[i, "column1"])
  # do more things with the data frame...
}

Innym sposobem jest tworzenie "list" dla oddzielnych kolumn (jak column1_list = df[["column1"]) i dostęp do list w jednej pętli. Takie podejście może być szybkie, ale również niewygodne, jeśli chcesz uzyskać dostęp do wielu kolumn.

Czy istnieje szybki sposób na zapętlenie wiersze ramki danych? Czy inna struktura danych jest lepsza do szybkiego zapętlania?

Author: smci, 2010-07-26

3 answers

Myślę, że muszę to pełna odpowiedź, ponieważ uważam komentarze trudniejsze do śledzenia i już straciłem jeden komentarz na ten temat... Istnieje przykład nullglob , który pokazuje różnice między funkcjami for I stosuje funkcje rodziny znacznie lepiej niż inne przykłady. Kiedy ktoś sprawia, że funkcja jest bardzo powolna, wtedy cała prędkość jest zużywana i nie znajdziesz różnic między wariacjami pętli. Ale kiedy uczynisz funkcję trywialną wtedy zobaczysz jak bardzo pętla wpływa na rzeczy.

Chciałbym też dodać, że niektórzy członkowie rodziny apply mają ciekawe właściwości użytkowe. Najpierw pokażę replikacje względnych wyników nullglob na mojej maszynie.

n <- 1e6
system.time(for(i in 1:n) sinI[i] <- sin(i))
  user  system elapsed 
 5.721   0.028   5.712 

lapply runs much faster for the same result
system.time(sinI <- lapply(1:n,sin))
   user  system elapsed 
  1.353   0.012   1.361 
Stwierdził też, że sapphy jest znacznie wolniejsza. Oto kilka innych, które nie były testowane.

Plain old stosuje się do macierzowej wersji danych...

mat <- matrix(1:n,ncol =1),1,sin)
system.time(sinI <- apply(mat,1,sin))
   user  system elapsed 
  8.478   0.116   8.531 

Więc samo polecenie apply() jest znacznie wolniejsze niż dla pętli. (bo pętla nie jest znacznie spowolniona, Jeśli używam sin (mat [i,1]).

Kolejny, który nie wydaje się być testowany w innych postach jest tapply.

system.time(sinI <- tapply(1:n, 1:n, sin))
   user  system elapsed 
 12.908   0.266  13.589 

Oczywiście, nigdy nie można użyć tapply w ten sposób, a jego użyteczność jest daleko poza takim problemem prędkości w większości przypadków.

 14
Author: John,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:03

Najszybszym sposobem jest nie pętla (tj. operacje wektorowe). Jednym z niewielu przypadków, w których musisz zapętlić, jest sytuacja, w której istnieją zależności (tzn. jedna iteracja zależy od drugiej). W przeciwnym razie spróbuj wykonać jak najwięcej wektoryzowanych obliczeń poza pętlą.

Jeśli robisz potrzebujesz pętli, to Korzystanie z pętli {[0] } jest zasadniczo tak szybkie, jak cokolwiek innego (lapply może być trochę szybsze, ale inne Funkcje apply wydają się być mniej więcej takie same for).

 12
Author: Shane,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:11

Wykorzystanie faktu, że dane.ramki są zasadniczo listami wektorów kolumnowych, można użyć do.wywołanie do zastosowania funkcji o arytmetyce liczby kolumn nad każdą kolumną danych.ramka (podobna do "zamka" nad listą w innych językach).

do.call(paste, data.frame(x=c(1,2), z=c("a","b"), z=c(5,6)))
 0
Author: Mark B.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-30 17:50:49