把一个向量分成若干块

我必须在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
215510 次浏览

你可以像mdsummer建议的那样,结合split/cut和quantile来创建偶数组:

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

这为您的示例提供了相同的结果,但不适用于倾斜变量。

还有更多的变种…

> 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

或者您可以使用存储在vector中的纯字名称。注意,使用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

这将以不同的方式划分它,但我认为这仍然是一个相当不错的列表结构:

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()使我的函数快了两倍。

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

也许这更清楚,但意思是一样的:
split(x,rep(1:n, ceiling(length(x)/n),length.out = length(x))) < br > < / p >

如果你想对它排序,就对它进行排序

将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
chunk2 <- function(x,n) split(x, cut(seq_along(x), n, labels = FALSE))

我需要相同的函数,并且已经阅读了以前的解决方案,但是我还需要在最后有不平衡的块,即如果我有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

这是另一种变体。

注意:在这个示例中,您在第二个参数中指定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,|

这个函数要归功于@Sebastian

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

如果你不喜欢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])的列是您正在寻找的机器人。

如果你不喜欢split() 而且你不喜欢matrix()(它的悬垂NAs),有这样的情况:

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()一样,它返回一个列表,但它不会在标签上浪费时间或空间,因此它可能更性能。

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

简化版:

n = 3
split(x, sort(x%%n))

注意:这只适用于数值向量。

我需要一个接受数据参数的函数。Table(引号中)和另一个参数,该参数是原始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)
}

这个函数给出了一系列数据。命名为df_[number]的表,其起始行来自原始数据。表中的名称。最后一个数据。表可以很短,并且填满了NAs,所以你必须将其子集返回到任何剩下的数据。这种类型的函数很有用,因为某些GIS软件对您可以导入的地址引脚数量有限制。切片数据。不建议将表分成更小的块,但这可能是不可避免的。

简单的函数通过简单地使用索引来分割一个向量-不需要过于复杂

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

如果这个答案来得这么晚,我很抱歉,但也许它对其他人有用。实际上,这个问题有一个非常有用的解决方案,在?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

还有一种可能是包parallel中的splitIndices函数:

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

注意:这只适用于数值。如果你想拆分一个字符向量,你需要做一些索引: lapply(splitIndices(20, 3), \(x) letters[1:20][x])

我想出了这个解决方案:

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),但实际上能够使用重复的条目产生正确的结果。

使用base 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

它分成大小为⌊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))

这里还有另一个,允许你控制你想要的结果是否有序:

split_to_chunks <- function(x, n, keep.order=TRUE){
if(keep.order){
return(split(x, sort(rep(1:n, length.out = length(x)))))
}else{
return(split(x, rep(1:n, length.out = length(x))))
}
}


split_to_chunks(x = 1:11, n = 3)
$`1`
[1] 1 2 3 4


$`2`
[1] 5 6 7 8


$`3`
[1]  9 10 11


split_to_chunks(x = 1:11, n = 3, keep.order=FALSE)


$`1`
[1]  1  4  7 10


$`2`
[1]  2  5  8 11


$`3`
[1] 3 6 9

不确定这是否回答了OP的问题,但我认为%%在这里是有用的

df # some data.frame
N_CHUNKS <- 10
I_VEC <- 1:nrow(df)
df_split <- split(df, sort(I_VEC %% N_CHUNKS))