Разбить вектор на куски в R - PullRequest
193 голосов
/ 23 июля 2010

Мне нужно разделить вектор на 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, Себастьян

Ответы [ 19 ]

277 голосов
/ 23 июля 2010

Однострочное разбиение 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
60 голосов
/ 29 апреля 2013
chunk2 <- function(x,n) split(x, cut(seq_along(x), n, labels = FALSE)) 
29 голосов
/ 21 апреля 2016
simplified version...
n = 3
split(x, sort(x%%n))
18 голосов
/ 23 июля 2010

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

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 

РЕДАКТИРОВАТЬ: переход от as.factor () к as.character () в моей функции сделал это в два раза быстрее.

17 голосов
/ 09 января 2015

Попробуйте функцию 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
12 голосов
/ 23 июля 2010

Еще несколько вариантов к куче ...

> 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
9 голосов
/ 23 июля 2010

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

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

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

7 голосов
/ 23 июля 2010

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

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

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

6 голосов
/ 14 сентября 2013

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

ПРИМЕЧАНИЕ: в этом примере вы указываете РАЗМЕР КУНКА во втором параметре

  1. все куски одинаковы, кроме последнего;
  2. последний в худшем случае будет меньше, но не больше размера куска.

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,|
6 голосов
/ 01 ноября 2018

Использование базовых R rep_len:

x <- 1:10
n <- 3

split(x, rep_len(1:n, length(x)))
# $`1`
# [1]  1  4  7 10
# 
# $`2`
# [1] 2 5 8
# 
# $`3`
# [1] 3 6 9

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...