Rでベクトルをチャンクに分割する


227

ベクトルをRで同じサイズのn個のチャンクに分割する必要があります。これを行うための基本関数が見つかりませんでした。また、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

コメント、提案、改善は大歓迎です。

乾杯、セバスチャン


5
はい、あなたが得るものが「等しいサイズのn個のチャンク」に対する解決策であるかどうかは非常に不明確です。しかし、たぶんこれであなたもそこに行きます:x <-1:10; n <-3; split(x、cut(x、n、labels = FALSE))
mdsumner 2010

質問の解決策と前のコメントの解決策はどちらも正しくありません。ベクトルにエントリが繰り返されている場合、それらは機能しない可能性があります。これを試してください:> 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)、まだソートされておらず、異なるクラス(整数、文字、因子)にある、欠落データ、反復値の例を示します。
カリン

回答:


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、2015年

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

私はこれがかなり古いことを知っていますが、ここでつまずく人には役立つかもしれません。OPの問題は同じサイズのチャンクに分割することでしたが、ベクターが除数の倍数にならない場合、最後のチャンクはチャンクとは異なるサイズになります。に分割するためn-chunksに使用しましたmax <- length(d)%/%n。これを31文字列のベクトルで使用し、10文の3つのベクトルと1文の1つのベクトルのリストを取得しました。
salvu


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

可能な限り同じサイズのチャンクを提供するので、これが好きです(たとえば、限られたRAMに対応するため、または複数のスレッド間でタスクを実行するために、大きなタスクを分割するのに適しています)。
alexvpickering

3
これは便利ですが、これは数値ベクトルでのみ機能することに注意してください。
キース・ヒュージット

@KeithHughittこれは因子で解決でき、レベルを数値として返します。または、少なくともこれが私が実装した方法です。
drmariod 2018

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
これは、分割アップのために動作しないxyまたはzで定義されたこのコメントを。特に、アプリケーションに応じて、結果をソートします。
カリン

むしろ、このコメント
カリン

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()に変更すると、2倍速くなりました。


13

パイルのいくつかのバリエーション...

> 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

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

ここに別のバリアントがあります。

注:このサンプルでは、​​2番目のパラメーターで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

(ダングリングNAで)気に入らない場合はsplit() 次のようになりますmatrix()

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

この回答が非常に遅くなった場合は申し訳ありませんが、おそらく他の人にとっては役に立つかもしれません。?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 2018

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)がメモリに負担をかけすぎている可能性があることを懸念している場合は、そうです。私の以前の提案の修正バージョンを試すことができます:チャンク<-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.