R: Разделить несбалансированный список в столбце data.frame - PullRequest
4 голосов
/ 18 января 2011

Предположим, у вас есть фрейм данных со следующей структурой:

df <- data.frame(a=c(1,2,3,4), b=c("job1;job2", "job1a", "job4;job5;job6", "job9;job10;job11"))

, где столбец b представляет собой список, разделенный точкой с запятой (несбалансированный по строке).Идеальный data.frame:

id,job,jobNum
1,job1,1
1,job2,2
...
3,job6,3
4,job9,1
4,job10,2
4,job11,3

У меня есть частичное решение, которое занимает почти 2 часа (170K строк):

# Split the column by the semicolon.  Results in a list.
df$allJobs <- strsplit(df$b, ";", fixed=TRUE)

# Function to reshape column that is a list as a data.frame
simpleStack <- function(data){
    start <- as.data.frame.list(data)                       
    names(start) <-c("id", "job")
    return(start)
}
# pylr!
system.time(df2 <- ddply(df, .(id), simpleStack))

Кажется, это проблема размера, потому чтоесли я запускаю

system.time(df2 <- ddply(df[1:4000,c("id", "allJobs")], .(id), simpleStack))

, это займет всего 9 секунд.Первое преобразование в набор data.frames с помощью sapply (с другой функцией) происходит быстро, но требуемый rbind занимает еще больше времени.

Ответы [ 2 ]

12 голосов
/ 18 января 2011
#Split by ; as before
allJobs <- strsplit(df$b, ";", fixed=TRUE)

#Replicate a by the number of jobs in each case
n <- sapply(allJobs, length)
id <- rep(df$a, times = n)

#Turn allJobs into a vector
job <- unlist(allJobs)

#Retrieve position of each job
jobNum <- unlist(lapply(n, seq_len))

#Combine into a data frame
df2 <- data.frame(id = id, job = job, jobNum = jobNum)
6 голосов
/ 26 августа 2013

cSplit из моего пакета "splitstacksahpe" предназначен для обработки подобного рода манипуляций с данными.

Вот этот вопрос в действии:

df <- data.frame(a=c(1,2,3,4), 
               b=c("job1;job2", "job1a", "job4;job5;job6", "job9;job10;job11"))

# install.packages("splitstackshape")
library(splitstackshape)
cSplit(df, "b", ";", "long", makeEqual = FALSE)
#    a b_new
# 1: 1  job1
# 2: 1  job2
# 3: 2 job1a
# 4: 3  job4
# 5: 3  job5
# 6: 3  job6
# 7: 4  job9
# 8: 4 job10
# 9: 4 job11

Выможно также использовать strsplit в «dplyr», а затем продолжить с «1009 * из« tidyr », например:

library(dplyr)
library(tidyr)
df %>% 
  mutate(b = strsplit(as.character(b), ";", fixed = TRUE)) %>% 
  unnest(b)
#   a     b
# 1 1  job1
# 2 1  job2
# 3 2 job1a
# 4 3  job4
# 5 3  job5
# 6 3  job6
# 7 4  job9
# 8 4 job10
# 9 4 job11
...