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
コメント、提案、改善は大歓迎です。
乾杯、セバスチャン
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))
simplified version...
n = 3
split(x, sort(x%%n))
これはあなたが持っているものとは異なる方法で分割しますが、それでも私は思うに素晴らしいリスト構造です:
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倍速くなりました。
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
パイルのいくつかのバリエーション...
> 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
split(x,matrix(1:n,n,length(x))[1:length(x)])
おそらくこれはより明確ですが、同じ考え:split(x,rep(1:n, ceiling(length(x)/n),length.out = length(x)))
注文したい場合は、並べ替えてください
Mdsummerで提案されている分割/カットと変位値を組み合わせて、偶数グループを作成できます。
split(x,cut(x,quantile(x,(0:n)/n), include.lowest=TRUE, labels=FALSE))
これにより、例では同じ結果が得られますが、歪んだ変数では得られません。
別のバリエーションがあります。
注:このサンプルでは、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,|
私は同じ関数が必要で、以前の解決策を読みましたが、最後にアンバランスなチャンクを持っている必要がありました。つまり、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
ベース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
単純にインデックスを使用してベクトルを分割する単純な関数-これを複雑にする必要はありません
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()
が気に入らない場合 そして 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()
と同様に、リストを返しますが、ラベルで時間やスペースを無駄にしないため、パフォーマンスが向上する可能性があります。
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を小さなチャンクにスライスすることは推奨されないかもしれませんが、避けられないかもしれません。
これは@Sebastianの功績です function
chunk <- function(x,y){
split(x, factor(sort(rank(row.names(x))%%y)))
}
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])の列は、探しているドロイドです。
うわー、この質問は予想以上の牽引力を得ました。
すべてのアイデアをありがとう。私はこの解決策を思いつきました:
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)のように動作しますが、実際には重複エントリで正しい結果を生成できます。
さらに別の可能性は、パッケージ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
この回答が遅すぎる場合は申し訳ありませんが、おそらく他の人にとって役に立つかもしれません。実際、この問題に対する非常に有用な解決策があります。?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
これは、サイズ⌊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))