Próbuję napisać funkcję akceptującą data.frame ( x
) iz column
niej. Funkcja wykonuje pewne obliczenia na x, a później zwraca kolejną ramkę data.frame. Utknąłem na najlepszej metodzie przekazywania nazwy kolumny do funkcji.
Dwa minimalne przykłady fun1
ifun2
poniżej uzyskania pożądanego rezultatu, jest w stanie wykonywać operacje na x$column
używając max()
jako przykład. Jednak obaj opierają się na pozornie (przynajmniej dla mnie) nieeleganckiej
- zadzwonić
substitute()
i ewentualnieeval()
- konieczność przekazania nazwy kolumny jako wektora znakowego.
fun1 <- function(x, column){
do.call("max", list(substitute(x[a], list(a = column))))
}
fun2 <- function(x, column){
max(eval((substitute(x[a], list(a = column)))))
}
df <- data.frame(B = rnorm(10))
fun1(df, "B")
fun2(df, "B")
Chciałbym móc wywołać tę funkcję jako fun(df, B)
. Inne opcje, które rozważałem, ale nie próbowałem:
- Przechodzić
column
jako liczbę całkowitą numeru kolumny. Myślę, że to pozwoliłoby uniknąćsubstitute()
. Idealnie, funkcja mogłaby zaakceptować oba. with(x, get(column))
, ale myślę, że nawet jeśli to zadziała, nadal będzie to wymagałosubstitute
- Skorzystaj z
formula()
imatch.call()
, z którymi nie mam dużego doświadczenia.
Pytanie dodatkowe : Czy jest do.call()
preferowane eval()
?
B
zakłada, że sam B jest obiektem.[[
, że jedyne rozwiązanie działało dla mnie.Ta odpowiedź obejmie wiele takich samych elementów, jak istniejące odpowiedzi, ale ten problem (przekazywanie nazw kolumn do funkcji) pojawia się na tyle często, że chciałem, aby była odpowiedź, która obejmowałaby sprawy nieco bardziej kompleksowo.
Załóżmy, że mamy bardzo prostą ramkę danych:
i chcielibyśmy napisać funkcję, która tworzy nową kolumnę będącą
z
sumą kolumnx
iy
.Bardzo częstą przeszkodą jest tutaj to, że naturalna (ale błędna) próba często wygląda tak:
Problem polega na tym,
df$col1
że nie ocenia wyrażeniacol1
. Po prostu szuka kolumny wdf
dosłownie nazwanymcol1
. To zachowanie opisano w?Extract
sekcji „Obiekty rekurencyjne (podobne do list)”.Najprostszym i najczęściej zalecanym rozwiązaniem jest po prostu przełączenie się z opcji
$
na[[
i przekazanie argumentów funkcji jako ciągów:Jest to często uważane za „najlepszą praktykę”, ponieważ jest to metoda najtrudniejsza do zepsucia. Przekazywanie nazw kolumn jako ciągów jest tak jednoznaczne, jak to tylko możliwe.
Poniższe dwie opcje są bardziej zaawansowane. Wiele popularnych pakietów korzysta z tego rodzaju technik, ale ich dobre użycie wymaga więcej uwagi i umiejętności, ponieważ mogą one wprowadzić subtelne zawiłości i nieprzewidziane punkty awarii. Ta sekcja książki Hadley's Advanced R jest doskonałym źródłem informacji na temat niektórych z tych zagadnień.
Jeśli naprawdę chcesz uchronić użytkownika przed wpisywaniem wszystkich tych cudzysłowów, jedną z opcji może być przekonwertowanie pustych, niecytowanych nazw kolumn na ciągi przy użyciu
deparse(substitute())
:Jest to, szczerze mówiąc, trochę głupie, ponieważ tak naprawdę robimy to samo, co w programie
new_column1
, tylko z masą dodatkowej pracy nad konwersją nagich nazw na ciągi.Wreszcie, jeśli chcemy uzyskać naprawdę wymyślny wygląd, możemy zdecydować, że zamiast podawać nazwy dwóch kolumn do dodania, chcielibyśmy być bardziej elastyczni i pozwolić na inne kombinacje dwóch zmiennych. W takim przypadku prawdopodobnie uciekniemy się do
eval()
wyrażenia obejmującego dwie kolumny:Dla zabawy nadal używam
deparse(substitute())
nazwy nowej kolumny. Tutaj będą działać wszystkie poniższe elementy:Krótka odpowiedź brzmi więc w zasadzie: przekazuj nazwy kolumn data.frame jako ciągi i użyj
[[
do zaznaczania pojedynczych kolumn. Uruchomić tylko zagłębiając sięeval
,substitute
itp jeśli naprawdę wiesz co robisz.źródło
Osobiście uważam, że przekazywanie kolumny jako łańcucha jest dość brzydkie. Lubię robić coś takiego:
co da:
Zwróć uwagę, że specyfikacja data.frame jest opcjonalna. możesz nawet pracować z funkcjami swoich kolumn:
źródło
Innym sposobem jest użycie
tidy evaluation
podejścia. Przekazywanie kolumn ramki danych jako łańcuchów lub nagich nazw kolumn jest całkiem proste. Zobacz więcejtidyeval
tutaj .Użyj nazw kolumn jako ciągów
Użyj nazw kolumn
Utworzono 01.03.2019 przez pakiet reprex (v0.2.1.9000)
źródło
Jako dodatkowa myśl, jeśli konieczne jest przekazanie nazwy kolumny bez cudzysłowu do funkcji niestandardowej, być może
match.call()
może być również przydatne w tym przypadku, jako alternatywa dladeparse(substitute())
:Jeśli w nazwie kolumny jest literówka, bezpieczniej byłoby zatrzymać się z błędem:
Utworzono 11.01.2019 przez pakiet reprex (v0.2.1)
Nie sądzę, żebym użył tego podejścia, ponieważ istnieje dodatkowe wpisywanie i złożoność niż zwykłe przekazywanie cytowanej nazwy kolumny, jak wskazano w powyższych odpowiedziach, ale cóż, jest to podejście.
źródło