Я читаю огромный набор данных, используя fread()
из data.table. Проблема в том, что количество полей (разделитель = ;
) различается в каждой строке. Меня в основном интересуют первые 5 столбцов, но я также хочу увидеть содержимое столбца с 6-го по нth.
образец данных
Я читаю данные с data.table::fread()
с sep = ""
, чтобы читать целыми строками.
DT <- data.table::fread("1;2;3;4;5;6
1;2;3;4;5;6;7;8
1;2;3;4;5", sep = "", header = FALSE, col.names = "text" )
# text
#1: 1;2;3;4;5;6
#2: 1;2;3;4;5;6;7;8
#3: 1;2;3;4;5
код до сих пор
Первые пять столбцов присутствуют во всех строках, и я могу легко получить их с помощью tstrsplit()
:
DT[, c("v1", "v2", "v3", "v4", "v5") := tstrsplit( text , ";")[1:5] ][]
# text v1 v2 v3 v4 v5
# 1: 1;2;3;4;5;6 1 2 3 4 5
# 2: 1;2;3;4;5;6;7;8 1 2 3 4 5
# 3: 1;2;3;4;5 1 2 3 4 5
мой вопрос
Я хочу поместить все поля после пятой (или все после пятой точки с запятой) в столбец с именем v6
, чтобы результат выглядел так:
desired_output <- DT[, v6 := c( "6", "6;7;8", NA_character_) ]
# text v1 v2 v3 v4 v5 v6
# 1: 1;2;3;4;5;6 1 2 3 4 5 6
# 2: 1;2;3;4;5;6;7;8 1 2 3 4 5 6;7;8
# 3: 1;2;3;4;5 1 2 3 4 5 <NA>
примечание: длина текста между ; ; может варьироваться, поэтому не всегда один, а также не всегда числовой.
Мои производственные данные содержат более 1 млн строк, поэтому чем быстрее решение, тем лучше.
Не могли бы вы уточнить, почему вы хотите, чтобы «лишние» строки были объединены. Если вы можете жить с отдельными столбцами (кажется более «аккуратным»), используйте sep = ";"
и fill = TRUE
в fread
. Вот и все.
@henrik Я пытался, но это: fread( "./stuff.log", header = FALSE, sep = "\t", fill = TRUE )
все еще выдает ошибку в моих производственных данных, Stopped early on line 201. Expected 8 fields but found 9
..
@akrun the 1;2;3;4;5
- это просто образец текста с образцами разделителей.. числа могут быть любыми, разделители в моих производственных данных на самом деле являются вкладками (\t
). Он хотел проиллюстрировать, что каждая строка имеет как минимум пять полей (а некоторые еще пару).
У @Henrik, похоже, были проблемы здесь: github.com/Rdatatable/data.table/issues/2727
Вариантом может быть separate
с параметром extra
, указанным как «объединить».
library(tidyverse)
n <- 6
DT %>%
separate(text, into = paste0("v", seq_len(n)), extra = "merge",
convert = TRUE, remove = FALSE)
# text v1 v2 v3 v4 v5 v6
#1: 1;2;3;4;5;6 1 2 3 4 5 6
#2: 1;2;3;4;5;6;7;8 1 2 3 4 5 6;7;8
#3: 1;2;3;4;5 1 2 3 4 5 <NA>
это работает, спасибо! .. хотя я предпочитаю (быстрое) решение base
или data.table
, так как мои производственные данные довольно велики (я отредактирую это, чтобы задать вопрос)
@Wimpel Что касается логики, вы проверяете количество элементов или совпадений в каждой строке?
количество «полей».. ;
— это разделитель, указывающий на новое поле. Я просто хочу прочитать текст в файле, но количество столбцов зависит от строки, и fread()
не может справиться с этим правильно за один раз, даже если fill = TRUE
(я еще не нашел правильных настроек).
Вот вариант с data.table
и stringr
. Не уверен, что это быстрее, чем separate
library(stringr)
DT[, paste0('col', 1:5) := tstrsplit(text, ';')[1:5]] # or tstrsplit(str_extract(text, '(\\d+;){4}\\d+'), ';')
DT[, col6 := str_remove(text, '(\\d+;){5}|(\\d+;){4}\\d+')]
DT
# text col1 col2 col3 col4 col5 col6
# 1: 1;2;3;4;5;6 1 2 3 4 5 6
# 2: 1;2;3;4;5;6;7;8 1 2 3 4 5 6;7;8
# 3: 1;2;3;4;5 1 2 3 4 5
Я приблизился к тому, что вы хотите, используя append
transpose
lapply
и paste0
вместе взятые. Не уверен, как он сравнивается с другими, хотя.
DT[, c("v1", "v2", "v3", "v4", "v5", "v6") := append(tstrsplit(text , ";")[1:5],
transpose(lapply(transpose(tstrsplit(text, ";")[-c(1:5)]), paste0, collapse=';')))][]
Это также можно изменить, чтобы сделать это, используя концепцию цепочки для лучшего чтения.
DT[, c("v1", "v2", "v3", "v4", "v5") := tstrsplit(text , ";")[1:5]
][, v6 := transpose(lapply(transpose(tstrsplit(text, ";")[-c(1:5)]), paste0, collapse=';'))][]
Оба дают следующий результат
text v1 v2 v3 v4 v5 v6
1: 1;2;3;4;5;6 1 2 3 4 5 6;NA;NA
2: 1;2;3;4;5;6;7;8 1 2 3 4 5 6;7;8
3: 1;2;3;4;5 1 2 3 4 5 NA;NA;NA
NA
производятся, чтобы сохранить длину элементов списка одинаковой. Но добавление [, v6 := gsub(";NA", "", v6)]
дальше в цепочку удаляет NA
DT[, c("v1", "v2", "v3", "v4", "v5") := tstrsplit(text , ";")[1:5]
][, v6 := transpose(lapply(transpose(tstrsplit(text, ";")[-c(1:5)]), paste0, collapse=';'))
][, v6 := gsub(";NA", "", v6)][]
Наконец давая
text v1 v2 v3 v4 v5 v6
1: 1;2;3;4;5;6 1 2 3 4 5 6
2: 1;2;3;4;5;6;7;8 1 2 3 4 5 6;7;8
3: 1;2;3;4;5 1 2 3 4 5 NA
Проблема в том, что в строке 201 есть 9 столбцов, но на данный момент fread
решил, что есть максимум 8 столбцов. Вы можете взломать его, чтобы прочитать все 9 столбцов с помощью следующей команды:
x <- fread("test.txt",fill=TRUE, sep = "\t", colClasses=rep("logical",9))
Если 9 недостаточно, увеличивайте это число до тех пор, пока вы больше не увидите эту ошибку. На самом деле это не должно приводить какие-либо столбцы к логическим (при указании аргумента colClasses
data.table::fread
отказывается приводить классы столбцов таким образом, что это приводит к потере информации). Я не уверен, какой штраф вызывает этот подход, но я думаю, что он быстрее, чем другие методы (по крайней мере, быстрее после того, как вы установили максимальное количество столбцов).
Если вы по-прежнему используете хочу для вставки столбцов 6+ вместе в один столбец, существует множество способов сделать это.
Для потомков см. ссылку, указанную в комментариях к вопросу (https://github.com/Rdatatable/data.table/issues/2727), чтобы узнать, решена ли проблема.
Это должно работать... Я пробовал, но все равно ошибки... Я также пытался "установить" colclasses на именованный вектор... тоже ошибки...
Другой вариант:
DT[, paste0("v", 1:5) := tstrsplit(text, ";", keep = 1:5)]
DT[, v6 := stringi::stri_match(text, regex = "^(?:.*?;){5}(.*)$")[,2]][]
В вашем наборе данных 1: 5 в «тексте» является обычным явлением. Это ты проверяешь? Кроме того, имеет ли значение порядок элементов, скажем, 1:5 в первых рядах, 2, 4, 1, 3 во втором ряду или 4, 4, 1, 2 в третьем ряду?