将向量拆分为R中的块


227

我必须在R中将向量拆分为n个大小相等的块。我找不到任何基本函数来执行此操作。谷歌也没有让我到任何地方。所以这就是我想出的,希望它能对某人有所帮助。

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

任何意见,建议或改进都将受到欢迎和赞赏。

塞巴斯蒂安欢呼


5
是的,目前还不清楚您得到的是“ n个大小相等的块”的解决方案。但这也许也能使您到达那里:x <-1:10; n <-3; split(x,cut(x,n,标签= FALSE))
mdsumner

问题中的解决方案和前面的注释中的解决方案都是错误的,因为如果引导程序有重复的条目,则它们可能不起作用。试试这个:> foo <-c(rep(1,12),rep(2,3),rep(3,3))[1] 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 3 3 3> chunk(foo,2)(给出错误的结果)> chunk(foo,3)(同样是错误的)
mathheadinclouds 2013年

(继续前面的评论)为什么?rank(x)不必是整数> rank(c(1,1,2,3))[1] 1.5 1.5 3.0 4.0,所以这就是问题中的方法失败的原因。这个工作(感谢下面的Harlan)> chunk2 <-function(x,n)split(x,cut(seq_along(x),n,labels = FALSE))
mathheadinclouds 2013年

2
> split(foo,cut(foo,3,labels = FALSE))(也是错误的)
mathheadinclouds 2013年

1
正如@mathheadinclouds所建议的,示例数据是一个非常特殊的情况。更通用的示例将更有用,并且测试效果更好。例如,x <- c(NA, 4, 3, NA, NA, 2, 1, 1, NA ); y <- letters[x]; z <- factor(y)给出了一些示例,这些示例包含缺少的数据,重复的值(尚未排序),并且位于不同的类(整数,字符,因子)中。
卡林

Answers:


313

单线将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

34
这个问题要求n块大小相等。这将使您获得未知数量的大小块n。我遇到了同样的问题,并使用了@mathheadinclouds的解决方案。
2014年

4
从d1的输出中可以看出,此答案并未将d分成大小相等的组(显然短4个)。因此,它无法回答问题。
Calimo

9
@rrs:split(d,ceiling(seq_along(d)/(length(d)/ n)))
gkcn 2015年

我知道这已经很老了,但对那些在这里绊倒的人可能会有帮助。尽管OP的问题是将其分成相等大小的块,但是如果向量碰巧不是除数的倍数,则最后一个小切口的大小将与块不同。拆分成n-chunks我用过的max <- length(d)%/%n。我将其与31个字符串的向量一起使用,并获得了10个句子和1个句子之一的3个向量的列表。
salvu


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

我喜欢它,因为它为您提供了尽可能相等大小的块(适合于分割大型任务,例如容纳有限的RAM或跨多个线程运行任务)。
alexvpickering

3
这很有用,但是请记住,这仅适用于数值向量。
基思·休吉特

@KeithHughitt这可以通过因子来解决,并将级别返回为数字。或者至少这是我实现它的方式。
drmariod

20

尝试使用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

2
了这并不适用于拆分工作xyz在定义此评论。特别是,它根据结果对结果进行排序,这可能会或可能不会。
卡林

而是此评论
卡林

18

这会将其拆分为您所拥有的内容,但是我认为仍然是一个不错的列表结构:

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()使其速度提高了一倍。


13

堆的更多变体...

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

请注意,您无需在factor此处使用该函数,但您仍想将sort第一个向量作为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

12

使用基数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

如前所述,如果要排序索引,只需:

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

9

您可以将mdsummer建议的分割/剪切与分位数相结合以创建偶数组:

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

这将为您的示例提供相同的结果,但不会为偏斜的变量提供相同的结果。


7

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

也许这更清楚,但是相同的想法:
split(x,rep(1:n, ceiling(length(x)/n),length.out = length(x)))

如果您要订购,请对其进行排序


6

我需要相同的功能,并且已经阅读了先前的解决方案,但是我还需要使不平衡块位于末尾,即,如果我有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

6

这是另一个变种。

注意:在此示例中,您要在第二个参数中指定“ CHUNK SIZE”

  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,|

4

简单的函数,只需使用索引即可分割向量-无需过于复杂

vsplit <- function(v, n) {
    l = length(v)
    r = l/n
    return(lapply(1:n, function(i) {
        s = max(1, round(r*(i-1))+1)
        e = min(l, round(r*i))
        return(v[s:e])
    }))
}

3

如果您不喜欢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(),它返回一个列表,但是它不会浪费时间或空间来放置标签,因此它的性能可能更高。


2

感谢此功能的 @Sebastian

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

2

如果您不喜欢split()并且不介意NA填补您的短尾巴:

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])是您要查找的机器人。


2

我需要一个函数,该函数接受data.table的参数(用引号引起来),另一个参数是该原始data.table的子集中的行数上限。此函数产生上限允许的任意数量的data.tables:

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)
    }

此函数为我提供了一系列名为df_ [number]的data.tables,其名称中的原始data.table为起始行。最后一个data.table可能很短,并且充满了NA,因此您必须将其子集化为剩下的任何数据。这种功能很有用,因为例如某些GIS软件对可以导入的地址针脚有限制。因此,不建议将data.tables切成较小的块,但这可能是不可避免的。


2

抱歉,这个答案来得太晚了,但也许对其他人有用。实际上,有一个非常有用的解决方案,在?split的结尾进行了解释。

> testVector <- c(1:10) #I want to divide it into 5 parts
> VectorList <- split(testVector, 1:5)
> VectorList
$`1`
[1] 1 6

$`2`
[1] 2 7

$`3`
[1] 3 8

$`4`
[1] 4 9

$`5`
[1]  5 10

3
如果每个组中的值数量不相等,这将中断!
Matifou

2

还有一种可能是splitIndicespackage 的功能parallel

library(parallel)
splitIndices(20, 3)

给出:

[[1]]
[1] 1 2 3 4 5 6 7

[[2]]
[1]  8  9 10 11 12 13

[[3]]
[1] 14 15 16 17 18 19 20

0

哇,这个问题比预期的要引人注目。

感谢您的所有想法。我想出了这个解决方案:

require(magrittr)
create.chunks <- function(x, elements.per.chunk){
    # plain R version
    # split(x, rep(seq_along(x), each = elements.per.chunk)[seq_along(x)])
    # magrittr version - because that's what people use now
    x %>% seq_along %>% rep(., each = elements.per.chunk) %>% extract(seq_along(x)) %>% split(x, .) 
}
create.chunks(letters[1:10], 3)
$`1`
[1] "a" "b" "c"

$`2`
[1] "d" "e" "f"

$`3`
[1] "g" "h" "i"

$`4`
[1] "j"

关键是使用seq(each = chunk.size)参数,以使其起作用。在我之前的解决方案中,使用seq_along的行为与rank(x)相似,但实际上能够使用重复的条目产生正确的结果。


对于那些担心rep(seq_along(x),each = elements.per.chunk)的人来说,可能在内存上的压力太大:是的。您可以尝试修改以前的建议:块<-函数(x,n)split(x,factor(seq_along(x)%% n))
塞巴斯蒂安

0

这将拆分为大小为⌊n/k⌋+ 1或⌊n/k⌋的块,并且不使用O(n log n)排序。

get_chunk_id<-function(n, k){
    r <- n %% k
    s <- n %/% k
    i<-seq_len(n)
    1 + ifelse (i <= r * (s+1), (i-1) %/% (s+1), r + ((i - r * (s+1)-1) %/% s))
}

split(1:10, get_chunk_id(10,3))
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.