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:
dat <- data.frame(x = 1:4,
y = 5:8)
i chcielibyśmy napisać funkcję, która tworzy nową kolumnę będącą z
sumą kolumn x
i y
.
Bardzo częstą przeszkodą jest tutaj to, że naturalna (ale błędna) próba często wygląda tak:
foo <- function(df,col_name,col1,col2){
df$col_name <- df$col1 + df$col2
df
}
#Call foo() like this:
foo(dat,z,x,y)
Problem polega na tym, df$col1
że nie ocenia wyrażenia col1
. Po prostu szuka kolumny w df
dosłownie nazwanym col1
. 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:
new_column1 <- function(df,col_name,col1,col2){
#Create new column col_name as sum of col1 and col2
df[[col_name]] <- df[[col1]] + df[[col2]]
df
}
> new_column1(dat,"z","x","y")
x y z
1 1 5 6
2 2 6 8
3 3 7 10
4 4 8 12
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())
:
new_column2 <- function(df,col_name,col1,col2){
col_name <- deparse(substitute(col_name))
col1 <- deparse(substitute(col1))
col2 <- deparse(substitute(col2))
df[[col_name]] <- df[[col1]] + df[[col2]]
df
}
> new_column2(dat,z,x,y)
x y z
1 1 5 6
2 2 6 8
3 3 7 10
4 4 8 12
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:
new_column3 <- function(df,col_name,expr){
col_name <- deparse(substitute(col_name))
df[[col_name]] <- eval(substitute(expr),df,parent.frame())
df
}
Dla zabawy nadal używam deparse(substitute())
nazwy nowej kolumny. Tutaj będą działać wszystkie poniższe elementy:
> new_column3(dat,z,x+y)
x y z
1 1 5 6
2 2 6 8
3 3 7 10
4 4 8 12
> new_column3(dat,z,x-y)
x y z
1 1 5 -4
2 2 6 -4
3 3 7 -4
4 4 8 -4
> new_column3(dat,z,x*y)
x y z
1 1 5 5
2 2 6 12
3 3 7 21
4 4 8 32
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.