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.
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
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
ipmax
, 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
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.
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.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