Разделить вектор на куски в R

Мне нужно разбить вектор на n кусков равного размера в R. Я не мог найти какую-либо базовую функцию для этого. Также Google никуда меня не достал. Итак, вот что я придумал, надеюсь, это кому-то помогает.

x <- 1:10
n <- 3
chunk <- function(x,n) split(x, factor(sort(rank(x)%%n)))
chunk(x,n)
$`0`
[1] 1 2 3

$`1`
[1] 4 5 6 7

$`2`
[1]  8  9 10

Любые комментарии, предложения или улучшения действительно приветствуются и оцениваются.

Cheers, Sebastian

Ответ 1

Однострочное расщепление d на куски размером 20:

split(d, ceiling(seq_along(d)/20))

Подробнее: Я думаю, что все, что вам нужно, это seq_along(), split() и ceiling():

> d <- rpois(73,5)
> d
 [1]  3  1 11  4  1  2  3  2  4 10 10  2  7  4  6  6  2  1  1  2  3  8  3 10  7  4
[27]  3  4  4  1  1  7  2  4  6  0  5  7  4  6  8  4  7 12  4  6  8  4  2  7  6  5
[53]  4  5  4  5  5  8  7  7  7  6  2  4  3  3  8 11  6  6  1  8  4
> max <- 20
> x <- seq_along(d)
> d1 <- split(d, ceiling(x/max))
> d1
$`1`
 [1]  3  1 11  4  1  2  3  2  4 10 10  2  7  4  6  6  2  1  1  2

$`2`
 [1]  3  8  3 10  7  4  3  4  4  1  1  7  2  4  6  0  5  7  4  6

$`3`
 [1]  8  4  7 12  4  6  8  4  2  7  6  5  4  5  4  5  5  8  7  7

$`4`
 [1]  7  6  2  4  3  3  8 11  6  6  1  8  4

Ответ 2

chunk2 <- function(x,n) split(x, cut(seq_along(x), n, labels = FALSE)) 

Ответ 3

Это разделит его по-другому на то, что у вас есть, но по-прежнему довольно неплохая структура списка, я думаю:

chunk.2 <- function(x, n, force.number.of.groups = TRUE, len = length(x), groups = trunc(len/n), overflow = len%%n) { 
  if(force.number.of.groups) {
    f1 <- as.character(sort(rep(1:n, groups)))
    f <- as.character(c(f1, rep(n, overflow)))
  } else {
    f1 <- as.character(sort(rep(1:groups, n)))
    f <- as.character(c(f1, rep("overflow", overflow)))
  }

  g <- split(x, f)

  if(force.number.of.groups) {
    g.names <- names(g)
    g.names.ordered <- as.character(sort(as.numeric(g.names)))
  } else {
    g.names <- names(g[-length(g)])
    g.names.ordered <- as.character(sort(as.numeric(g.names)))
    g.names.ordered <- c(g.names.ordered, "overflow")
  }

  return(g[g.names.ordered])
}

Что даст вам следующее, в зависимости от того, как вы хотите отформатировать его:

> x <- 1:10; n <- 3
> chunk.2(x, n, force.number.of.groups = FALSE)
$`1`
[1] 1 2 3

$`2`
[1] 4 5 6

$`3`
[1] 7 8 9

$overflow
[1] 10

> chunk.2(x, n, force.number.of.groups = TRUE)
$`1`
[1] 1 2 3

$`2`
[1] 4 5 6

$`3`
[1]  7  8  9 10

Запуск нескольких таймингов с использованием этих настроек:

set.seed(42)
x <- rnorm(1:1e7)
n <- 3

Тогда мы имеем следующие результаты:

> system.time(chunk(x, n)) # your function 
   user  system elapsed 
 29.500   0.620  30.125 

> system.time(chunk.2(x, n, force.number.of.groups = TRUE))
   user  system elapsed 
  5.360   0.300   5.663 

EDIT: переход от as.factor() к as.character() в моей функции сделал это в два раза быстрее.

Ответ 4

Попробуйте функцию ggplot2, cut_number:

library(ggplot2)
x <- 1:10
n <- 3
cut_number(x, n) # labels = FALSE if you just want an integer result
#>  [1] [1,4]  [1,4]  [1,4]  [1,4]  (4,7]  (4,7]  (4,7]  (7,10] (7,10] (7,10]
#> Levels: [1,4] (4,7] (7,10]

# if you want it split into a list:
split(x, cut_number(x, n))
#> $`[1,4]`
#> [1] 1 2 3 4
#> 
#> $`(4,7]`
#> [1] 5 6 7
#> 
#> $`(7,10]`
#> [1]  8  9 10

Ответ 5

Несколько вариантов для сваи...

> x <- 1:10
> n <- 3

Обратите внимание, что здесь вам не нужно использовать функцию factor, но вы все равно хотите sort o/w, ваш первый вектор будет 1 2 3 10:

> chunk <- function(x, n) split(x, sort(rank(x) %% n))
> chunk(x,n)
$`0`
[1] 1 2 3
$`1`
[1] 4 5 6 7
$`2`
[1]  8  9 10

Или вы можете назначить символьные индексы, пометить числа в левых тиках выше:

> my.chunk <- function(x, n) split(x, sort(rep(letters[1:n], each=n, len=length(x))))
> my.chunk(x, n)
$a
[1] 1 2 3 4
$b
[1] 5 6 7
$c
[1]  8  9 10

Или вы можете использовать имена простого слова, сохраненные в векторе. Обратите внимание, что использование sort для получения последовательных значений в x приводит в алфавитном порядке метки:

> my.other.chunk <- function(x, n) split(x, sort(rep(c("tom", "dick", "harry"), each=n, len=length(x))))
> my.other.chunk(x, n)
$dick
[1] 1 2 3
$harry
[1] 4 5 6
$tom
[1]  7  8  9 10

Ответ 6

simplified version...
n = 3
split(x, sort(x%%n))

Ответ 7

Вы могли бы объединить split/cut, как было предложено mdsummer, с квантилем для создания четных групп:

split(x,cut(x,quantile(x,(0:n)/n), include.lowest=TRUE, labels=FALSE))

Это дает тот же результат для вашего примера, но не для перекошенных переменных.

Ответ 8

Вот еще один вариант.

ПРИМЕЧАНИЕ. В этом примере вы указываете РАЗМЕР CHUNK во втором параметре

  • все куски являются однородными, за исключением последних;
  • последняя в худшем случае будет меньше, не больше размера куска.

chunk <- function(x,n)
{
    f <- sort(rep(1:(trunc(length(x)/n)+1),n))[1:length(x)]
    return(split(x,f))
}

#Test
n<-c(1,2,3,4,5,6,7,8,9,10,11)

c<-chunk(n,5)

q<-lapply(c, function(r) cat(r,sep=",",collapse="|") )
#output
1,2,3,4,5,|6,7,8,9,10,|11,|

Ответ 9

split(x,matrix(1:n,n,length(x))[1:length(x)])

возможно, это более ясно, но та же идея:
split(x,rep(1:n, ceiling(length(x)/n),length.out = length(x)))

если вы хотите его заказать, бросьте вокруг него

Ответ 10

Мне нужна была одна и та же функция, и я прочитал предыдущие решения, однако мне также нужно было иметь неуравновешенный кусок в конце, т.е. если у меня есть 10 элементов, чтобы разделить их на векторы по 3 каждый, то мой результат должен иметь векторы с 3,3,4 элементами соответственно. Поэтому я использовал следующее (я оставил код неоптимизированным для удобочитаемости, в противном случае не нужно иметь много переменных):

chunk <- function(x,n){
  numOfVectors <- floor(length(x)/n)
  elementsPerVector <- c(rep(n,numOfVectors-1),n+length(x) %% n)
  elemDistPerVector <- rep(1:numOfVectors,elementsPerVector)
  split(x,factor(elemDistPerVector))
}
set.seed(1)
x <- rnorm(10)
n <- 3
chunk(x,n)
$`1`
[1] -0.6264538  0.1836433 -0.8356286

$`2`
[1]  1.5952808  0.3295078 -0.8204684

$`3`
[1]  0.4874291  0.7383247  0.5757814 -0.3053884

Ответ 11

Кредит @Sebastian для этой функции

chunk <- function(x,y){
         split(x, factor(sort(rank(row.names(x))%%y)))
         }

Ответ 12

Если вам не нравится split(), и вы не возражаете против того, чтобы NAs отбрасывали ваш короткий хвост:

chunk <- function(x, n) { if((length(x)%%n)==0) {return(matrix(x, nrow=n))} else {return(matrix(append(x, rep(NA, n-(length(x)%%n))), nrow=n))} }

Столами возвращаемой матрицы ([, 1: ncol]) являются дроиды, которые вы ищете.

Ответ 13

Если вам не нравится split() и, вам не нравится matrix() (с его оборванными NA), вот это:

chunk <- function(x, n) (mapply(function(a, b) (x[a:b]), seq.int(from=1, to=length(x), by=n), pmin(seq.int(from=1, to=length(x), by=n)+(n-1), length(x)), SIMPLIFY=FALSE))

Как и split(), он возвращает список, но он не тратит время или пространство на ярлыки, поэтому он может быть более эффективным.

Ответ 14

Мне нужна функция, которая принимает аргумент data.table(в кавычках) и другой аргумент, который является верхним пределом количества строк в подмножествах этой исходной таблицы данных. Эта функция производит любое количество данных. Таблицы, которые верхний предел позволяет:

library(data.table)    
split_dt <- function(x,y) 
    {
    for(i in seq(from=1,to=nrow(get(x)),by=y)) 
        {df_ <<- get(x)[i:(i + y)];
            assign(paste0("df_",i),df_,inherits=TRUE)}
    rm(df_,inherits=TRUE)
    }

Эта функция дает мне ряд data.tables с именем df_ [число] с начальной строкой из исходной таблицы данных в имени. Последняя таблица данных может быть короткой и заполнена NA, поэтому вам нужно подмножить это обратно, чтобы все данные остались. Этот тип функции полезен, поскольку определенное программное обеспечение ГИС имеет ограничения на количество адресов контактов, которые вы можете импортировать, например. Таким образом, нарезка данных. Таблицы на более мелкие куски не рекомендуется, но их нельзя избежать.