2010-07-23 18 views
157

Devo dividere un vettore in n pezzi di uguale dimensione in R. Non sono riuscito a trovare alcuna funzione di base per farlo. Inoltre, Google non mi ha portato da nessuna parte. Quindi ecco cosa mi è venuto in mente, spero che aiuti qualcuno in qualche posto.Dividi un vettore in blocchi in R

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 

Eventuali commenti, suggerimenti o miglioramenti sono davvero benvenuti e apprezzati.

Cheers, Sebastian

+4

Sì, è molto chiaro che quello che si ottiene è la soluzione a "n pezzi di uguale dimensione". Ma forse questo ti porta anche lì: x <- 1:10; n <- 3; split (x, cut (x, n, labels = FALSE)) – mdsumner

+0

sia la soluzione nella domanda, sia la soluzione nel commento precedente non sono corretti, in quanto potrebbero non funzionare, se il vettore ha voci ripetute. Prova questo: > foo <- c (rep (1, 12), rep (2,3), rep (3,3)) [1] 1 1 1 1 1 1 1 1 1 1 1 2 2 2 3 3 3 > pezzo (foo, 2) (dà risultato sbagliato) > chunk (foo, 3) (anche sbagliato) – mathheadinclouds

+0

(continua commento precedente) perché? rank (x) non deve essere un numero intero > rank (c (1,1,2,3)) [1] 1,5 1,5 3,0 4,0 ecco perché il metodo nella domanda fallisce. questo funziona (grazie a Harlan in basso) > chunk2 <- function (x, n) split (x, cut (seq_along (x), n, labels = FALSE)) – mathheadinclouds

risposta

7

È possibile combinare la divisione/taglio, come suggerito da mdsummer, con quantile per creare anche gruppi:

split(x,cut(x,quantile(x,(0:n)/n), include.lowest=TRUE, labels=FALSE)) 

Questo dà lo stesso risultato per il tuo esempio, ma non per variabili distorte.

12

Alcuni più varianti al palo ...

> x <- 1:10 
> n <- 3 

nota, che non è necessario utilizzare la funzione factor qui, ma si vuole ancora sort o/w il tuo primo vettore sarebbe 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 

Oppure è possibile assegnare indici di carattere, vicepresidenti i numeri a sinistra zecche sopra:

> 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 

Oppure è possibile utilizzare nomi di parole semplici memorizzati in un vettore. Si noti che l'utilizzo di sort per ottenere valori consecutivi in ​​x alfabetizza le etichette:

> 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 
18

Questo si dividerà in modo diverso a quello che hai, ma è ancora abbastanza una struttura bella lista penso:

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

Quale vi darà la seguente, a seconda di come lo vuoi formattato:

> 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 

Esecuzione di un paio di tempi che utilizzano queste impostazioni:

set.seed(42) 
x <- rnorm(1:1e7) 
n <- 3 

Poi abbiamo i seguenti risultati:

> 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 

EDIT: Cambiare da as.factor() per as.character() nella mia funzione ha reso due volte più veloce.

5

split(x,matrix(1:n,n,length(x))[1:length(x)])

forse questo è più evidente, ma la stessa idea:
split(x,rep(1:n, ceiling(length(x)/n),length.out = length(x)))

se lo vuoi ordinato, lanciare una sorta intorno ad esso

232

una battuta divisione d in blocchi di dimensioni 20:

split(d, ceiling(seq_along(d)/20)) 

Ulteriori dettagli: Penso tutto ciò che serve è seq_along(), split() e 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 
+18

La domanda richiede' n' pezzi di dimensioni uguali. Questo ti dà un numero sconosciuto di pezzi di dimensioni 'n'. Ho avuto lo stesso problema e ho usato le soluzioni di @mathheadinclouds. – rrs

+2

Come si può vedere dall'uscita di d1, questa risposta non suddivide d in gruppi di uguale dimensione (4 è ovviamente più breve). Quindi non risponde alla domanda. – Calimo

+6

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

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

avevo bisogno la stessa funzione e hanno letto le precedenti soluzioni, però ho anche bisogno di avere il pezzo non bilanciato per essere alla fine vale a dire se ho 10 elementi per dividerli in vettori di 3 ciascuno, quindi il mio risultato dovrebbe avere vettori con 3,3,4 elementi rispettivamente. Così ho usato il seguente (ho lasciato il codice unoptimised per migliorare la leggibilità, altrimenti non c'è bisogno di avere molte variabili):

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

Ecco un'altra variante.

NOTA: con questo esempio si sta specificando la dimensione del blocco nel secondo parametro

  1. tutti i pezzi sono uniformi, tranne che per l'ultimo;
  2. l'ultimo sarà nel peggiore dei casi più piccolo, mai più grande della dimensione del blocco.

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

credito a @Sebastian per questo function

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

Se non ti piace split() e non vi occupate di AN imbottitura fuori la vostra coda corta:

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

Le colonne del ma restituito trix ([, 1: ncol]) sono i droidi che stai cercando.

2

Se non ti piace split()e non ti piace matrix() (con il suo penzoloni AN), c'è questo:

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

Come split(), restituisce una lista, ma non lo fa perdere tempo o spazio con le etichette, quindi potrebbe essere più performante.

13

Prova la funzione 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 
+0

Questo non funziona per dividere il 'x',' y' o 'z' definito in [questo commento] (https://stackoverflow.com/questions/3318333/split-a-vector-into-chunks- in-R # comment84830680_3318333). In particolare, ordina i risultati, che possono o meno essere a posto, a seconda dell'applicazione. – Kalin

+0

Piuttosto, [questo commento] (https://stackoverflow.com/questions/3318333/split-a-vector-into-chunks-in-r#comment84830878_3318333). – Kalin

15
simplified version... 
n = 3 
split(x, sort(x%%n)) 
+0

Mi piace perché fornisce blocchi di dimensioni uguali alle dimensioni (utile per dividere un'attività di grandi dimensioni, ad esempio per ospitare RAM limitata o eseguire un'attività su più thread). – alexvpickering

+1

Questo è utile, ma tieni presente che funzionerà solo con vettori numerici. –

1

Ho bisogno di una funzione che prende l'argomento di un data.table (tra virgolette) e un altro argomento che è il limite superiore al numero di righe nei sottoinsiemi di quel dato originale.table.Questa funzione produce qualunque sia il numero di data.tables tale limite superiore permette di:

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

Questa funzione mi dà una serie di data.tables chiamato df_ [numero] con la fila a partire dalla data.table originale nel nome . L'ultimo data.table può essere breve e pieno di NA, quindi devi ricondurlo a qualsiasi dato. Questo tipo di funzione è utile perché alcuni software GIS hanno dei limiti sul numero di pin di indirizzo che puoi importare, per esempio. Pertanto, l'analisi dei dati in sezioni più piccole potrebbe non essere consigliabile, ma potrebbe non essere evitabile.

0

funzione semplice per suddividere un vettore, semplicemente utilizzando gli indici - non c'è bisogno di complicare questo

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]) 
    })) 
} 
Problemi correlati