Каков наиболее эффективный способ разделить комбинированный столбец факторов на два столбца факторов в r data.table?

У меня есть большая таблица данных (9 млн строк) с двумя столбцами: fcombined и значение fcombined — это фактор, но на самом деле это результат взаимодействия двух факторов. Теперь возникает вопрос, как наиболее эффективно снова разделить столбец с одним фактором на два? Я уже придумал решение, которое работает нормально, но, возможно, есть более прямой способ, который я пропустил. Рабочий пример:

library(stringr)
f1=1:20
f2=1:20
g=expand.grid(f1,f2)
combinedfactor=as.factor(paste(g$Var1,g$Var2,sep="_"))
largedata=1:10^6
DT=data.table(fcombined=combinedfactor,value=largedata)


splitfactorcol=function(res,colname,splitby="_",namesofnewcols){#the nr. of cols retained is length(namesofnewcols)
  helptable=data.table(.factid=seq_along(levels(res[[colname]])) ,str_split_fixed(levels(res[[colname]]),splitby,length(namesofnewcols)))
  setnames(helptable,colnames(helptable),c(".factid",namesofnewcols))
  setkey(helptable,.factid)
  res$.factid=unclass(res[[colname]])
  setkey(res,.factid)
  m=merge(res,helptable)
  m$.factid=NULL
  m
}
splitfactorcol(DT,"fcombined",splitby="_",c("f1","f2"))

person Johannes Kutsam    schedule 14.06.2013    source источник
comment
ваш код возвращает ошибку: str_split_fixed function not found   -  person Arun    schedule 14.06.2013
comment
Одним из простых способов может быть: DT[, c("a1", "a2") := as.data.frame(do.call(rbind, strsplit(as.character(fcombined), "_")), stringsAsFactors=FALSE)] (а затем преобразовать a1 и a2 в тип numeric). Хотя не уверен, что будет быстрее.   -  person Arun    schedule 14.06.2013
comment
str_split_fixed находится в библиотеке (stringr)   -  person Johannes Kutsam    schedule 14.06.2013
comment
насколько я помню, strsplit медленнее, чем str_split_fixed. Разделение по уровням вместо преобразования всего столбца в символ также сокращает время процессора.   -  person Johannes Kutsam    schedule 14.06.2013
comment
colsplit в пакете reshape2 делает это, но начинает только с вектора (столбец для разделения) и возвращает data.frame, который можно cbind преобразовать обратно в исходные данные. Он не работает в data.table напрямую, поэтому я не знаю, насколько он будет эффективен. Но стоит рассмотреть/сопоставить.   -  person Brian Diggs    schedule 14.06.2013
comment
Код, который вы используете, в значительной степени соответствует тому, что сделал бы я, поэтому я не могу комментировать улучшения эффективности. Два второстепенных момента: DT и helptable являются таблицами данных, поэтому m будет таблицей данных, и вы можете удалить ее по ссылке m[,".factid":=NULL]. Точно так же вы можете создать res$.factid по ссылке: res[".factid":=unclass(get(colname))] или что-то в этом роде. И с setnames, если вы переименовываете все, вы можете просто передать новые имена: setnames(helptable,new_names).   -  person Frank    schedule 14.06.2013
comment
@JohannesKutsam, +1 за использование уровней. Я отпишусь, если мне удастся получить что-то лучше.   -  person Arun    schedule 15.06.2013


Ответы (1)


Я думаю, что это помогает и примерно в 5 раз быстрее.

setkey(DT, fcombined)
DT[DT[, data.table(fcombined = levels(fcombined),
                   do.call(rbind, strsplit(levels(fcombined), "_")))]]

Я разделил уровни, а затем просто объединил этот результат обратно в исходный data.table.

Кстати, в моих тестах strsplit была примерно в 2 раза быстрее (для этой задачи), чем функция stringr.

person eddi    schedule 15.06.2013
comment
@eddi, я думаю, будет лучше, если вы включите результаты тестов, когда вы заявляете точное x-кратное ускорение. Вы все равно проводите тесты... Это поможет другим включить ваши результаты в свои результаты напрямую, когда/если другие тоже ответят. - person Arun; 16.06.2013
comment
@ Арун, ха-ха, наверное, было бы неплохо :) Я не включил его сюда в основном из-за лени, но также и потому, что я не думаю, что точный эталон имеет большое значение в этом случае; также обратите внимание на использование слова «о вышеизложенном», а не на точные цифры - person eddi; 16.06.2013