Je dois diviser un vecteur en n morceaux de taille égale en R. Je ne pouvais trouver aucune fonction de base pour le faire. De plus, Google ne m'a pas conduit nulle part. Donc, voici ce que je suis venu avec, j'espère que cela aide quelqu'un quelque part.
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
Tous les commentaires, suggestions ou améliorations sont vraiment les bienvenus et appréciés.
A bientôt, Sebastian
Une ligne qui se scinde en morceaux de taille 20:
split(d, ceiling(seq_along(d)/20))
Plus de détails: Je pense que tout ce dont vous avez besoin est seq_along()
, split()
et 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))
Cela divisera la chose différemment de ce que vous avez, mais c'est quand même une structure de liste agréable, je pense:
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])
}
Ce qui vous donnera ce qui suit, selon la façon dont vous voulez le formater:
> 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
Exécuter quelques timings en utilisant ces paramètres:
set.seed(42)
x <- rnorm(1:1e7)
n <- 3
Ensuite, nous avons les résultats suivants:
> 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: Changer de as.factor () à as.character () dans ma fonction l'a fait deux fois plus vite.
Essayez la fonction 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
Quelques variantes supplémentaires à la pile ...
> x <- 1:10
> n <- 3
Notez que vous n'avez pas besoin d'utiliser la fonction factor
ici, mais vous voulez quand même sort
dont le premier vecteur serait 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
Ou vous pouvez assigner des index de caractère, au contraire des nombres dans les ticks à gauche ci-dessus:
> 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
Ou vous pouvez utiliser des noms simples enregistrés dans un vecteur. Notez que l’utilisation de sort
pour obtenir des valeurs consécutives en x
permet d’alphabétiser les étiquettes:
> 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
Vous pouvez combiner le fractionnement/découpage, comme suggéré par mdsummer, avec un quantile pour créer des groupes pairs:
split(x,cut(x,quantile(x,(0:n)/n), include.lowest=TRUE, labels=FALSE))
Cela donne le même résultat pour votre exemple, mais pas pour les variables asymétriques.
split(x,matrix(1:n,n,length(x))[1:length(x)])
c'est peut-être plus clair, mais la même idée:split(x,rep(1:n, ceiling(length(x)/n),length.out = length(x)))
si vous voulez le commander, jetez un sort autour
Voici une autre variante.
REMARQUE: avec cet exemple, vous spécifiez CHUNK SIZE dans le deuxième paramètre.
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,|
J'avais besoin de la même fonction et j'avais lu les solutions précédentes, mais j'avais aussi besoin du bloc déséquilibré pour être à la fin, c'est-à-dire que si j'ai 10 éléments pour les scinder en vecteurs de 3 chacun, mon résultat devrait avoir des vecteurs avec 3 3,4 éléments respectivement. J'ai donc utilisé ce qui suit (j'ai laissé le code non optimisé pour la lisibilité, sinon pas besoin d'avoir beaucoup de 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
Fonction simple pour diviser un vecteur en utilisant simplement des index - inutile de trop compliquer les choses
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])
}))
}
Si vous n'aimez pas split()
et vous n'aimez pas matrix()
(avec ses NA pendants), il y a ceci:
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))
Comme split()
, il retourne une liste, mais il ne perd pas de temps et d'espace avec les étiquettes, il peut donc être plus performant.
J'ai besoin d'une fonction qui prend l'argument d'un data.table (entre guillemets) et un autre argument qui est la limite supérieure du nombre de lignes dans les sous-ensembles de ce data.table d'origine. Cette fonction produit le nombre de data.tables que la limite supérieure permet:
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)
}
Cette fonction me donne une série de data.tables nommée df_ [numéro] avec la première ligne du nom original data.table. Le dernier fichier data.table peut être court et rempli d'AN. Vous devez donc sous-définir ce dernier en fonction des données restantes. Ce type de fonction est utile car certains logiciels SIG limitent le nombre de broches d’adresses que vous pouvez importer, par exemple. Donc, découper des data.tables en plus petits morceaux peut ne pas être recommandé, mais cela peut ne pas être évitable.
Merci à @Sebastian pour cette function
chunk <- function(x,y){
split(x, factor(sort(rank(row.names(x))%%y)))
}
En utilisant le rep_len
de la base R:
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
Et comme déjà mentionné si vous voulez des index triés, simplement:
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
Si vous n'aimez pas split()
et que cela ne vous dérange pas, les membres de l'équipe nationale complètent votre queue courte
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))} }
Les colonnes de la matrice retournée ([ 1: ncol]) sont les droïdes que vous recherchez.
Wow, cette question a eu plus de succès que prévu.
Merci pour toutes les idees. Je suis venu avec cette solution:
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"
La clé consiste à utiliser le paramètre seq (each = chunk.size) pour que cela fonctionne. Utiliser seq_along agit comme le rang (x) dans ma solution précédente, mais est en réalité capable de produire le résultat correct avec des entrées en double.
Une autre possibilité est la fonction splitIndices
du paquet parallel
:
library(parallel)
splitIndices(20, 3)
Donne:
[[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
Désolé si cette réponse est si tardive, mais peut-être qu'elle peut être utile à quelqu'un d'autre. En fait, il existe une solution très utile à ce problème, expliquée à la fin de la division.
> 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
Cela se divise en morceaux de taille ⌊n/k⌋ + 1 ou n/k⌋ et n'utilise pas le tri 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))