Ця відповідь охоплюватиме багато тих самих елементів, що і відповіді, але це питання (передача назв стовпців функціям) виникає досить часто, що я хотів, щоб там була відповідь, яка охоплювала речі трохи більш всебічно.
Припустимо, у нас дуже простий кадр даних:
dat <- data.frame(x = 1:4,
y = 5:8)
і ми хотіли б написати функцію, яка створює новий стовпець, z
який є сумою стовпців x
і y
.
Тут дуже поширеним каменем спотикання є те, що природна (але неправильна) спроба часто виглядає так:
foo <- function(df,col_name,col1,col2){
df$col_name <- df$col1 + df$col2
df
}
#Call foo() like this:
foo(dat,z,x,y)
Проблема тут полягає в тому df$col1
, що вираз не оцінює col1
. Він просто шукає стовпчик у df
буквальному сенсі col1
. Така поведінка описана в ?Extract
розділі "Рекурсивні (подібні до списку) об'єкти".
Найпростіше і найбільш часто рекомендований рішення просто перейти від $
до [[
і передати аргументи функції в вигляді рядка:
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
Це часто вважається «найкращою практикою», оскільки саме цей метод найскладніший. Передавання назв стовпців як рядків приблизно настільки ж однозначне, як ви можете отримати.
Наступні два варіанти є більш досконалими. Багато популярних пакети використовують ці види техніки, але їх використання також вимагає більше турботи і вміння, так як вони можуть ввести тонкі складності і несподівані точки відмови. Цей розділ книги Advanced R від Hadley є чудовим посиланням на деякі з цих питань.
Якщо ви дійсно хочете врятувати користувача від введення всіх цих лапок, одним із варіантів може бути перетворення голих, не цитованих імен стовпців у рядки, використовуючи 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
Це, чесно кажучи, трохи нерозумно, адже ми справді робимо те саме, що і в new_column1
, просто з купою зайвої роботи з перетворення голих імен на рядки.
Нарешті, якщо ми хочемо по- справжньому уявити, ми можемо вирішити, що замість того, щоб додавати імена двох стовпців, щоб додати, ми хотіли б бути більш гнучкими та допускати інші комбінації двох змінних. У цьому випадку ми, швидше за все, вдамось до використання eval()
виразу, що включає два стовпці:
new_column3 <- function(df,col_name,expr){
col_name <- deparse(substitute(col_name))
df[[col_name]] <- eval(substitute(expr),df,parent.frame())
df
}
Просто для розваги, я все ще використовую deparse(substitute())
для назви нової колонки. Тут буде працювати все наступне:
> 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
Отже, в основному коротка відповідь: передайте назви стовпців data.frame як рядки та використовуйте [[
для вибору окремих стовпців. Тільки почати заглиблюючись eval
, substitute
і т.д. , якщо ви дійсно знаєте , що ви робите.