Я работаю с большим количеством файлов с фиксированной шириной (т.е. без разделительного символа), которые мне нужно прочитать в R. Таким образом, обычно существует определение ширины столбца для синтаксического анализа строки в переменных. Я могу использовать read.fwf
для чтения данных без проблем. Однако для больших файлов это может занять длинное время. Для недавнего набора данных это заняло 800 секунд для чтения в наборе данных с ~ 500 000 строк и 143 переменных.
seer9 <- read.fwf("~/data/rawdata.txt",
widths = cols,
header = FALSE,
buffersize = 250000,
colClasses = "character",
stringsAsFactors = FALSE))
fread
в пакете data.table
в R отлично подходит для решения большинства проблем чтения данных, за исключением того, что он не анализирует файлы фиксированной ширины. Тем не менее, я могу читать каждую строку в виде одной символьной строки (~ 500 000 строк, 1 столбец). Это занимает 3-5 секунд. (Мне нравится data.table.)
seer9 <- fread("~/data/rawdata.txt", colClasses = "character",
sep = "\n", header = FALSE, verbose = TRUE)
На SO есть множество хороших сообщений о том, как анализировать текстовые файлы. См. Предложение JHoward here, чтобы создать матрицу начального и конечного столбцов и substr
для анализа данных. См. Предложение GSee здесь для использования strsplit
. Я не мог понять, как сделать эту работу с этими данными. (Кроме того, Майкл Смит сделал несколько предложений в списке рассылки data.table с участием sed
, которые были выше моей способности реализовать.) Теперь, используя fread
и substr()
, я могу сделать все это примерно через 25-30 секунд. Обратите внимание, что при принуждении к таблице данных в конце происходит кусок времени (5 секунд?).
end_col <- cumsum(cols)
start_col <- end_col - cols + 1
start_end <- cbind(start_col, end_col) # matrix of start and end positions
text <- lapply(seer9, function(x) {
apply(start_end, 1, function(y) substr(x, y[1], y[2]))
})
dt <- data.table(text$V1)
setnames(dt, old = 1:ncol(dt), new = seervars)
Мне интересно, можно ли это улучшить? Я знаю, что я не единственный, кто должен читать файлы с фиксированной шириной, поэтому, если это можно сделать быстрее, это сделает загрузку даже более крупных файлов (с миллионами строк) более терпимыми. Я попытался использовать parallel
с mclapply
и data.table
вместо lapply
, но это ничего не изменило. (Вероятно, из-за моей неопытности в R.) Я полагаю, что функцию Rcpp можно было бы написать, чтобы сделать это очень быстро, но это выходит за рамки моего набора навыков. Кроме того, я не могу использовать приложение и применять соответствующим образом.
Моя реализация data.table(с цепочкой magrittr
) принимает одно и то же время:
text <- seer9[ , apply(start_end, 1, function(y) substr(V1, y[1], y[2]))] %>%
data.table(.)
Может ли кто-нибудь сделать предложения, чтобы улучшить скорость этого? Или это примерно так же хорошо, как и получается?
Вот код для создания подобной таблицы данных внутри R (а не для привязки к фактическим данным). Он должен иметь 331 символ и 500 000 строк. Существуют пробелы для имитации отсутствующих полей в данных, но это НЕ данные, разделенные пробелами. (Я читаю необработанные данные SEER, если кто-то заинтересован.) Также включая ширину столбцов (cols) и имена переменных (seervars), если это помогает кому-то другому. Это фактические определения столбцов и переменных для данных SEER.
seer9 <-
data.table(rep((paste0(paste0(letters, 1000:1054, " ", collapse = ""), " ")),
500000))
cols = c(8,10,1,2,1,1,1,3,4,3,2,2,4,4,1,4,1,4,1,1,1,1,3,2,2,1,2,2,13,2,4,1,1,1,1,3,3,3,2,3,3,3,3,3,3,3,2,2,2,2,1,1,1,1,1,6,6,6,2,1,1,2,1,1,1,1,1,2,2,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,7,5,4,10,3,3,2,2,2,3,1,1,1,1,2,2,1,1,2,1,9,5,5,1,1,1,2,2,1,1,1,1,1,1,1,1,2,3,3,3,3,3,3,1,4,1,4,1,1,3,3,3,3,2,2,2,2)
seervars <- c("CASENUM", "REG", "MAR_STAT", "RACE", "ORIGIN", "NHIA", "SEX", "AGE_DX", "YR_BRTH", "PLC_BRTH", "SEQ_NUM", "DATE_mo", "DATE_yr", "SITEO2V", "LATERAL", "HISTO2V", "BEHO2V", "HISTO3V", "BEHO3V", "GRADE", "DX_CONF", "REPT_SRC", "EOD10_SZ", "EOD10_EX", "EOD10_PE", "EOD10_ND", "EOD10_PN", "EOD10_NE", "EOD13", "EOD2", "EOD4", "EODCODE", "TUMOR_1V", "TUMOR_2V", "TUMOR_3V", "CS_SIZE", "CS_EXT", "CS_NODE", "CS_METS", "CS_SSF1", "CS_SSF2", "CS_SSF3", "CS_SSF4", "CS_SSF5", "CS_SSF6", "CS_SSF25", "D_AJCC_T", "D_AJCC_N", "D_AJCC_M", "D_AJCC_S", "D_SSG77", "D_SSG00", "D_AJCC_F", "D_SSG77F", "D_SSG00F", "CSV_ORG", "CSV_DER", "CSV_CUR", "SURGPRIM", "SCOPE", "SURGOTH", "SURGNODE", "RECONST", "NO_SURG", "RADIATN", "RAD_BRN", "RAD_SURG", "SS_SURG", "SRPRIM02", "SCOPE02", "SRGOTH02", "REC_NO", "O_SITAGE", "O_SEQCON", "O_SEQLAT", "O_SURCON", "O_SITTYP", "H_BENIGN", "O_RPTSRC", "O_DFSITE", "O_LEUKDX", "O_SITBEH", "O_EODDT", "O_SITEOD", "O_SITMOR", "TYPEFUP", "AGE_REC", "SITERWHO", "ICDOTO9V", "ICDOT10V", "ICCC3WHO", "ICCC3XWHO", "BEHANAL", "HISTREC", "BRAINREC", "CS0204SCHEMA", "RAC_RECA", "RAC_RECY", "NHIAREC", "HST_STGA", "AJCC_STG", "AJ_3SEER", "SSG77", "SSG2000", "NUMPRIMS", "FIRSTPRM", "STCOUNTY", "ICD_5DIG", "CODKM", "STAT_REC", "IHS", "HIST_SSG_2000", "AYA_RECODE", "LYMPHOMA_RECODE", "DTH_CLASS", "O_DTH_CLASS", "EXTEVAL", "NODEEVAL", "METSEVAL", "INTPRIM", "ERSTATUS", "PRSTATUS", "CSSCHEMA", "CS_SSF8", "CS_SSF10", "CS_SSF11", "CS_SSF13", "CS_SSF15", "CS_SSF16", "VASINV", "SRV_TIME_MON", "SRV_TIME_MON_FLAG", "SRV_TIME_MON_PA", "SRV_TIME_MON_FLAG_PA", "INSREC_PUB", "DAJCC7T", "DAJCC7N", "DAJCC7M", "DAJCC7STG", "ADJTM_6VALUE", "ADJNM_6VALUE", "ADJM_6VALUE", "ADJAJCCSTG")
UPDATE: LaF выполнил все чтение всего за 7 секунд из файла .txt. Возможно, есть еще более быстрый способ, но я сомневаюсь, что что-то может сделать заметно лучше. Удивительный пакет.
27 июля 2015 г. Обновление Просто хотел предоставить небольшое обновление. Я использовал новый пакет readr, и я смог прочитать весь файл за 5 секунд, используя readr:: read_fwf.
seer9_readr <- read_fwf("path_to_data/COLRECT.TXT",
col_positions = fwf_widths(cols))
Кроме того, обновленная функция stringi:: stri_sub не менее чем в два раза быстрее, чем base:: substr(). Таким образом, в приведенном выше коде, который использует fread для чтения файла (около 4 секунд), а затем применяется для разбора каждой строки, извлечение 143 переменных заняло около 8 секунд с помощью stringi:: stri_sub по сравнению с 19 для base:: substr. Таким образом, fread plus stri_sub все еще занимает около 12 секунд. Неплохо.
seer9 <- fread("path_to_data/COLRECT.TXT",
colClasses = "character",
sep = "\n",
header = FALSE)
text <- seer9[ , apply(start_end, 1, function(y) substr(V1, y[1], y[2]))] %>%
data.table(.)
Обновление за 10 декабря 2015 года:
См. также ниже от @MichaelChirico, который добавил несколько отличных тестов и пакет iotools.