Bir vektörü R'deki parçalara ayırın


227

Bir vektörü R cinsinden eşit büyüklükteki n parçalarına bölmeliyim. Bunu yapmak için herhangi bir temel işlev bulamadım. Ayrıca Google beni hiçbir yere götürmedi. İşte ortaya çıkardığım şey, umarım bir yere yardım eder.

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

Herhangi bir yorum, öneri veya iyileştirme gerçekten memnuniyetle karşılanır ve takdir edilir.

Şerefe, Sebastian


5
Evet, elde ettiğiniz şeyin "eşit büyüklükteki n topaklara" çözüm olduğu çok açık değil. Ama belki bu da sizi oraya götürür: x <- 1:10; n <- 3; split (x, kes (x, n, etiketler = YANLIŞ))
mdsumner

hem sorudaki çözüm hem de önceki açıklamadaki çözüm yanlıştır, çünkü vektörde tekrarlanan girişler varsa bunlar çalışmayabilir. Bunu deneyin:> 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> chunk (foo, 2) (yanlış sonuç verir)> chunk (foo, 3) (ayrıca yanlış)
mathheadinclouds

(önceki yorumu devam ediyor) neden? rank (x) bir tamsayı> rank (c (1,1,2,3)) [1] 1.5 1.5 3.0 4.0 tam sayı olmak zorunda değildir, bu yüzden sorudaki yöntem başarısız olur. bu çalışır (aşağıdaki Harlan sayesinde)> chunk2 <- fonksiyon (x, n) bölünmüş (x, kes (seq_along (x), n, etiketler = YANLIŞ))
mathheadinclouds 29:13

2
> split (foo, cut (foo, 3, etiketler = YANLIŞ)) (ayrıca yanlış)
mathheadinclouds

1
@Mathheadinclouds'un da belirttiği gibi, örnek veriler çok özel bir durumdur. Daha genel örnekler daha faydalı ve daha iyi testler olacaktır. Örnegin x <- c(NA, 4, 3, NA, NA, 2, 1, 1, NA ); y <- letters[x]; z <- factor(y), önceden siralanmamis ve farkli siralarda (tamsayı, karakter, faktör) eksik veri, tekrarlanan degerlere sahip örnekler verir.
Kalin

Yanıtlar:


315

D'yi 20 boyutlu parçalara bölen tek astarlı:

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

Diğer ayrıntılar: Ben tek ihtiyacınız olduğunu düşünüyorum seq_along(), split()ve 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

34
Soru neşit büyüklükte parçalar ister . Bu size bilinmeyen bir boyutta boyut kazandırır n. Aynı sorunu yaşadım ve çözümleri mathheadinclouds'dan kullandım.
rrs

4
D1'in çıktısından görebileceği gibi, bu cevap d'yi eşit büyüklükteki gruplara ayırmaz (4 açıkça daha kısadır). Böylece soruya cevap vermez.
Calimo

9
@rrs: bölünmüş (d, tavan (seq_along (d) / (uzunluk (d) / n)))
gkcn

Bunun oldukça eski olduğunu biliyorum, ama burada rastlayanlara yardımcı olabilir. Her ne kadar OP'nin sorusu eşit büyüklükteki parçalara bölünmek olsa da, vektör bölücünün katı değilse, son chink yığından farklı bir boyuta sahip olacaktır. Ayrılmak için n-chunkskullandım max <- length(d)%/%n. Bunu 31 karakterden oluşan bir vektörle kullandım ve 10 cümleden oluşan 3 vektörün ve 1 cümleden birinin bir listesini elde ettim.
salvu


36
simplified version...
n = 3
split(x, sort(x%%n))

Mümkün olduğunca eşit boyutta parçalar verir gibi bunu seviyorum (örneğin sınırlı RAM barındırmak veya birden çok iş parçacığında bir görevi çalıştırmak için büyük görevi bölmek için iyi).
alexvpickering

3
Bu yararlıdır, ancak bunun yalnızca sayısal vektörler üzerinde çalışacağını unutmayın.
Keith Hughitt

@ KeithHughitt bu faktörlerle çözülebilir ve seviyeleri sayısal olarak döndürülebilir. Ya da en azından ben böyle uyguladım.
drmariod

20

Ggplot2 işlevini deneyin 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

2
Yukarı Bu yarma için çalışmaz x, yya ztanımlanan bu yorumun . Özellikle, uygulamaya bağlı olarak iyi olabilecek veya olmayabilecek sonuçları sıralar.
Kalin

Aksine, bu yorum .
Kalin

18

Bu, sahip olduklarınıza farklı şekilde bölünecektir, ancak yine de oldukça güzel bir liste yapısı olduğunu düşünüyorum:

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

Bu, nasıl biçimlendirilmesini istediğinize bağlı olarak size aşağıdakileri verecektir:

> 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

Bu ayarları kullanarak birkaç zamanlama çalıştırma:

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

Sonra aşağıdaki sonuçlara sahibiz:

> 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: işlevimde as.factor () 'dan as.character ()' e geçiş onu iki kat daha hızlı yaptı.


13

Kazık için birkaç varyant ...

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

factorBuradaki işlevi kullanmanız gerekmediğini , ancak yine de sortilk vektörünüzü o / w yapmak istediğinizi unutmayın 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

Veya karakter endeksleri atayabilir, yukarıdaki sol kenelerdeki sayıları değiştirebilirsiniz:

> 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

Veya bir vektörde saklanan düz sözcük adlarını kullanabilirsiniz. sortArdışık değerleri almak için kullanmanın xetiketleri alfabetikleştirdiğini unutmayın:

> 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

Baz R'leri kullanma rep_len:

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

Ve daha önce de belirtildiği gibi, sıralı indeksler istiyorsanız, basitçe:

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

9

Mdsummer tarafından önerildiği gibi bölme / kesmeyi eşit gruplar oluşturmak için kantil ile birleştirebilirsiniz:

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

Bu, örneğiniz için aynı sonucu verir, ancak çarpık değişkenler için geçerli değildir.


7

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

belki bu daha açık, ama aynı fikir:
split(x,rep(1:n, ceiling(length(x)/n),length.out = length(x)))

sipariş edilmesini istiyorsan, etrafına bir tür at


6

Aynı fonksiyona ihtiyacım vardı ve önceki çözümleri okudum, ancak aynı zamanda dengesiz yığının sonunda olması gerekiyordu, yani her birinin 3'lü vektörlere bölmek için 10 elementim varsa, sonuçum 3'lü vektörlere sahip olmalı, Sırasıyla 3,4 element. Bu yüzden aşağıdaki kullanılır (i okunabilirlik için optimize edilmemiş kod bıraktı, aksi takdirde birçok değişkene gerek yok):

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

İşte başka bir varyant.

NOT: Bu örnekle ikinci parametrede CHUNK SIZE (BELLEK BOYUTU)

  1. sonuncusu hariç tüm parçalar eşittir;
  2. sonuncusu en kötü ihtimalle daha küçük olacak, asla yığın boyutundan daha büyük olmayacaktır.

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

4

Bir dizini basitçe dizinler kullanarak bölmek için basit işlev - bunu fazla zorlaştırmanıza gerek yok

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

3

Eğer sevmiyorsanız split() ve sevmiyorsanız matrix()(sarkan NA'ları ile), şu var:

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

Gibi split(), bir liste döndürür, ancak etiketlerle zaman veya yer kaybetmez, bu yüzden daha performanslı olabilir.


2

Bu işlev için @Sebastian'a teşekkür ederiz

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

2

Eğer beğenmediyseniz split()ve NA'ların kısa kuyruğunuzu doldurmasını önemsemiyorsanız:

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

Döndürülen matrisin sütunları ([, 1: ncol]) aradığınız droidlerdir.


2

Ben bir data.table argümanı (tırnak içinde) ve o orijinal data.table alt kümelerinde satır sayısı üst sınırı olan başka bir argüman alır bir işlev gerekir. Bu işlev, üst sınırın izin verdiği sayıda veri üretir.

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

Bu işlev bana orijinal veri.table adındaki başlangıç ​​satırı ile df_ [sayı] adlı bir data.tables dizisi verir. Son data.table kısa olabilir ve NA'larla doldurulabilir, böylece geri kalan verilere geri ayarlamanız gerekir. Bu tür bir işlev yararlıdır, çünkü bazı CBS yazılımları örneğin kaç tane adres pini alabileceğiniz konusunda sınırlara sahiptir. Bu nedenle data.table'ları daha küçük parçalara bölmek tavsiye edilmeyebilir, ancak önlenemez.


2

Bu cevap çok geç gelirse özür dilerim, ama belki başka biri için yararlı olabilir. Aslında? Bölünmenin sonunda açıklanan bu soruna çok yararlı bir çözüm var.

> 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

3
bu, her grupta eşit sayıda değer olmadığında kırılacaktır!
Matifou

2

Yine bir başka olasılık da splitIndicespaketin fonksiyonudur parallel:

library(parallel)
splitIndices(20, 3)

verir:

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

Vay be, bu soru beklenenden daha fazla çekişe sahip.

Tüm fikirler için teşekkürler. Bu çözümü buldum:

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"

Anahtar seq (each = chunk.size) parametresini kullanmaktır. Seq_along kullanmak önceki çözümümde rank (x) gibi davranıyor, ancak aslında yinelenen girişlerle doğru sonucu üretebiliyor.


Rep'ın (seq_along (x), her = elements.per.chunk) bellekte çok zorlanabileceğinden endişe duyanlar için: evet öyle. Önceki önerimin değiştirilmiş bir sürümünü deneyebilirsiniz: chunk <- function (x, n) split (x, factor (seq_along (x) %% n))
Sebastian

0

Bu ⌊n / k⌋ + 1 veya ⌊n / k⌋ boyutunda parçalara ayrılır ve O (n log n) türünü kullanmaz.

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))
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.