varias - Divide un vector en trozos en R
varias graficas en r (15)
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.
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 es realmente bienvenido y apreciado.
Saludos, Sebastian
Algunas variantes más a la pila ...
> x <- 1:10
> n <- 3
Tenga en cuenta que no necesita usar la función de factor
aquí, pero aún quiere sort
el 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, vicio a los números en los tics de la izquierda de 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
Aquí hay otra variante.
NOTA: con esta muestra está especificando el TAMAÑO DE CHUNK en el segundo parámetro
- todos los trozos son uniformes, excepto el último;
- 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,|
Esto lo dividirá de manera diferente a lo que tienes, pero creo que sigue siendo una buena estructura de listas:
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])
}
Lo cual le dará lo siguiente, dependiendo de cómo lo quiera formatear:
> 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
Ejecutando un par de sincronizaciones usando estas configuraciones:
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
EDITAR: Cambiar de as.factor () a as.character () en mi función lo hizo dos veces más rápido.
Función simple para dividir un vector simplemente usando índices: no hay necesidad de complicar demasiado esto
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])
}))
}
Gracias a @Sebastian por esta function
chunk <- function(x,y){
split(x, factor(sort(rank(row.names(x))%%y)))
}
Necesitaba la misma función y había leído las soluciones anteriores, sin embargo, también necesitaba tener el fragmento desequilibrado para estar al final, es decir, si tengo 10 elementos para dividirlos en vectores de 3 cada uno, entonces mi resultado debería tener vectores con 3, 3,4 elementos respectivamente. Así que utilicé lo siguiente (dejé el código sin optimizar para la legibilidad, de lo contrario no es necesario 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
Necesito una función que tome el argumento de un data.table (entre comillas) y otro argumento que sea el límite superior sobre el número de filas en los subconjuntos de ese data.table original. Esta función produce cualquier cantidad de datos. Tablas que el 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 tablas de datos llamadas df_ [número] con la fila inicial de la tabla de datos 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.
Prueba la función cut_number
, 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
Puede combinar la división / corte, como lo sugiere mdsummer, con quantile para crear grupos pares:
split(x,cut(x,quantile(x,(0:n)/n), include.lowest=TRUE, labels=FALSE))
Esto le da el mismo resultado para su ejemplo, pero no para las variables asimétricas.
Si no te gusta split()
y no te gusta matrix()
(con sus NA colgantes), 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))
Al igual que split()
, devuelve una lista, pero no pierde tiempo ni espacio con las etiquetas, por lo que puede ser más eficaz.
Si no te gusta la split()
y no te importa que las NAs rellenen tu 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 matriz devuelta ([, 1: ncol]) son los droides que está buscando.
Una división de un trazador de líneas en trozos de tamaño 20:
split(d, ceiling(seq_along(d)/20))
Más detalles: creo que 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
split(x,matrix(1:n,n,length(x))[1:length(x)])
quizás esto es más claro, pero la misma idea:
split(x,rep(1:n, ceiling(length(x)/n),length.out = length(x)))
si lo quieres ordenado, haz una especie de rodeo
chunk2 <- function(x,n) split(x, cut(seq_along(x), n, labels = FALSE))
simplified version...
n = 3
split(x, sort(x%%n))