Tách một vectơ thành các khối trong R


227

Tôi phải chia một vectơ thành n khối có kích thước bằng nhau trong R. Tôi không thể tìm thấy bất kỳ hàm cơ sở nào để làm điều đó. Google cũng không đưa tôi đến đâu cả. Vì vậy, đây là những gì tôi nghĩ ra, hy vọng nó sẽ giúp được ai đó.

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

Bất kỳ ý kiến, đề xuất hoặc cải tiến là thực sự hoan nghênh và đánh giá cao.

Chúc mừng, Sebastian


5
Vâng, rất không rõ ràng rằng những gì bạn nhận được là giải pháp cho "n khối có kích thước bằng nhau". Nhưng có lẽ điều này cũng đưa bạn đến đó: x <- 1:10; n <- 3; chia (x, cắt (x, n, nhãn = FALSE))
mdsumner

cả giải pháp trong câu hỏi và giải pháp trong nhận xét trước đều không chính xác, ở chỗ chúng có thể không hoạt động, nếu vectơ có các mục lặp lại. Hãy thử điều này:> 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) (cho kết quả sai)> chunk (foo, 3) (cũng sai)
mathheadinclouds

(tiếp tục bình luận trước) tại sao? thứ hạng (x) không cần phải là số nguyên> thứ hạng (c (1,1,2,3)) [1] 1.5 1.5 3.0 4.0 vì vậy đó là lý do tại sao phương thức trong câu hỏi không thành công. cái này hoạt động (nhờ Harlan bên dưới)> chunk2 <- function (x, n) split (x, cut (seq_along (x), n, nhãn = FALSE))
mathheadinclouds

2
> split (foo, cut (foo, 3, nhãn = FALSE)) (cũng sai)
mathheadinclouds

1
Như @mathheadinclouds gợi ý, dữ liệu ví dụ là một trường hợp rất đặc biệt. Các ví dụ tổng quát hơn sẽ hữu ích hơn và kiểm tra tốt hơn. Ví dụ, x <- c(NA, 4, 3, NA, NA, 2, 1, 1, NA ); y <- letters[x]; z <- factor(y)đưa ra các ví dụ với dữ liệu bị thiếu, các giá trị lặp lại, chưa được sắp xếp và nằm trong các lớp khác nhau (số nguyên, ký tự, hệ số).
Kalin

Câu trả lời:


313

Một lớp lót tách d thành các khối có kích thước 20:

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

Thêm chi tiết: Tôi nghĩ tất cả những gì bạn cần là 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
Câu hỏi yêu cầu cho ncác khối có kích thước bằng nhau. Điều này giúp bạn có một số lượng không xác định của kích thước n. Tôi đã có cùng một vấn đề và sử dụng các giải pháp từ @mathheadinclouds.
rrs

4
Như người ta có thể thấy từ đầu ra của d1, câu trả lời này không chia d thành các nhóm có kích thước bằng nhau (4 rõ ràng là ngắn hơn). Do đó, nó không trả lời câu hỏi.
Calimo

9
@rrs: split (d, trần (seq_along (d) / (length (d) / n)))
gkcn

Tôi biết điều này khá cũ nhưng nó có thể giúp ích cho những người vấp ngã ở đây. Mặc dù câu hỏi của OP là chia thành các phần có kích thước bằng nhau, nhưng nếu vectơ xảy ra không phải là bội số của phép chia, thì chink cuối cùng sẽ có kích thước khác với chunk. Để chia thành n-chunkstôi sử dụng max <- length(d)%/%n. Tôi đã sử dụng điều này với một vectơ 31 chuỗi và thu được danh sách 3 vectơ gồm 10 câu và một trong 1 câu.
salvu


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

Tôi thích điều này vì nó cung cấp cho bạn các khối có kích thước bằng nhau nhất có thể (tốt cho việc phân chia nhiệm vụ lớn, ví dụ như để chứa RAM hạn chế hoặc để chạy một tác vụ trên nhiều luồng).
alexvpickering

3
Điều này rất hữu ích, nhưng hãy nhớ rằng điều này sẽ chỉ hoạt động trên các vectơ số.
Keith Hughitt

@KeithHughitt điều này có thể được giải quyết bằng các yếu tố và trả về các mức dưới dạng số. Hoặc ít nhất đây là cách tôi thực hiện nó.
drmariod

20

Hãy thử chức năng 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
Điều này không làm việc cho tách lên x, yhoặc zđược định nghĩa trong nhận xét này . Cụ thể, nó sắp xếp các kết quả, có thể có hoặc không ổn, tùy thuộc vào ứng dụng.
Kalin

Thay vào đó, bình luận này .
Kalin

18

Điều này sẽ phân chia nó khác với những gì bạn có, nhưng tôi vẫn nghĩ là một cấu trúc danh sách khá hay:

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

Điều này sẽ cung cấp cho bạn những điều sau đây, tùy thuộc vào cách bạn muốn nó được định dạng:

> 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

Chạy một vài thời gian bằng các cài đặt này:

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

Sau đó, chúng tôi có kết quả sau đây:

> 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: Việc thay đổi từ as.factor () thành as.character () trong chức năng của tôi khiến nó nhanh gấp đôi.


13

Một vài biến thể nữa cho đống ...

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

Lưu ý rằng bạn không cần sử dụng factorhàm ở đây, nhưng bạn vẫn muốn sorto / w véc tơ đầu tiên của bạn sẽ là 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

Hoặc bạn có thể gán các chỉ số ký tự, ngược lại các số trong dấu tích bên trái ở trên:

> 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

Hoặc bạn có thể sử dụng tên từ được lưu trữ trong một vectơ. Lưu ý rằng sử dụng sortđể có được các giá trị liên tiếp trong xbảng chữ cái nhãn:

> 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

Sử dụng cơ sở 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

Và như đã đề cập nếu bạn muốn các chỉ số được sắp xếp, chỉ cần:

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

Bạn có thể kết hợp phân tách / cắt, theo đề xuất của mdsummer, với định lượng để tạo các nhóm chẵn:

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

Điều này cho kết quả tương tự cho ví dụ của bạn, nhưng không cho các biến bị lệch.


7

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

có lẽ điều này rõ ràng hơn, nhưng cùng một ý tưởng:
split(x,rep(1:n, ceiling(length(x)/n),length.out = length(x)))

nếu bạn muốn nó ra lệnh, ném một loại xung quanh nó


6

Tôi cần cùng chức năng và đã đọc các giải pháp trước đó, tuy nhiên tôi cũng cần phải có đoạn không cân bằng ở cuối, tức là nếu tôi có 10 phần tử để chia chúng thành 3 vectơ, thì kết quả của tôi sẽ có vectơ với 3, 3,4 yếu tố tương ứng. Vì vậy, tôi đã sử dụng như sau (tôi để lại mã không được tối ưu hóa cho khả năng đọc, nếu không thì không cần phải có nhiều biến):

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

Đây là một biến thể khác.

LƯU Ý: với mẫu này, bạn chỉ định CHUNK SIZE trong tham số thứ hai

  1. tất cả các khối là thống nhất, ngoại trừ cuối cùng;
  2. ý chí cuối cùng tồi tệ nhất là nhỏ hơn, không bao giờ lớn hơn kích thước 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,|

4

Hàm đơn giản để phân tách một vectơ bằng cách sử dụng các chỉ mục - không cần quá phức tạp hóa điều này

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

Nếu bạn không thích split() bạn không thích matrix()(với các NA nguy hiểm của nó), thì có:

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

Giống như split(), nó trả về một danh sách, nhưng nó không lãng phí thời gian hoặc không gian với nhãn, vì vậy nó có thể hiệu quả hơn.



2

Nếu bạn không thích split()và bạn không phiền NA sẽ bỏ cái đuôi ngắn của bạn:

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

Các cột của ma trận trả về ([, 1: ncol]) là các droid bạn đang tìm kiếm.


2

Tôi cần một hàm lấy đối số của data.table (trong ngoặc kép) và một đối số khác là giới hạn trên của số lượng hàng trong các tập hợp con của data.table gốc đó. Hàm này tạo ra bất kỳ số lượng dữ liệu nào. Giới hạn trên cho phép:

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

Hàm này cung cấp cho tôi một loạt data.tables có tên df_ [number] với hàng bắt đầu từ data.table gốc trong tên. Dữ liệu cuối cùng có thể ngắn và chứa đầy NA, do đó bạn phải đặt lại dữ liệu đó cho bất kỳ dữ liệu nào còn lại. Loại chức năng này rất hữu ích vì một số phần mềm GIS nhất định có giới hạn về số lượng chân địa chỉ bạn có thể nhập, chẳng hạn. Vì vậy, việc cắt dữ liệu. Các phần dữ liệu thành các phần nhỏ hơn có thể không được khuyến nghị, nhưng có thể không tránh được.


2

Xin lỗi nếu câu trả lời này đến quá muộn, nhưng có lẽ nó có thể hữu ích cho người khác. Trên thực tế, có một giải pháp rất hữu ích cho vấn đề này, được giải thích vào cuối?

> 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
điều này sẽ phá vỡ nếu có số lượng giá trị không đồng đều trong mỗi nhóm!
Matifou

2

Một khả năng khác là splitIndiceschức năng từ gói parallel:

library(parallel)
splitIndices(20, 3)

Cung cấp:

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

Wow, câu hỏi này có nhiều lực kéo hơn dự kiến.

Cảm ơn về các ý kiến. Tôi đã đưa ra giải pháp này:

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"

Điều quan trọng là sử dụng tham số seq (Each = chunk.size) để làm cho nó hoạt động. Sử dụng seq_along hoạt động như thứ hạng (x) trong giải pháp trước đây của tôi, nhưng thực sự có thể tạo ra kết quả chính xác với các mục trùng lặp.


Đối với những người quan tâm rằng rep (seq_along (x), mỗi = yếu tố.per.chunk) có thể quá căng thẳng trên bộ nhớ: đúng vậy. Bạn có thể thử phiên bản sửa đổi của đề xuất trước đây của tôi: chunk <- function (x, n) split (x, Fact (seq_along (x) %% n))
Sebastian

0

Điều này phân chia thành các khối có kích thước ⌊n / k⌋ + 1 hoặc ⌊n / k⌋ và không sử dụng sắp xếp 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))
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.