Используя некоторые примеры данных:
dat <- read.table(stringsAsFactors=FALSE, header=TRUE, text="
G1S1 G1S2 G1S3 G2S1 G2S2 G2S3 G3S1 G3S2 G3S3
111 121 131 211 221 231 311 321 331
112 122 132 212 222 232 312 322 332
113 123 133 213 223 233 313 323 333")
Желая разделить только по второму символу каждого имени, давайте сначала узнаем, как отделить это от остальных:
gsub("S.*", "", names(dat))
# [1] "G1" "G1" "G1" "G2" "G2" "G2" "G3" "G3" "G3"
Теперь мы хотим выделить столбцы в эти три группы.Одним из способов является split
их.Если мы используем split
сам по себе, он не будет работать:
split(dat, gsub("S.*", "", names(dat)))
# Warning in split.default(x = seq_len(nrow(x)), f = f, drop = drop, ...) :
# data length is not a multiple of split variable
# $G1
# G1S1 G1S2 G1S3 G2S1 G2S2 G2S3 G3S1 G3S2 G3S3
# 1 111 121 131 211 221 231 311 321 331
# 2 112 122 132 212 222 232 312 322 332
# 3 113 123 133 213 223 233 313 323 333
# $G2
# [1] G1S1 G1S2 G1S3 G2S1 G2S2 G2S3 G3S1 G3S2 G3S3
# <0 rows> (or 0-length row.names)
# $G3
# [1] G1S1 G1S2 G1S3 G2S1 G2S2 G2S3 G3S1 G3S2 G3S3
# <0 rows> (or 0-length row.names)
Это потому, что split
видит, что он работает на data.frame
и пытается делать что-то построчное, чтоэто не то, что мы хотим.Если вы ищите, есть ряд split
S3 методов (функций, которые имеют конкретные версии на основе первого аргумента):
methods("split")
# [1] split.data.frame split.Date split.default split.POSIXct
# see '?methods' for accessing help and source code
Версия, которую мы молча использовали, это split.data.frame
, потому чтоdat
это рамка.Мы можем переопределить это:
lodf <- split.default(dat, gsub("S.*", "", names(dat)))
lodf
# $G1
# G1S1 G1S2 G1S3
# 1 111 121 131
# 2 112 122 132
# 3 113 123 133
# $G2
# G2S1 G2S2 G2S3
# 1 211 221 231
# 2 212 222 232
# 3 213 223 233
# $G3
# G3S1 G3S2 G3S3
# 1 311 321 331
# 2 312 322 332
# 3 313 323 333
Отсюда: я лично рекомендую хранить их в этой структуре list
-фреймов (эрго мое временное имя lodf
).Это сделано при условии, что что-то, что вы делаете с одним, вы будете делать (то же самое) с другими, и в этом случае lapply
является естественным выбором для операций.
Если вам действительно нужно прерватьэто вниз (опять же, обескураженный), вы можете назначить их как имена групп для вызывающей среды с помощью:
for (nm in names(lodf)) assign(nm, lodf[[nm]])
ls()
# [1] "dat" "G1" "G2" "G3" "lodf"
Ответ, связанный с фреймами в списках: Как мне сделатьсписок фреймов данных?
Вы могли бы сделать еще один шаг, возможно, справиться с этим в «аккуратном» (т. е. «длинном») формате: включить группу в сами фреймы:
do.call(rbind.data.frame, c(mapply(function(nm, x) {
# remove the 'G#' from each column name, allows row-binding later
names(x) <- gsub(paste0("^", nm), "", names(x))
# add the group name as a new column
transform(x, Grp = nm)
}, names(lodf), lodf, SIMPLIFY = FALSE), list(stringsAsFactors = FALSE)))
# S1 S2 S3 Grp
# G1.1 111 121 131 G1
# G1.2 112 122 132 G1
# G1.3 113 123 133 G1
# G2.1 211 221 231 G2
# G2.2 212 222 232 G2
# G2.3 213 223 233 G2
# G3.1 311 321 331 G3
# G3.2 312 322 332 G3
# G3.3 313 323 333 G3
(мне не нравятся имена строк G1.1
и т. Д., Но они безвредны.) Объяснение:
mapply
похоже на lapply
за исключением того, что для каждого вызова функции требуется 1 или более аргументов, что фактически позволяет "сжать" список аргументов вместе.Аргументами для этого являются names(lodf)
и lodf
, поэтому первый вызов (вызов anonfunc
анонимной функции, принимающей в качестве аргументов (nm, x)
) выглядит как anonfunc(names(lodf)[[1]], lodf[[1]])
, второй anonfunc(names(lodf)[[2]], lodf[[2]])
и т. Д. - внутри этой анонимной функции я сначала удаляю
G#
из имен столбцов.Это позволяет нам связывать их позже как S1
, S2
и т. Д. - Я добавляю имя группы в кадры как
Grp
.
Этот форматпозволяет сделать одну вещь один раз , применяя группировку по переменной Grp
по мере необходимости.Например, если вы используете dplyr
или data.table
, это довольно просто сделать ... %>% dplyr::group_by(Grp) %>% ...
или DT[, .(...), by="Grp"]
, соответственно.