Mam ramkę danych z wieloma kolumnami. Dla każdego wiersza w ramce danych chcę wywołać funkcję w wierszu, a dane wejściowe funkcji używają wielu kolumn z tego wiersza. Na przykład, powiedzmy, że mam te dane i ten testFunc, który akceptuje dwa argumenty:
> df <- data.frame(x=c(1,2), y=c(3,4), z=c(5,6))
> df
x y z
1 1 3 5
2 2 4 6
> testFunc <- function(a, b) a + b
Powiedzmy, że chcę zastosować ten testFunc do kolumn x i z. Więc dla wiersza 1 chcę 1 + 5, a dla wiersza 2 chcę 2 + 6. Czy istnieje sposób na zrobienie tego bez pisania pętli for, być może z rodziną funkcji Apply?
Próbowałem tego:
> df[,c('x','z')]
x z
1 1 5
2 2 6
> lapply(df[,c('x','z')], testFunc)
Error in a + b : 'b' is missing
Ale masz błąd, jakieś pomysły?
EDYCJA: faktyczna funkcja, którą chcę wywołać, nie jest prostą sumą, ale jest to power.t.test. Użyłem a + b tylko dla celów. Ostatecznym celem jest zrobienie czegoś takiego (napisanego w pseudokodzie):
df = data.frame(
delta=c(delta_values),
power=c(power_values),
sig.level=c(sig.level_values)
)
lapply(df, power.t.test(delta_from_each_row_of_df,
power_from_each_row_of_df,
sig.level_from_each_row_of_df
))
gdzie wynikiem jest wektor wyjść dla power.t.test dla każdego wiersza df.
dplyr
dowiedzieć się więcej.Odpowiedzi:
Możesz zastosować
apply
do podzbioru oryginalnych danych.lub jeśli twoja funkcja jest po prostu sumą, użyj wersji wektorowej:
Jeśli chcesz użyć
testFunc
EDYCJA Aby uzyskać dostęp do kolumn według nazwy, a nie indeksu, możesz zrobić coś takiego:
źródło
apply
na big data.frames, spowoduje to skopiowanie całego obiektu (w celu konwersji na macierz). Spowoduje to również problemy, jeśli masz różne obiekty klas w data.frame.A
data.frame
jestlist
, więc ...W przypadku funkcji wektoryzowanych
do.call
jest zwykle dobrym rozwiązaniem. Ale w grę wchodzą nazwy argumentów. Tutaj twójtestFunc
jest wywoływany z argumentami x i y zamiast a i b....
Pozwala nieistotne args być przekazywane bez powodowania błędu:Dla funkcji non-wektorowy ,
mapply
będzie działać, ale trzeba dopasować kolejność args lub jawnie nazwać je:Czasami
apply
zadziała - tak jak wtedy, gdy wszystkie argumenty są tego samego typu, więcdata.frame
przekształcenie macierzy w macierz nie powoduje problemów przy zmianie typów danych. Twój przykład był tego rodzaju.Jeśli twoja funkcja ma zostać wywołana w ramach innej funkcji, do której są przekazywane wszystkie argumenty, istnieje znacznie lepsza metoda niż te. Przestudiuj pierwsze linie ciała,
lm()
jeśli chcesz iść tą trasą.źródło
Vectorize
jako opakowaniemapply
do wektoryzacji funkcjiPosługiwać się
mapply
źródło
Nowa odpowiedź z
dplyr
pakietemJeśli funkcja, którą chcesz zastosować, jest wektoryzowana, możesz użyć
mutate
funkcji zdplyr
pakietu:Stara odpowiedź z
plyr
pakietemMoim skromnym zdaniem, narzędzie najlepiej nadaje się do tego zadania jest
mdply
odplyr
pakietu.Przykład:
Niestety, jak zauważył Bertjan Broeksema , to podejście zawodzi, jeśli nie używasz wszystkich kolumn ramki danych w
mdply
wywołaniu. Na przykład,źródło
dplyr::mutate_each
. Na przykład:iris %>% mutate_each(funs(half = . / 2),-Species)
.Inni słusznie wskazali, że
mapply
jest on stworzony do tego celu, ale (ze względu na kompletność) koncepcyjnie prostszą metodą jest po prostu użyciefor
pętli.źródło
Wiele funkcji jest już wektoryzowanych, więc nie ma potrzeby wykonywania żadnych iteracji (ani
for
pętli, ani*pply
funkcji). TwójtestFunc
jest jednym z takich przykładów. Możesz po prostu zadzwonić:Ogólnie zalecałbym najpierw wypróbowanie takich metod wektoryzacji i sprawdzenie, czy dadzą one zamierzone rezultaty.
Alternatywnie, jeśli chcesz przekazać wiele argumentów do funkcji, która nie jest wektoryzowana,
mapply
może być tym, czego szukasz:źródło
Oto alternatywne podejście. Jest bardziej intuicyjny.
Jednym z kluczowych aspektów, które, moim zdaniem, nie wzięły pod uwagę niektórych odpowiedzi, na które zwracam uwagę dla potomności, jest zastosowanie () pozwala na łatwe obliczenia wierszy, ale tylko dla danych macierzowych (wszystkich danych liczbowych)
operacje na kolumnach są nadal możliwe dla ramek danych:
Aby operować na wierszach, najpierw dokonujemy transpozycji.
Wadą jest to, że wierzę, że R zrobi kopię twojej tabeli danych. Co może być problemem z pamięcią. (Jest to naprawdę smutne, ponieważ tdf jest programistycznie prosty, aby po prostu być iteratorem do oryginalnego df, oszczędzając w ten sposób pamięć, ale R nie pozwala na odwoływanie się do wskaźnika lub iteratora).
Powiązane pytanie dotyczy również sposobu działania na każdej pojedynczej komórce w ramce danych.
źródło
Przyszedłem tutaj, szukając nazwy funkcji tidyverse - o której wiedziałem, że istnieje. Dodanie tego dla (moich) przyszłych odniesień i dla
tidyverse
entuzjastów:purrrlyr:invoke_rows
(purrr:invoke_rows
w starszych wersjach).W połączeniu ze standardowymi metodami statystyk, jak w pierwotnym pytaniu, pakiet miotły prawdopodobnie by pomógł.
źródło
Odpowiedź @ user20877984 jest doskonała. Ponieważ podsumowali to znacznie lepiej niż moja poprzednia odpowiedź, oto moja (prawdopodobnie wciąż tandetna) próba zastosowania tego pojęcia:
Używanie
do.call
w sposób podstawowy:Praca na pełnym zestawie danych:
lapply
power.t.test
funkcja każdego z rzędów wymienionych wartości:źródło
2
, dlaczego po prostu nie nakładasz1
?data.table
ma też naprawdę intuicyjny sposób::=
Operator może być wywołana w nawiasach w celu dodania nowej kolumny przy użyciu funkcjiZa pomocą tej metody można również łatwo zaakceptować stałe jako argumenty:
źródło
Jeśli kolumny data.frame są różnych typów,
apply()
występuje problem. Subtelność dotycząca iteracji wierszy polega na tym, jakapply(a.data.frame, 1, ...)
niejawna konwersja typów na typy znakowe, gdy kolumny są różnych typów; na przykład. współczynnik i kolumna liczbowa. Oto przykład użycia współczynnika w jednej kolumnie do zmodyfikowania kolumny liczbowej:Odejmowanie kończy się niepowodzeniem, ponieważ kolumny są konwertowane na typy znaków.
Jedną z poprawek jest konwersja wsteczna drugiej kolumny na liczbę:
Ale konwersji można uniknąć, oddzielając kolumny i używając
mapply()
:mapply()
jest potrzebny, ponieważ[[ ]]
nie akceptuje argumentu wektorowego. Więc iteracja kolumny może być wykonana przed odejmowaniem, przekazując wektor do[]
, za pomocą nieco bardziej brzydkiego kodu:źródło
Naprawdę fajną funkcją do tego jest
adply
fromplyr
, zwłaszcza jeśli chcesz dołączyć wynik do oryginalnej ramki danych. Ta funkcja i jej kuzynddply
zaoszczędziły mi wielu bólów głowy i linii kodu!Alternatywnie możesz wywołać żądaną funkcję.
źródło