Вопрос по r-faq, split, r, string – Разделите запятую строки в столбце на отдельные строки
У меня есть фрейм данных, вот так:
data.frame(director = c("Aaron Blaise,Bob Walker", "Akira Kurosawa",
"Alan J. Pakula", "Alan Parker", "Alejandro Amenabar", "Alejandro Gonzalez Inarritu",
"Alejandro Gonzalez Inarritu,Benicio Del Toro", "Alejandro González Iñárritu",
"Alex Proyas", "Alexander Hall", "Alfonso Cuaron", "Alfred Hitchcock",
"Anatole Litvak", "Andrew Adamson,Marilyn Fox", "Andrew Dominik",
"Andrew Stanton", "Andrew Stanton,Lee Unkrich", "Angelina Jolie,John Stevenson",
"Anne Fontaine", "Anthony Harvey"), AB = c('A', 'B', 'A', 'A', 'B', 'B', 'B', 'A', 'B', 'A', 'B', 'A', 'A', 'B', 'B', 'B', 'B', 'B', 'B', 'A'))
Как видите, некоторые записи вdirector
столбец - это несколько имен, разделенных запятыми. Я хотел бы разбить эти записи на отдельные строки, сохраняя при этом значения другого столбца. Например, первая строка в приведенном выше фрейме данных должна быть разбита на две строки с одним именем в каждойdirector
столбец и «А» вAB
колонка.
r-faq
). На сегодняшний день на него ответили три раза, предлагая 6 различных подходов, ноне хватает эталона в качестве руководства, какой из подходов является самым быстрым1.
Тестовые решения включают в себя
Мэтью Лундбергбазовый подход R но модифицируется в соответствии сРич Скривенкомментарий,Яп-х дваdata.table
методы и два /dplyr
tidyr
подходы,Анандаsplitstackshape
решение,и два дополнительных варианта Jaap'sdata.table
методы.В целом 8 различных методов были сопоставлены с 6 различными размерами фреймов данных с использованиемmicrobenchmark
пакет (см. код ниже).
Данные выборки, представленные OP, состоят только из 20 строк. Для создания больших фреймов данных эти 20 строк просто повторяются 1, 10, 100, 1000, 10000 и 100000 раз, что дает размер проблемы до 2 миллионов строк.
Результаты тестовРезультаты тестов показывают, что для достаточно больших фреймов данных всеdata.table
методы быстрее, чем любой другой метод. Для фреймов данных с более чем 5000 строк, Jaap'sdata.table
способ 2 и вариантDT3
являются самыми быстрыми, величины быстрее, чем самые медленные методы.
Примечательно, что время двухtidyverse
методы иsplistackshape
решение настолько похоже, чтоСложно выделить кривые на графике. Это самый медленный из тестируемых методов для всех размеров фреймов данных.
Для небольших кадров данных, МэттS Base R решение иdata.table
метод 4, кажется, имеет меньше накладных расходов, чем другие методы.
director <-
c("Aaron Blaise,Bob Walker", "Akira Kurosawa", "Alan J. Pakula",
"Alan Parker", "Alejandro Amenabar", "Alejandro Gonzalez Inarritu",
"Alejandro Gonzalez Inarritu,Benicio Del Toro", "Alejandro González Iñárritu",
"Alex Proyas", "Alexander Hall", "Alfonso Cuaron", "Alfred Hitchcock",
"Anatole Litvak", "Andrew Adamson,Marilyn Fox", "Andrew Dominik",
"Andrew Stanton", "Andrew Stanton,Lee Unkrich", "Angelina Jolie,John Stevenson",
"Anne Fontaine", "Anthony Harvey")
AB <- c("A", "B", "A", "A", "B", "B", "B", "A", "B", "A", "B", "A",
"A", "B", "B", "B", "B", "B", "B", "A")
library(data.table)
library(magrittr)
Определить функцию для тестов прогонов размера задачиn
run_mb <- function(n) {
# compute number of benchmark runs depending on problem size `n`
mb_times <- scales::squish(10000L / n , c(3L, 100L))
cat(n, " ", mb_times, "\n")
# create data
DF <- data.frame(director = rep(director, n), AB = rep(AB, n))
DT <- as.data.table(DF)
# start benchmarks
microbenchmark::microbenchmark(
matt_mod = {
s <- strsplit(as.character(DF$director), ',')
data.frame(director=unlist(s), AB=rep(DF$AB, lengths(s)))},
jaap_DT1 = {
DT[, lapply(.SD, function(x) unlist(tstrsplit(x, ",", fixed=TRUE))), by = AB
][!is.na(director)]},
jaap_DT2 = {
DT[, strsplit(as.character(director), ",", fixed=TRUE),
by = .(AB, director)][,.(director = V1, AB)]},
jaap_dplyr = {
DF %>%
dplyr::mutate(director = strsplit(as.character(director), ",")) %>%
tidyr::unnest(director)},
jaap_tidyr = {
tidyr::separate_rows(DF, director, sep = ",")},
cSplit = {
splitstackshape::cSplit(DF, "director", ",", direction = "long")},
DT3 = {
DT[, strsplit(as.character(director), ",", fixed=TRUE),
by = .(AB, director)][, director := NULL][
, setnames(.SD, "V1", "director")]},
DT4 = {
DT[, .(director = unlist(strsplit(as.character(director), ",", fixed = TRUE))),
by = .(AB)]},
times = mb_times
)
}
Запустите бенчмарк для разных задач# define vector of problem sizes
n_rep <- 10L^(0:5)
# run benchmark for different problem sizes
mb <- lapply(n_rep, run_mb)
Подготовить данные для построенияmbl <- rbindlist(mb, idcol = "N")
mbl[, n_row := NROW(director) * n_rep[N]]
mba <- mbl[, .(median_time = median(time), N = .N), by = .(n_row, expr)]
mba[, expr := forcats::fct_reorder(expr, -median_time)]
Создать диаграммуlibrary(ggplot2)
ggplot(mba, aes(n_row, median_time*1e-6, group = expr, colour = expr)) +
geom_point() + geom_smooth(se = FALSE) +
scale_x_log10(breaks = NROW(director) * n_rep) + scale_y_log10() +
xlab("number of rows") + ylab("median of execution time [ms]") +
ggtitle("microbenchmark results") + theme_bw()
Информация о сеансе версии пакета (отрывок) 1devtools::session_info()
#Session info
# version R version 3.3.2 (2016-10-31)
# system x86_64, mingw32
#Packages
# data.table * 1.10.4 2017-02-01 CRAN (R 3.3.2)
# dplyr 0.5.0 2016-06-24 CRAN (R 3.3.1)
# forcats 0.2.0 2017-01-23 CRAN (R 3.3.2)
# ggplot2 * 2.2.1 2016-12-30 CRAN (R 3.3.2)
# magrittr * 1.5 2014-11-22 CRAN (R 3.3.0)
# microbenchmark 1.4-2.1 2015-11-25 CRAN (R 3.3.3)
# scales 0.4.1 2016-11-09 CRAN (R 3.3.2)
# splitstackshape 1.4.2 2014-10-23 CRAN (R 3.3.3)
# tidyr 0.6.1 2017-01-10 CRAN (R 3.3.2)
Мое любопытство было задетоэтот обильный комментарий Brilliant! На порядок быстрее! кtidyverse
ответвопрос который был закрыт как дубликат этого вопроса.
data.table
dplyr
, так далее.
Uwe
Называя ваш оригинальный data.framev
у нас есть это:
> s <- strsplit(as.character(v$director), ',')
> data.frame(director=unlist(s), AB=rep(v$AB, sapply(s, FUN=length)))
director AB
1 Aaron Blaise A
2 Bob Walker A
3 Akira Kurosawa B
4 Alan J. Pakula A
5 Alan Parker A
6 Alejandro Amenabar B
7 Alejandro Gonzalez Inarritu B
8 Alejandro Gonzalez Inarritu B
9 Benicio Del Toro B
10 Alejandro González Iñárritu A
11 Alex Proyas B
12 Alexander Hall A
13 Alfonso Cuaron B
14 Alfred Hitchcock A
15 Anatole Litvak A
16 Andrew Adamson B
17 Marilyn Fox B
18 Andrew Dominik B
19 Andrew Stanton B
20 Andrew Stanton B
21 Lee Unkrich B
22 Angelina Jolie B
23 John Stevenson B
24 Anne Fontaine B
25 Anthony Harvey A
Обратите внимание на использованиеrep
построить новую колонку AB. Вот,sapply
возвращает количество имен в каждой из исходных строк.
vapply
? Есть что-нибудь, что делаетvapply
здесь уместнее?
42-
sapply(s, length)
можно заменить на.lengths(s)
Rich Scriven
но другой обобщенной альтернативой является использованиеcSplit
от моего "splitstackshape» пакет, который имеетdirection
аргумент. Установите это в"long"
чтобы получить указанный вами результат:
library(splitstackshape)
head(cSplit(mydf, "director", ",", direction = "long"))
# director AB
# 1: Aaron Blaise A
# 2: Bob Walker A
# 3: Akira Kurosawa B
# 4: Alan J. Pakula A
# 5: Alan Parker A
# 6: Alejandro Amenabar B
1) два способа с:data.table
library(data.table)
# method 1 (preferred)
setDT(v)[, lapply(.SD, function(x) unlist(tstrsplit(x, ",", fixed=TRUE))), by = AB
][!is.na(director)]
# method 2
setDT(v)[, strsplit(as.character(director), ",", fixed=TRUE), by = .(AB, director)
][,.(director = V1, AB)]
2) а /dplyr
tidyr
сочетание: Кроме того, вы также можете использовать /dplyr
tidyr
сочетание:
library(dplyr)
library(tidyr)
v %>%
mutate(director = strsplit(as.character(director), ",")) %>%
unnest(director)
3) сtidyr
только: Сtidyr 0.5.0
(и позже), вы также можете просто использовать:separate_rows
separate_rows(v, director, sep = ",")
Вы можете использоватьconvert = TRUE
параметр для автоматического преобразования чисел в числовые столбцы.
4) с основанием R:
# if 'director' is a character-column:
stack(setNames(strsplit(df$director,','), df$AB))
# if 'director' is a factor-column:
stack(setNames(strsplit(as.character(df$director),','), df$AB))
data.table(id= "X21", a = "chr1;chr1;chr1", b="123;133;134",c="234;254;268")
становиться?data.table(id = c("X21","X21",X21"), a=c("chr1","chr1","chr1"), b=c("123","133","134"), c=c("234","254","268"))
Reilstein