Разделить строковый столбец фрейма данных на несколько столбцов
Я хотел бы взять данные из формы
before = data.frame(attr = c(1,30,4,6), type=c('foo_and_bar','foo_and_bar_2'))
attr type
1 1 foo_and_bar
2 30 foo_and_bar_2
3 4 foo_and_bar
4 6 foo_and_bar_2
и использовать split() в графе "type" С выше, чтобы получить что-то вроде этого:
attr type_1 type_2
1 1 foo bar
2 30 foo bar_2
3 4 foo bar
4 6 foo bar_2
Я придумал что-то невероятно сложное с участием какой-то формы apply это сработало, но с тех пор я потерял это. Это казалось слишком сложным, чтобы быть лучшим способом. Я могу использовать strsplit как показано ниже, но затем неясно, как вернуть это обратно в 2 столбца в фрейме данных.
> strsplit(as.character(before$type),'_and_')
[[1]]
[1] "foo" "bar"
[[2]]
[1] "foo" "bar_2"
[[3]]
[1] "foo" "bar"
[[4]]
[1] "foo" "bar_2"
Спасибо за любую указатели. Я еще не совсем грокнул R-списки.
15 ответов:
другой вариант-использовать новый пакет tidyr.
library(dplyr) library(tidyr) before <- data.frame( attr = c(1, 30 ,4 ,6 ), type = c('foo_and_bar', 'foo_and_bar_2') ) before %>% separate(type, c("foo", "bar"), "_and_") ## attr foo bar ## 1 1 foo bar ## 2 30 foo bar_2 ## 3 4 foo bar ## 4 6 foo bar_2
5 лет спустя добавление обязательного
data.tableрешениеlibrary(data.table) ## v 1.9.6+ setDT(before)[, paste0("type", 1:2) := tstrsplit(type, "_and_")] before # attr type type1 type2 # 1: 1 foo_and_bar foo bar # 2: 30 foo_and_bar_2 foo bar_2 # 3: 4 foo_and_bar foo bar # 4: 6 foo_and_bar_2 foo bar_2мы также могли бы убедиться, что результирующие столбцы будут иметь правильные типы и повышение производительности путем добавления
type.convertиfixedаргументы (с"_and_"на самом деле это не регулярное выражение)setDT(before)[, paste0("type", 1:2) := tstrsplit(type, "_and_", type.convert = TRUE, fixed = TRUE)]
еще один подход: использовать
rbindonout:before <- data.frame(attr = c(1,30,4,6), type=c('foo_and_bar','foo_and_bar_2')) out <- strsplit(as.character(before$type),'_and_') do.call(rbind, out) [,1] [,2] [1,] "foo" "bar" [2,] "foo" "bar_2" [3,] "foo" "bar" [4,] "foo" "bar_2"и совместить:
data.frame(before$attr, do.call(rbind, out))
обратите внимание, что sapply с помощью " ["можно использовать для извлечения первого или второго элементов в этих списках так:
before$type_1 <- sapply(strsplit(as.character(before$type),'_and_'), "[", 1) before$type_2 <- sapply(strsplit(as.character(before$type),'_and_'), "[", 2) before$type <- NULLи вот метод gsub:
before$type_1 <- gsub("_and_.+$", "", before$type) before$type_2 <- gsub("^.+_and_", "", before$type) before$type <- NULL
вот один лайнер по тем же линиям, что и решение Анико, но с использованием пакета stringr Хэдли:
do.call(rbind, str_split(before$type, '_and_'))
легко использовать
sapply()и[функция:before <- data.frame(attr = c(1,30,4,6), type=c('foo_and_bar','foo_and_bar_2')) out <- strsplit(as.character(before$type),'_and_')например:
> data.frame(t(sapply(out, `[`))) X1 X2 1 foo bar 2 foo bar_2 3 foo bar 4 foo bar_2
sapply()результат является матрицей и нуждается в транспонировании и приведении обратно к фрейму данных. Именно тогда некоторые простые манипуляции, которые дают желаемый результат:after <- with(before, data.frame(attr = attr)) after <- cbind(after, data.frame(t(sapply(out, `[`)))) names(after)[2:3] <- paste("type", 1:2, sep = "_")в этот момент
afterто, что вы хотели> after attr type_1 type_2 1 1 foo bar 2 30 foo bar_2 3 4 foo bar 4 6 foo bar_2
вот базовый R один лайнер, который перекрывает ряд предыдущих решений, но возвращает данные.рамка с собственными именами.
out <- setNames(data.frame(before$attr, do.call(rbind, strsplit(as.character(before$type), split="_and_"))), c("attr", paste0("type_", 1:2))) out attr type_1 type_2 1 1 foo bar 2 30 foo bar_2 3 4 foo bar 4 6 foo bar_2он использует
strsplitразбить переменную, иdata.frameСdo.call/rbindпоместить данные обратно в данных.рамка. Дополнительным дополнительным улучшением является использованиеsetNamesдля добавления имен переменных в данные.рамка.
объект почти исчерпано, я хотел бы предложить решение немного более общей версии, где вы не знаете количество выходных столбцов, априори. Так, например, у вас есть
before = data.frame(attr = c(1,30,4,6), type=c('foo_and_bar','foo_and_bar_2', 'foo_and_bar_2_and_bar_3', 'foo_and_bar')) attr type 1 1 foo_and_bar 2 30 foo_and_bar_2 3 4 foo_and_bar_2_and_bar_3 4 6 foo_and_barмы не можем использовать dplyr
separate()потому что мы не знаем количество столбцов результата до раскола, тогда я создал функцию, которая используетstringrчтобы разделить столбец, учитывая шаблон и префикс имени для созданных столбцов. Я надеюсь, что кодирование используемые шаблоны, являются правильными.split_into_multiple <- function(column, pattern = ", ", into_prefix){ cols <- str_split_fixed(column, pattern, n = Inf) # Sub out the ""'s returned by filling the matrix to the right, with NAs which are useful cols[which(cols == "")] <- NA cols <- as.tibble(cols) # name the 'cols' tibble as 'into_prefix_1', 'into_prefix_2', ..., 'into_prefix_m' # where m = # columns of 'cols' m <- dim(cols)[2] names(cols) <- paste(into_prefix, 1:m, sep = "_") return(cols) }мы можем использовать
split_into_multipleв трубе dplyr следующим образом:after <- before %>% bind_cols(split_into_multiple(.$type, "_and_", "type")) %>% # selecting those that start with 'type_' will remove the original 'type' column select(attr, starts_with("type_")) >after attr type_1 type_2 type_3 1 1 foo bar <NA> 2 30 foo bar_2 <NA> 3 4 foo bar_2 bar_3 4 6 foo bar <NA>и тогда мы можем использовать
gatherприбраться...after %>% gather(key, val, -attr, na.rm = T) attr key val 1 1 type_1 foo 2 30 type_1 foo 3 4 type_1 foo 4 6 type_1 foo 5 1 type_2 bar 6 30 type_2 bar_2 7 4 type_2 bar_2 8 6 type_2 bar 11 4 type_3 bar_3
другой подход, если вы хотите придерживаться с
strsplit()использовать . Вот такое решение.tmp <- matrix(unlist(strsplit(as.character(before$type), '_and_')), ncol=2, byrow=TRUE) after <- cbind(before$attr, as.data.frame(tmp)) names(after) <- c("attr", "type_1", "type_2")
начиная с версии R 3.4.0 вы можете использовать
strcapture()С utils пакет(входит в комплект базовых установок R), привязка вывода к другому столбцу (столбцам).out <- strcapture( "(.*)_and_(.*)", as.character(before$type), data.frame(type_1 = character(), type_2 = character()) ) cbind(before["attr"], out) # attr type_1 type_2 # 1 1 foo bar # 2 30 foo bar_2 # 3 4 foo bar # 4 6 foo bar_2
этот вопрос довольно старый, но я добавлю решение, которое я нашел, будет самым простым в настоящее время.
library(reshape2) before = data.frame(attr = c(1,30,4,6), type=c('foo_and_bar','foo_and_bar_2')) newColNames <- c("type1", "type2") newCols <- colsplit(before$type, "_and_", newColNames) after <- cbind(before, newCols) after$type <- NULL after
база, но, вероятно, медленно:
n <- 1 for(i in strsplit(as.character(before$type),'_and_')){ before[n, 'type_1'] <- i[[1]] before[n, 'type_2'] <- i[[2]] n <- n + 1 } ## attr type type_1 type_2 ## 1 1 foo_and_bar foo bar ## 2 30 foo_and_bar_2 foo bar_2 ## 3 4 foo_and_bar foo bar ## 4 6 foo_and_bar_2 foo bar_2
tp <- c("a-c","d-e-f","g-h-i","m-n") temp = strsplit(as.character(tp),'-') x=c(); y=c(); z=c(); #tab=data.frame() #tab= cbind(tab,c(x,y,z)) for(i in 1:length(temp) ) { l = length(temp[[i]]); if(l==2) { x=c(x,temp[[i]][1]); y=c(y,"NA") z=c(z,temp[[i]][2]); df= as.data.frame(cbind(x,y,z)) }else { x=c(x,temp[[i]][1]); y=c(y,temp[[i]][2]); z=c(z,temp[[i]][3]); df= as.data.frame(cbind(x,y,z)) } }
Comments