Ich muss einen Vektor in n gleich große Blöcke in R aufteilen. Ich konnte keine Basisfunktion dafür finden. Auch Google hat mich nirgendwo hinbekommen. Also hier ist was ich mir ausgedacht habe, hoffentlich hilft es jemandem wo.
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
Alle Kommentare, Vorschläge oder Verbesserungen sind sehr willkommen und willkommen.
Prost, Sebastian
Ein-Liner, der d in Stücke der Größe 20 aufteilt:
split(d, ceiling(seq_along(d)/20))
Weitere Details: Ich denke, alles was Sie brauchen, ist seq_along()
, split()
und 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))
Dies wird sich anders als das, was Sie haben, aufteilen, aber ich denke immer noch eine ziemlich nette Listenstruktur:
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])
}
Je nachdem, wie Sie es formatieren möchten, erhalten Sie Folgendes:
> 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
Einige Timings mit diesen Einstellungen ausführen:
set.seed(42)
x <- rnorm(1:1e7)
n <- 3
Dann haben wir folgende Ergebnisse:
> 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
BEARBEITEN: Der Wechsel von as.factor () zu as.character () in meiner Funktion hat es doppelt so schnell gemacht.
Probieren Sie die Funktion ggplot2 aus, 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
Noch ein paar Varianten zum Haufen ...
> x <- 1:10
> n <- 3
Beachten Sie, dass Sie hier nicht die Funktion factor
verwenden müssen. Sie möchten jedoch trotzdem sort
. Ihr erster Vektor wäre 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
Oder Sie können Zeichenindizes zuweisen, und umgekehrt die Zahlen in den linken Ticks oben:
> 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
Oder Sie können Klartextnamen verwenden, die in einem Vektor gespeichert sind. Beachten Sie, dass die Verwendung von sort
zum Abrufen aufeinander folgender Werte in x
die Beschriftungen alphabetisch sortiert:
> 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
Sie können den von mdsummer vorgeschlagenen Split/Cut mit Quantile kombinieren, um gerade Gruppen zu erstellen:
split(x,cut(x,quantile(x,(0:n)/n), include.lowest=TRUE, labels=FALSE))
Dies ergibt das gleiche Ergebnis für Ihr Beispiel, jedoch nicht für schräge Variablen.
split(x,matrix(1:n,n,length(x))[1:length(x)])
vielleicht ist das klarer, aber die gleiche Idee:split(x,rep(1:n, ceiling(length(x)/n),length.out = length(x)))
wenn Sie es bestellen möchten, werfen Sie eine Sorte darum
Hier ist eine andere Variante.
HINWEIS: In diesem Beispiel geben Sie die CHUNK SIZE im zweiten Parameter an
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,|
Ich brauchte die gleiche Funktion und habe die vorherigen Lösungen gelesen. Allerdings musste ich auch den unausgeglichenen Block am Ende haben, dh wenn ich 10 Elemente habe, um sie in Vektoren von jeweils 3 aufzuteilen, sollte mein Ergebnis Vektoren mit 3 haben. 3,4 Elemente. Also habe ich Folgendes verwendet (ich habe den Code aus Gründen der Lesbarkeit nicht optimiert, ansonsten müssen nicht viele Variablen verwendet werden):
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
Einfache Funktion zum Aufteilen eines Vektors durch einfaches Verwenden von Indizes - dies muss nicht zu kompliziert werden
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])
}))
}
Wenn Sie split()
nicht mögen und Du magst matrix()
(mit seinen hängenden NAs) nicht, es gibt folgendes:
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))
Wie split()
gibt es eine Liste zurück, verschwendet jedoch keine Zeit und keinen Platz mit Beschriftungen, daher ist es möglicherweise performanter.
Ich brauche eine Funktion, die das Argument einer data.table (in Anführungszeichen) verwendet, und ein anderes Argument, das die Obergrenze für die Anzahl der Zeilen in den Teilmengen dieser ursprünglichen data.table ist. Diese Funktion erzeugt eine beliebige Anzahl von data.tables, die der obere Grenzwert zulässt:
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)
}
Diese Funktion gibt mir eine Reihe von data.tables namens df_ [number] mit der Anfangszeile der ursprünglichen data.table im Namen. Die letzte data.table kann kurz sein und mit NAs gefüllt sein, so dass Sie diese auf die verbleibenden Daten zurücksetzen müssen. Diese Art von Funktion ist nützlich, da für bestimmte GIS-Software beispielsweise Beschränkungen bestehen, wie viele Adress-Pins Sie importieren können. Das Aufteilen von data.tables in kleinere Abschnitte wird möglicherweise nicht empfohlen, ist jedoch möglicherweise nicht vermeidbar.
Dank an @Sebastian für diese Funktion
chunk <- function(x,y){
split(x, factor(sort(rank(row.names(x))%%y)))
}
rep_len
der Basis-R verwenden:
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
Und wie bereits erwähnt, wenn Sie sortierte Indizes wünschen, einfach:
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
Wenn Sie split()
nicht mögen und es Ihnen nichts ausmacht, wenn NAs Ihren kurzen Schwanz ausklappen:
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))} }
Die Spalten der zurückgegebenen Matrix ([ 1: ncol]) sind die Droiden, nach denen Sie suchen.
Wow, diese Frage bekam mehr Zugkraft als erwartet.
Danke für all die Ideen. Ich habe mir diese Lösung ausgedacht:
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"
Der Schlüssel ist die Verwendung des Parameters seq (each = chunk.size), damit es funktioniert. Die Verwendung von seq_along verhält sich in meiner vorherigen Lösung wie Rang (x), ist jedoch in der Lage, das richtige Ergebnis mit doppelten Einträgen zu erzeugen.
Eine weitere Möglichkeit ist die splitIndices
-Funktion aus dem Paket parallel
:
library(parallel)
splitIndices(20, 3)
Gibt:
[[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
Tut mir leid, wenn diese Antwort so spät kommt, aber vielleicht kann es für jemand anderen nützlich sein. Tatsächlich gibt es eine sehr nützliche Lösung für dieses Problem, die am Ende von Split erklärt wird.
> 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
Dies teilt sich in Blöcke der Größe ⌊n/k⌋ + 1 oder ⌊n/k⌋ auf und verwendet nicht die Sortierung 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))