2010-07-23 17 views
157

Tengo que dividir un vector en n trozos de igual tamaño en R. No pude encontrar ninguna función base para hacer eso. Además, Google no me llevó a ninguna parte. Así que esto es lo que se me ocurrió, con suerte, ayuda a alguien en algún lugar.Dividir un vector en trozos en 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 

Cualquier comentario, sugerencia o mejora son realmente bienvenidos y apreciados.

Saludos, Sebastian

+4

Sí, es muy claro que lo que se obtiene es la solución a "n trozos de igual tamaño". Pero tal vez esto también te lleve allí: x <- 1:10; n <- 3; split (x, cut (x, n, labels = FALSE)) – mdsumner

+0

Tanto la solución en la pregunta como la solución en el comentario anterior son incorrectas, ya que podrían no funcionar, si el vector tiene entradas repetidas. Pruebe esto: > 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 > trozo (foo, 2) (da un resultado erróneo) > trozo (foo, 3) (también mal) – mathheadinclouds

+0

(continuando comentario anterior) por qué? rango (x) no necesita ser un número entero > rango (c (1,1,2,3)) [1] 1,5 1,5 3,0 4,0 así que por eso el método en la pregunta falla. Esta funciona (gracias a Harlan a continuación) > chunk2 <- function (x, n) split (x, corte (seq_along (X), n, etiquetas = FALSE)) – mathheadinclouds

Respuesta

7

Se puede combinar la división/corte, según lo sugerido por mdsummer, con cuantil para crear incluso grupos:

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

Esto da el mismo resultado por su ejemplo, pero no para variables asimétricas

12

Unas cuantas más variantes a la pila ...

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

Nota, que no es necesario utilizar la función factor aquí, pero todavía quiere sort o/w su primer vector sería 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 

O puede asignar índices de caracteres, vice los números de garrapatas izquierda arriba:

> 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 

O puede usar nombres de palabras simples almacenados en un vector. Tenga en cuenta que el uso de sort para obtener valores consecutivos en x alfabetiza las etiquetas:

> 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

Esto dividirá de manera diferente a lo que tiene, pero es todavía una estructura bastante lista agradable pienso:

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

Qué le dará la siguiente, dependiendo de cómo quiere que el formato:

> 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 

Ejecución de un par de tiempos que utilizan estos ajustes:

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

entonces tenemos los siguientes resultados:

> 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: El cambio de as.factor() para as.character() en mi función lo hizo dos veces más rápido.

5

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

tal vez esto es más claro, pero la misma idea:
split(x,rep(1:n, ceiling(length(x)/n),length.out = length(x)))

si quieres que ordenó, lanzar una especie alrededor

232

Una sola línea de división d en trozos de tamaño 20:

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

Más detalles: creo todo lo que necesita es seq_along(), split() y 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 pregunta requiere' n' trozos del mismo tamaño. Esto te da un número desconocido de fragmentos de tamaño 'n'. Tuve el mismo problema y utilicé las soluciones de @mathheadinclouds. – rrs

+2

Como se puede ver en la salida de d1, esta respuesta no se divide en grupos de igual tamaño (4 es obviamente más corto). Por lo tanto, no responde la pregunta. – Calimo

+6

@rrs: división (d, techo (seq_along (d)/(longitud (d)/n))) – gkcn

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

que necesitaba la misma función y han leído las soluciones anteriores, sin embargo yo también necesitaba tener el trozo desequilibrada a estar en el extremo, es decir, si tengo 10 elementos para dividirlos en vectores de 3 cada uno, entonces mi resultado debe tener vectores con 3,3,4 elementos respectivamente. Así que utiliza los siguientes (i dejado el código unoptimised para facilitar la lectura, de lo contrario no hay necesidad de tener muchas variables):

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

Aquí es otra variante.

NOTA: con esta muestra que está especificando el tamaño del fragmento en el segundo parámetro

  1. todos los trozos son uniformes, a excepción de la última;
  2. el último será, en el peor de los casos, más pequeño, nunca más grande que el tamaño del fragmento.

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

crédito a @Sebastian para este function

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

Si no te gusta split() y no le importa AN relleno fuera de su cola 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))} } 

Las columnas de la máquina devuelta trix ([, 1: ncol]) son los droides que estás buscando.

2

Si no te gusta split()y no le gusta matrix() (con su colgando AN), hay esto:

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

Como split(), devuelve una lista, pero no lo hace pierda tiempo o espacio con etiquetas, por lo que puede ser más eficiente.

13

Pruebe la función 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

Esto no funciona para dividir el 'x',' y', o 'z' definido en [este comentario] (https://stackoverflow.com/questions/3318333/split-a-vector-into-chunks- in-r # comment84830680_3318333). En particular, ordena los resultados, que pueden estar correctos o no, según la aplicación. – Kalin

+0

Más bien, [este comentario] (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

Me gusta, ya que le da trozos que son del mismo tamaño posible (bueno para dividir una tarea grande, por ejemplo, para acomodar RAM limitada o para ejecutar una tarea en varios subprocesos). – alexvpickering

+1

Esto es útil, pero tenga en cuenta que esto solo funcionará en vectores numéricos. –

1

Necesito una función que toma el argumento de un data.table (entre comillas) y otro argumento que es el límite superior del número de filas en los subconjuntos de esa data.table original.Esta función produce cualquier número de data.tables que límite superior permite:

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

esta función me da una serie de data.tables llamado df_ [número] con la fila a partir de la data.table original en el nombre . La última tabla de datos puede ser corta y estar llena de NA, por lo que debe subconjuntarla a los datos que queden. Este tipo de función es útil porque ciertos programas GIS tienen límites sobre cuántos pines de dirección puede importar, por ejemplo. Por lo tanto, no es recomendable dividir los datos en tablas en trozos más pequeños, pero puede que no se pueda evitar.

0

función simple para dividir un vector mediante el simple uso de índices - no hay necesidad de complicar más este

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