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, labels = FALSE))
mdsumner

벡터에 항목이 반복적으로 입력되면 문제의 솔루션과 앞의 주석의 솔루션이 모두 작동하지 않을 수 있습니다. 다음을 시도해보십시오 :> foo <-c (rep (1, 12), rep (2,3), rep (3,3)) 3> 청크 (foo, 2) (잘못된 결과 제공)> chunk (foo, 3) (또한 잘못된)
mathheadinclouds

(이전 의견 계속) 왜? 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

2
> split (foo, cut (foo, 3, labels = FALSE)) (또한 잘못됨)
mathheadinclouds

1
@mathheadinclouds에서 알 수 있듯이 예제 데이터는 매우 특별한 경우입니다. 보다 일반적인 예제는 더 유용하고 더 나은 테스트입니다. 예 x <- c(NA, 4, 3, NA, NA, 2, 1, 1, NA ); y <- letters[x]; z <- factor(y)를 들어 누락 된 데이터, 반복되는 값, 아직 정렬되지 않았으며 다른 클래스 (정수, 문자, 요소)에있는 예제를 제공합니다.
Kalin

답변:


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의 솔루션을 사용했습니다.
rrs

4
d1의 출력에서 ​​볼 수 있듯이이 대답은 d를 동일한 크기의 그룹으로 나누지 않습니다 (4는 분명히 더 짧습니다). 따라서 질문에 대답하지 않습니다.
Calimo

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

나는 이것이 꽤 오래되었다는 것을 알고 있지만 여기에서 넘어지는 사람들에게는 도움이 될 수 있습니다. OP의 질문은 동일한 크기의 청크로 분할하는 것이었지만 벡터가 제수의 배수가 아닌 경우 마지막 척은 청크와 크기가 다릅니다. 로 나누기 위해 n-chunks사용했습니다 max <- length(d)%/%n. 이것을 31 문자열의 벡터와 함께 사용하여 10 문장으로 구성된 3 개의 벡터와 1 문장 중 하나의 목록을 얻었습니다.
salvu

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

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

가능한 한 동일한 크기의 청크를 제공하기 때문에 이것을 좋아합니다 (예 : 제한된 RAM을 수용하거나 여러 스레드에서 작업을 실행하기 위해 큰 작업을 나누는 데 적합합니다).
alexvpickering

3
이 방법은 유용하지만 숫자 형 벡터에서만 작동합니다.
Keith Hughitt

@ 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
가까지이 분할 작동하지 않습니다 x, y또는 z정의 이 댓글 . 특히 응용 프로그램에 따라 결과가 정렬 될 수 있습니다.
Kalin

오히려이 의견 입니다.
Kalin

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 / cut을 Quantile과 결합하여 짝수 그룹을 만들 수 있습니다.

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

다른 변형이 있습니다.

참고 :이 샘플에서는 두 번째 매개 변수에 척 크기를 지정합니다

  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()(그 매달려 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()목록을 반환하지만 레이블이있는 시간이나 공간을 낭비하지 않으므로 성능이 향상 될 수 있습니다.



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

이 함수는 이름의 원래 data.table에서 시작 행이있는 df_ [number]라는 일련의 data.tables를 제공합니다. 마지막 data.table은 짧고 NA로 채워질 수 있으므로 남아있는 데이터로 하위 집합을 다시 설정해야합니다. 이 유형의 기능은 특정 GIS 소프트웨어가 가져올 수있는 주소 핀 수에 제한이 있기 때문에 유용합니다. 따라서 data.tables를 작은 청크로 분할하는 것은 권장되지 않지만 피할 수는 없습니다.


2

이 답변이 너무 늦다면 미안하지만 다른 사람에게 유용 할 수 있습니다. 실제로이 문제에는 매우 유용한 해결책이 있습니다.

> 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

또 다른 가능성은 splitIndices패키지 의 기능입니다 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)가 메모리에 너무 긴장할 수 있다는 우려가있는 사람들에게는 그렇습니다. 내 이전 제안의 수정 된 버전을 시도 할 수 있습니다 : chunk <-function (x, n) split (x, factor (seq_along (x) %% n))
Sebastian

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))
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.