2016-05-18 1 views
1

У меня есть именованный список переменной длины, содержащие функцииБыстрый способ (векторизации?), Чтобы применить список функций для соответствующих столбцов в кадре данных

mFunc <- list(A = identity, B = exp) 

У меня также есть data.frame, имена которых являются надстройкой имена mFunc:

dat <- data.frame(A = 1:3, B = 1:3, C = 1:3) 

То, что я хочу сделать, это использовать все функции в mFunc для соответствующих столбцов в dat. Если бы я сделать это вручную, я бы

dat$A <- mFunc$A(dat$A) 
dat$B <- mFunc$B(dat$B) 

и ожидаемые результаты могли бы быть:

# A   B C 
# 1 1 2.718282 1 
# 2 2 7.389056 2 
# 3 3 20.085537 3 

Я думал об использовании петли на имена mFunc

library(plyr) 
dat[, names(mFunc)] <- llply(names(mFunc), function(n) mFunc[[n]](dat[[n]])) 

, который дает мне желаемый результат. Мне было интересно, есть ли более быстрые (векторизованные) способы сделать это без использования неявного цикла?

+0

Как сделать одну функцию, которая переключается на разные функции на основе имени прошедшего столбца? – zx8754

+2

Заполнение столбцов должно быть достаточно эффективным. Я не думаю, что вы можете векторизовать его любым лучшим способом, поскольку у вас есть разные функции, которые необходимо оценить. Хотя я не понимаю, почему вы используете 'plyr'. Почему не просто 'dat [, names (mFunc)] <- lapply (names (mFunc), function (x) mFunc [[x]] (dat [[x]]))'? Другим способом может быть использование «mapply», например 'dat [, names (mFunc)] <- mapply (function (x, y) x (y), mFunc, dat [, names (mFunc)])' –

+1

Я использую 'plyr', потому что мне нравится концепция, что вы можете контролировать ввод и вывод вашей функции' apply'. База 'R' имеет аналогичные функции, но я обнаружил, что функции' plyr' более читабельны и легче переключаются между собой. Без контекста я согласен с вами в том, что 'lapply' делает то же самое, но в остальной части моего кода я использую всевозможные' 'ply', и мне нравится использовать глаголы из одного семейства. – thothal

ответ

0

Benchmark

Вслед за обсуждения, я сделал некоторые тесты с llply и lapply решение:

library(dplyr) 
library(tidyr) 
library(microbenchmark) 

makeTestData <- function(n, m = 10) { 
    ret <- matrix(1, m, n) 
    as.list(as.data.frame(ret)) 
} 

d1e7 <- makeTestData(1e7) 
d1e6 <- makeTestData(1e6) 
d1e5 <- makeTestData(1e5) 
d1e4 <- makeTestData(1e4) 

(mb <- microbenchmark(dplyr_1e7 = llply(d1e7, sum), 
         base__1e7 = lapply(d1e7, sum), 
         dplyr_1e6 = llply(d1e6, sum), 
         base__1e6 = lapply(d1e6, sum), 
         dplyr_1e5 = llply(d1e5, sum), 
         base__1e5 = lapply(d1e5, sum), 
         dplyr_1e4 = llply(d1e4, sum), 
         base__1e4 = lapply(d1e4, sum), 
         unit = "s")) 

# Unit: seconds 
#  expr   min   lq  mean  median   uq  max neval cld 
# dplyr_1e7 0.516720538 0.589467527 0.684496553 0.626970606 0.713114314 1.50305854 100 d 
# base__1e7 0.510537527 0.568317076 0.650109031 0.593912047 0.654385189 1.65846752 100 cd 
# dplyr_1e6 0.498767583 0.574463177 0.678273933 0.614690247 0.695204388 1.22767911 100 d 
# base__1e6 0.490427644 0.543392591 0.610153445 0.587542713 0.638059997 1.03797757 100 c 
# dplyr_1e5 0.045505505 0.047664210 0.076620745 0.049840086 0.062213015 0.83327188 100 b 
# base__1e5 0.043557401 0.046385212 0.079063845 0.050644790 0.079270672 0.54647818 100 b 
# dplyr_1e4 0.004488338 0.004746704 0.005941600 0.004970133 0.005845892 0.03784612 100 a 
# base__1e4 0.004290173 0.004527419 0.006759915 0.004698939 0.005140864 0.04752546 100 a 

И некоторые статистические данные:

mb %>% group_by(expr) %>% summarise(mean = mean(time)) %>% 
    separate(expr, c("system", "size"), sep = "\\_+") %>% 
    spread(system, mean) %>% mutate(ratio = base/dplyr) 

# Source: local data frame [4 x 4] 

# size  base  dplyr  ratio 
# (chr)  (dbl)  (dbl)  (dbl) 
# 1 1e4 6759915 5941600 1.1377262 
# 2 1e5 79063845 76620745 1.0318856 
# 3 1e6 610153445 678273933 0.8995679 
# 4 1e7 650109031 684496553 0.9497623 

Заключение

Таким образом, решение base на 6% быстрее для больших наборов данных и на 13% медленнее (!) Для небольших наборов данных. Абсолютное время выполнения для обоих значений одинаково. Следовательно, я могу принять небольшую задержку (даже если она присутствует: не заметна для меня в любом случае) для использования того же семейства глаголов.

+0

Процент - относительная вещь. Быть медленнее на небольшом наборе данных на 13% не ощущается. Хотя медленнее на больших наборах данных даже на несколько процентов ощущалось бы гораздо больше. Хотя в этом конкретном случае нет большой разницы. Но это было в значительной степени моей точкой зрения, что ваше решение должно быть достаточно эффективным. –

+0

Уверенный, 99% на 1 мс пренебрежимо мал, против 0.1% в год делает огромную разницу - согласен. Поэтому мне было интересно, почему вы думаете, что 'lapply' будет * гораздо более эффективным * чем' llply'? – thothal

+1

Потому что 'llply' является оберткой. –

 Смежные вопросы

  • Нет связанных вопросов^_^