Stosowanie wielu funkcji do każdego wiersza ramki danych

Za każdym razem, gdy myślę, że rozumiem pracę z wektorami, to, co wydaje się prostym problemem, wywraca moją głowę na lewą stronę. Dużo czytania i próbowania różnych przykładów nie pomogło przy tej okazji. Proszę, Nakarm mnie łyżką...

Chcę zastosować dwie niestandardowe funkcje do każdego wiersza ramki danych i dodać wyniki jako dwie nowe kolumny. Oto mój przykładowy kod:

# Required packages:
library(plyr)

FindMFE <- function(x) {
    MFE <- max(x, na.rm = TRUE) 
    MFE <- ifelse(is.infinite(MFE ) | (MFE  < 0), 0, MFE)
    return(MFE)
}

FindMAE <- function(x) {
    MAE <- min(x, na.rm = TRUE) 
    MAE <- ifelse(is.infinite(MAE) | (MAE> 0), 0, MAE)
    return(MAE)
}

FindMAEandMFE <- function(x){
        # I know this next line is wrong...
    z <- apply(x, 1, FindMFE, FindMFE)
        return(z)
}

df1 <- data.frame(Bar1=c(1,2,3,-3,-2,-1),Bar2=c(3,1,3,-2,-3,-1))

df1 = transform(df1, 
    FindMAEandMFE(df1)  
)

#DF1 should end up with the following data...
#Bar1   Bar2    MFE MAE
#1      3       3   0
#2      1       2   0
#3      3       3   0
#-3     -2      0   -3
#-2     -3      0   -3
#-1     -1      0   -1

Byłoby wspaniale uzyskać odpowiedź za pomocą biblioteki plyr i bardziej bazowego podejścia. Obie będą pomoc w moim zrozumieniu. Oczywiście, proszę wskazać, gdzie robię źle, jeśli to oczywiste. ;-)

A teraz wróć do plików pomocy dla mnie!

Edit: chciałbym mieć rozwiązanie wielowymiarowe, ponieważ nazwy kolumn mogą się zmieniać i rozszerzać w czasie. Umożliwia również ponowne wykorzystanie kodu w przyszłości.

Author: Look Left, 2011-08-24

4 answers

Myślę, że myślisz zbyt skomplikowanie. Co jest nie tak z dwoma oddzielnymi wywołaniami apply()? Istnieje jednak znacznie lepszy sposób, aby zrobić to, co robisz tutaj, który nie wymaga zapętlania / zastosowania połączeń. Zajmę się nimi osobno, ale drugie rozwiązanie jest lepsze, ponieważ jest naprawdę wektoryzowane.

Dwie wersje wywołań aplikacji

Pierwsze dwa oddzielne wywołania apply przy użyciu wszystkich podstawowych funkcji R:

df1 <- data.frame(Bar1=c(1,2,3,-3,-2,-1),Bar2=c(3,1,3,-2,-3,-1))
df1 <- transform(df1, MFE = apply(df1, 1, FindMFE), MAE = apply(df1, 1, FindMAE))
df1

Co daje:

> df1
  Bar1 Bar2 MFE MAE
1    1    3   3   0
2    2    1   2   0
3    3    3   3   0
4   -3   -2   0  -3
5   -2   -3   0  -3
6   -1   -1   0  -1

Ok, zapętlamy rzędy df1 dwa razy może to trochę nieefektywne, ale nawet w przypadku dużych problemów spędziłeś już więcej czasu myśląc o zrobieniu tego sprytnie w jednym przejściu, niż zaoszczędzisz robiąc to w ten sposób.

Za pomocą funkcji wektorowych pmax() i pmin()

Więc lepszym sposobem na to jest zwrócenie uwagi na funkcje pmax() i pmin() i uświadomienie sobie, że mogą robić to, co robiły każde wywołanie {14]}. Na przykład:

> (tmp <- with(df1, pmax(0, Bar1, Bar2, na.rm = TRUE)))
[1] 3 2 3 0 0 0

Byłoby MFE z twojego pytania. Jest to bardzo proste w obsłudze z, jeśli masz dwie kolumny i są Bar1 i Bar2 lub pierwsze 2 kolumny df1, zawsze. Ale to nie jest zbyt ogólne; co jeśli masz wiele kolumn, które chcesz obliczyć itp? pmax(df1[, 1:2], na.rm = TRUE) nie zrobimy tego, co chcemy:

> pmax(df1[, 1:2], na.rm = TRUE)
  Bar1 Bar2
1    1    3
2    2    1
3    3    3
4   -3   -2
5   -2   -3
6   -1   -1

Sztuką uzyskania ogólnego rozwiązania za pomocą pmax() i pmin() jest użycie do.call() do zorganizowania wywołań tych dwóch funkcji dla nas. Aktualizacja funkcji do wykorzystania tego pomysłu mamy:

FindMFE2 <- function(x) {
   MFE <- do.call(pmax, c(as.list(x), 0, na.rm = TRUE))
   MFE[is.infinite(MFE)] <- 0
   MFE
}

FindMAE2 <- function(x) {
   MAE <- do.call(pmin, c(as.list(x), 0, na.rm = TRUE))
   MAE[is.infinite(MAE)] <- 0
   MAE
}

Które dają:

> transform(df1, MFE = FindMFE2(df1), MAE = FindMAE2(df1))
  Bar1 Bar2 MFE MAE
1    1    3   3   0
2    2    1   2   0
3    3    3   3   0
4   -3   -2   0  -3
5   -2   -3   0  -3
6   -1   -1   0  -1

I nie apply() w zasięgu wzroku. Jeśli chcesz to zrobić w jednym kroku, teraz jest to znacznie łatwiejsze do owinięcia:

FindMAEandMFE2 <- function(x){
    cbind(MFE = FindMFE2(x), MAE = FindMAE2(x))
}

Który może być użyty jako:

> cbind(df1, FindMAEandMFE2(df1))
  Bar1 Bar2 MFE MAE
1    1    3   3   0
2    2    1   2   0
3    3    3   3   0
4   -3   -2   0  -3
5   -2   -3   0  -3
6   -1   -1   0  -1
 19
Author: Gavin Simpson,
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
2011-08-24 11:14:46

Pokazuję trzy alternatywne jedynki:

  • Korzystanie z funkcji each plyr
  • Korzystanie z plyr each Funkcja z bazą R
  • Korzystanie z funkcji pmin i pmax, które są wektorowe

Rozwiązanie 1: plyr i każdy

Pakiet plyr definiuje funkcję each, która robi to, co chcesz. Od ?each: Połącz wiele funkcji w jedną funkcję. oznacza to, że możesz rozwiązać swój problem za pomocą jednowierszowy:

library(plyr)
adply(df1, 1, each(MAE=function(x)max(x, 0), MFE=function(x)min(x, 0)))

  Bar1 Bar2 MAE MFE
1    1    3   3   0
2    2    1   2   0
3    3    3   3   0
4   -3   -2   0  -3
5   -2   -3   0  -3
6   -1   -1   0  -1

Rozwiązanie 2: każdy i baza R

Można oczywiście używać each Z FUNKCJAMI bazowymi. Oto jak możesz go użyć z apply - pamiętaj, że musisz przetransponować wyniki przed dodaniem do oryginalnych danych.rama.

library(plyr)
data.frame(df1, 
  t(apply(df1, 1, each(MAE=function(x)max(x, 0), MFE=function(x)min(x, 0)))))

  Bar1 Bar2 MAE MFE
1    1    3   3   0
2    2    1   2   0
3    3    3   3   0
4   -3   -2   0  -3
5   -2   -3   0  -3
6   -1   -1   0  -1

Rozwiązanie 3: Korzystanie z funkcji wektorowych

Używając wektoryzowanych funkcji pmin i pmax, możesz użyć tej jednowierszowej:

transform(df1, MFE=pmax(0, Bar1, Bar2), MAE=pmin(0, Bar1, Bar2))

  Bar1 Bar2 MFE MAE
1    1    3   3   0
2    2    1   2   0
3    3    3   3   0
4   -3   -2   0  -3
5   -2   -3   0  -3
6   -1   -1   0  -1
 19
Author: Andrie,
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
2011-08-24 11:55:33

Jest tu wiele dobrych odpowiedzi. Zacząłem to, gdy Gavin Simpson edytował, więc omówimy podobny Grunt. To, co robią równoległe min i max (pmin i pmax), jest prawie dokładnie tym, do czego piszesz swoje funkcje. Może to być trochę nieprzezroczyste, co robi 0 w pmax (0, Bar1, Bar2), ale zasadniczo 0 jest poddawane recyklingowi, więc to tak, jakby robić

pmax(c(0,0,0,0,0,0), Bar1, Bar2)

To zabierze każdy przedmiot z trzech rzeczy, które przeszły i znajdzie ich maksimum. Tak, max będzie 0, jeśli to było negatywne i osiąga wiele z tego, co twoje oświadczenie ifelse zrobił. Możesz przepisać, aby uzyskać wektory i połączyć rzeczy z funkcjami podobnymi do tego, co robiłeś, a to może sprawić, że będzie to nieco bardziej przejrzyste. W tym przypadku po prostu przekazalibyśmy ramkę danych do nowej równoległej i szybkiej funkcji findMFE, która będzie działać z dowolną numeryczną ramką danych i wyjdzie z wektora.

findMFE <- function(dataf){
    MFE <- do.call( pmax, c(dataf, 0, na.rm = TRUE))
}

MFE <- findMFE(df1)

To co robi ta funkcja to dodawanie dodatkowej kolumny 0s do przekazywanej ramki danych, a następnie wywołanie Pmax passing each osobna kolumna df1 jakby byĹ 'a listÄ ...(ramki danych sÄ ... listami, wiÄ ™ c jest to Ĺ' atwe).

Teraz zauważam, że w rzeczywistości chcesz poprawić wartości Inf w swoich danych, które nie są w twoim przykładzie... możemy dodać dodatkową linię do twojej funkcji...

findMFE <- function(dataf){
    MFE <- do.call( pmax, c(dataf, 0, na.rm = TRUE))
    ifelse(is.infinite(MFE), 0, MFE)
}

To jest właściwe użycie funkcji ifelse() na wektorze. Zrobiłem to w ten sposób jako przykład dla ciebie, ale Gavin Simpson używa MFE [jest.infinite(MFE)]

Porównywalnym findMAE jest...

findMAE <- function(dataf){
    MAE <- do.call( pmin, c(dataf, 0, na.rm = TRUE))
    ifelse(is.infinite(MAE), 0, MAE)
}

A funkcja zespolona jest prosta...

findMFEandMAE <- function(dataf){
    MFE <- findMFE(dataf)
    MAE <- findMAE(dataf)
    return(data.frame(MFE, MAE))
}

MFEandMAE

Kilka porad

Jeśli masz polecenie Skalar if nie używaj ifelse (), użyj if () else. Jest znacznie szybszy w sytuacjach skalarnych. Twoje funkcje są skalarne i próbujesz je wektoryzować. ifelse() jest już wektoryzowana i działa bardzo szybko, gdy jest używana w ten sposób ale znacznie wolniejsze niż if () else, gdy jest używane Skalar.

Również, jeśli masz zamiar umieścić rzeczy w pętli lub apply instrukcji umieścić jak najmniej tam, jak to możliwe. Na przykład, w Twoim przypadku ifelse () naprawdę musiało zostać wyjęte z pętli i następnie zastosowane do całego wyniku MFE.

 6
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-10-03 18:38:56

Jeśli naprawdę, naprawdę tego chcesz, możesz:

FindMAEandMFE <- function(x){
    t(apply(x, 1, function(currow){c(MAE=FindMAE(currow), MFE=FindMFE(currow))}))
}

(nie testowane - powinna zwracać tablicę z dwoma (nazwanymi, jak sądzę) kolumnami i tyle wierszy co dane.ramka miała). Teraz możesz zrobić:

df1<-cbind(df1, FindMAEandMFE(df1))
Bardzo ohydne. Proszę posłuchać rady Gavina.
 1
Author: Nick Sabbe,
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
2011-08-24 10:49:24