2010-12-03 3 views
136

Я хотел бы взять данные формыСплит столбец кадра данных в несколько столбцов

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-списки.

ответ

179

Использование stringr::str_split_fixed

library(stringr) 
str_split_fixed(before$type, "_and_", 2) 
+2

это работало очень хорошо для моей проблемы и сегодня .. но это было добавление 'c' в начале каждой строки. Любая идея, почему? `left_right <- str_split_fixed (as.character (split_df), '\">', 2) ` – LearneR 2015-07-28 06:53:12

+0

Я хотел бы разделить с шаблоном, который имеет« ... », когда я применяю эту функцию, он ничего не возвращает. может быть проблемой. Мой тип - это что-то вроде «test ... score» – user3841581 2016-03-14 08:15:50

27

Обратите внимание, что 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 
10

Простой способ заключается в использовании 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_') 

F или пример:

> 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 
3

Другой подход, если вы хотите придерживаться strsplit() является использование unlist() команда. Вот решение по этим направлениям.

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") 
36

Еще один подход: использовать rbind на out:

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)) 
+1

Другая альтернатива ive в новых версиях R - это `strcapture (" (. *) _ и _ (. *) ", as.character (до $ type), data.frame (type_1 =" ", type_2 =" "))` – 2016-11-10 18:23:33

24

вот один лайнер по той же схеме, как решение Анико, но с помощью Хэдли stringr:

do.call(rbind, str_split(before$type, '_and_')) 
+8

это также работает с strsplit из базового пакета – schultem 2013-03-07 09:46:23

+1

Хорошая уловка, лучшее решение для меня.Хотя немного медленнее, чем с пакетом `stringr`. – Melka 2016-03-30 11:34:08

94

Другой вариант заключается в использовании нового пакета 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 
15

Чтобы добавить опции, вы можете также использовать мою splitstackshape::cSplit функцию так:

library(splitstackshape) 
cSplit(before, "type", "_and_") 
# attr type_1 type_2 
# 1: 1 foo bar 
# 2: 30 foo bar_2 
# 3: 4 foo bar 
# 4: 6 foo bar_2 
35

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)] 
5

Вот основание R один вкладыш, который накладывается рядом предыдущих решений, но возвращают data.frame с именами.

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 поместить данные обратно в data.frame. Дополнительным дополнительным улучшением является использование setNames для добавления имен переменных в data.frame.

-4
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)) 
    } 
} 
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 
1

Этот вопрос довольно старый, но я добавлю решение, которое я нашел самым простым в настоящее время.

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 
1

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

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 
0

база, но, вероятно, медленно:

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 
Смежные вопросы