webentwicklung-frage-antwort-db.com.de

Teile einen Vektor in Stücke in R auf

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

178
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
264
Harlan
chunk2 <- function(x,n) split(x, cut(seq_along(x), n, labels = FALSE)) 
57
simplified version...
n = 3
split(x, sort(x%%n))
24
zhan2383

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.

18
Tony Breyal

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
17
Scott Worland

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
12
Richard Herron

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.

7
SiggyF

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

7
frankc

Hier ist eine andere Variante. 

HINWEIS: In diesem Beispiel geben Sie die CHUNK SIZE im zweiten Parameter an

  1. alle Brocken sind bis auf die letzten gleichförmig;
  2. der letzte wird im schlimmsten Fall kleiner sein, niemals größer als die Brockengröße.

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,|
6
eAndy

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
5
Zak D

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

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.

3
verbamour

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.

2
rferrisx

Dank an @Sebastian für diese Funktion

chunk <- function(x,y){
         split(x, factor(sort(rank(row.names(x))%%y)))
         }
2
user1587280

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
2
FXQuantTrader

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.

2
verbamour

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. 

0
Sebastian

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
0
Matifou

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
0
Laura Paladini

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