2014-01-22 5 views
5

У меня очень большой Data Table с двумя колоннами. И я хочу применить пользовательскую функцию к определенному столбцу. Код для создания проблемы заключается в следующем:Применить пользовательскую функцию для всего столбца data.table?

require(data.table) 
X <- rep("This is just random text", 1e5) 
data <- data.frame(1:1e5, replicate(1, X, simplify=FALSE), stringsAsFactors=FALSE) 
colnames(data) <- paste("X", seq_len(ncol(data)), sep="") 
DT <- as.data.table(data) 

Теперь у нас есть большой таблицы данных, которая выглядит как

| X1 |   X2   | 
|----|-------------------------| 
| 1 | This is just random text| 
| 2 | This is just random text| 
| 3 | This is just random text| 
| 4 | This is just random text| 
| .. |   ...   | 

Что делать, если я хочу сделать некоторые векторную операцию на любом из этой колонки с учетом что эта таблица данных будет иметь очень большие размеры (приблизительно ~100M строк).

Давайте рассмотрим пример столбца X1. Предположим, я хочу, чтобы применить следующую функцию на нем:

Fun4X1 <- function(x){return(x+x*2)} 

И очень сложную NLP функцию на колонке X2, которая выглядит что-то вроде

Fun4X2 <- function(x){ 
      require(stringr) 
      return(str_split(x, " ")[[1]][1]) 
      } 

Как я буду ходить делать это для большого набора данных ? Пожалуйста, предложите мин. поскольку мой Function сам по себе очень сложный.

P.S. Я пробовал foreach, sapply, и, конечно, for-loop и все очень медленно на довольно хорошей аппаратной системе.

+0

Что не так с использованием 'Fun4X2 [DT $ X2]'? – Andrie

+2

Кроме того, если вы выполняете обработку текста, ознакомьтесь с 'grep' и' gsub' - функции 'stringr' являются простыми оболочками вокруг этих очень мощных функций. – Andrie

+0

Как насчет 'DT [, список (Fun4X1 (X1), Fun4X3 (X2))]' где «Fun4X3» определяется как: «Fun4X3 <- function (x) strsplit (x,« ») [[1]] [ 1] ' – A5C1D2H2I1M1N2O1R2T1

ответ

1

Проверьте пакет снегопада, http://cran.r-project.org/web/packages/snowfall/snowfall.pdf, для параллельных вычислений. Вы можете настроить локальный кластер и использовать все свои ядра. Я обнаружил, что, используя sfApply из этого пакета, он уменьшил большую часть моего времени вычислений на 5X

(У меня 8-ядерный, поэтому он будет в 8 раз быстрее, но, очевидно, затраты на загрузку данные в кластер и их сбор в конце).

например.

install.packages('snowfall') 

require(snowfall) 
sfInit(parallel=TRUE, cpus=4) 
sfExport(list=c('DT','Fun4X1','Fun4X2')) 
sfApply(DT,1,function(X) return(c(fun4X1(X[1]),fun4X2(X[2])))) 
sfStop() 

С apply занимает 25,07 сек, с sfApply занимает 9,11 сек на моей машине

+0

Спасибо @James за усилия. Я попробовал ваше решение с тем, что дала @Ananda, и я использую некоторые функции, такие как as.String из пакета NLP и т. Д. Внутри «Fun4X2», поэтому, кажется, есть некоторые ошибки, такие как «Ошибка в checkForRemoteErrors (val): 4 узла, вызванные ошибками ; первая ошибка: не удалось найти функцию «as.String» ' – Ankit

+0

Решила ошибку, вызвав эти пакеты внутри самой функции ... Спасибо ... Попробует новое решение на больших данных и опубликует отзывы и комбинированный метод в ближайшее время .. – Ankit

1

Вы можете использовать быстрый и векторизованную функцию sub для второй задачи:

Fun4X2 <- function(x) sub("(.+?) .*", "\\1", x) 

head(Fun4X2(DT[,X2])) 
# [1] "This" "This" "This" "This" "This" "This" 
+0

Это действительно [самый быстрый вариант] (http://stackoverflow.com/a/21288337/1270695) для этой части проблемы, если она является частью фактической проблемы OP. +1 для этого :-) – A5C1D2H2I1M1N2O1R2T1

4

Подход должен быть ничем не отличается от применения любой другой встроенной (или упакованной) функции к определенному столбцу в data.table: Используйте конструкцию типа list(fun(variable), otherfun(othervariable)). Вы также можете назвать результирующие столбцы, если захотите, иначе они будут называться «V1», «V2» и так далее.

Другими словами, для вашей проблемы вы можете сделать:

DT[, list(X1 = Fun4X1(X1), X2 = Fun4X2(X2))] 

Я подозреваю, однако, что много вашего замедления может быть связано с функциями, которые вы действительно используете.Сравните следующие незначительные уточнения:

Fun4X2.old <- function(x){ 
    require(stringr) 
    return(str_split(x, " ")[[1]][1]) 
} 

Fun4X2.new1 <- function(x) { 
    vapply(strsplit(x, " "), 
     function(y) y[1], character(1)) 
} 

Fun4X2.new2 <- function(x) { 
    vapply(strsplit(x, " ", fixed=TRUE), 
     function(y) y[1], character(1)) 
} 

Fun4X2.sub <- function(x) sub("(.+?) .*", "\\1", x) 

X <- rep("This is just random text", 1e5)  

system.time(out1 <- Fun4X2.old(X)) 
# user system elapsed 
# 18.838 0.000 18.659 
system.time(out2 <- Fun4X2.new1(X)) 
# user system elapsed 
# 0.000 0.000 0.944 
system.time(out3 <- Fun4X2.new2(X)) 
# user system elapsed 
# 1.584 0.000 0.270 
system.time(out4 <- Fun4X2.sub(X)) 
# user system elapsed 
# 0.000 0.000 0.222 

последнее замечание, по поводу your comment here:

@AnandaMahto I am looking for something similar to this but if I use your solution then the output on text column in not vectorized and I get same output even if I have different text in each row

Кстати, оригинал Fun4X2() (переименован Fun4X2.old() выше) имеет такое же поведение.

DT2 <- data.table(X1 = 1:4, X2 = c("a b c", "d e f", "g h i", "j k l")) 
DT2[, list(Fun4X1(X1), Fun4X2.old(X2))] 
# V1 V2 
# 1: 3 a 
# 2: 6 a 
# 3: 9 a 
# 4: 12 a 

DT2[, list(Fun4X1(X1), Fun4X2.new1(X2))] 
# V1 V2 
# 1: 3 a 
# 2: 6 d 
# 3: 9 g 
# 4: 12 j 
+0

Спасибо @Ananda за решение. Просто одно быстрое сомнение; мы можем принять это решение и обработать его в нескольких ядрах одновременно. Я имею в виду, что если у меня есть 4 ядра и 100000 строк, я могу разделить данные, чтобы сказать 25k строк для каждого ядра и обработать его, а затем снова объединить? Так как это может увеличить скорость почти в 4 раза. Кроме того, столбец 1 может действовать как ключ, а столбец 2 - как значение. – Ankit

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