แยกเวกเตอร์เป็นชิ้นใน R


227

ฉันต้องแยกเวคเตอร์เป็น n ชิ้นที่มีขนาดเท่ากันในอาร์ฉันไม่สามารถหาฟังก์ชันพื้นฐานใด ๆ นอกจากนี้ Google ยังไม่ได้รับฉันทุกที่ ดังนั้นนี่คือสิ่งที่ฉันมาด้วยหวังว่ามันจะช่วยให้ใครบางคนที่ไหน

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

ความคิดเห็นข้อเสนอแนะหรือการปรับปรุงใด ๆ ยินดีต้อนรับและชื่นชมจริงๆ

ไชโยเซบาสเตียน


5
ใช่มันไม่ชัดเจนว่าสิ่งที่คุณได้รับคือทางออกของ "n ชิ้นที่มีขนาดเท่ากัน" แต่นี่อาจทำให้คุณไปที่นั่นด้วย: x <- 1:10; n <- 3; แยก (x, ตัด (x, n, ป้าย = FALSE))
mdsumner

ทั้งโซลูชันในคำถามและโซลูชันในความคิดเห็นก่อนหน้านี้ไม่ถูกต้องซึ่งอาจไม่ทำงานหากเวกเตอร์มีรายการซ้ำ ลองสิ่งนี้:> foo <- c (ตัวแทน (1, 12), ตัวแทน (2,3), ตัวแทน (3,3)) [1] 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 3 3 3 3> chunk (foo, 2) (ให้ผลลัพธ์ที่ผิด)> chunk (foo, 3) (ผิดด้วย)
mathheadinclouds

(ความคิดเห็นก่อนหน้านี้ดำเนินการต่อ) ทำไม? อันดับ (x) ไม่จำเป็นต้องเป็นจำนวนเต็ม> อันดับ (c (1,1,2,3)) [1] 1.5 1.5 3.0 4.0 นั่นคือสาเหตุที่วิธีการของคำถามล้มเหลว อันนี้ใช้งานได้ (ต้องขอบคุณฮาร์ลานด้านล่าง)> chunk2 <- ฟังก์ชั่น (x, n) แยก (x, ตัด (seq_along (x), n, ป้ายกำกับ = FALSE))
mathheadinclouds

2
> split (foo, cut (foo, 3, labels = FALSE)) (ผิดเช่นกัน)
mathheadinclouds

1
@thathheadinclouds แนะนำข้อมูลตัวอย่างเป็นกรณีพิเศษมาก ตัวอย่างที่กว้างกว่าจะเป็นประโยชน์และทดสอบได้ดีกว่า ตัวอย่างเช่นx <- c(NA, 4, 3, NA, NA, 2, 1, 1, NA ); y <- letters[x]; z <- factor(y)ให้ข้อมูลที่ขาดหายไปค่าซ้ำที่ยังไม่ได้เรียงและอยู่ในชั้นเรียนที่แตกต่างกัน (จำนวนเต็มตัวละครปัจจัย)
Kalin

คำตอบ:


313

ซับหนึ่งแยกออกเป็นชิ้นขนาด 20:

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

รายละเอียดเพิ่มเติม: ผมคิดว่าทั้งหมดที่คุณต้องการseq_along(), split()และ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
คำถามถามnชิ้นที่มีขนาดเท่ากัน nนี้ทำให้คุณไม่ทราบจำนวนชิ้นขนาด ฉันมีปัญหาเดียวกันและใช้วิธีแก้ปัญหาจาก @mathheadinclouds
rrs

4
อย่างที่เราเห็นได้จากผลลัพธ์ของ d1 คำตอบนี้ไม่ได้แยก d ออกเป็นกลุ่มขนาดเท่ากัน (4 สั้นกว่าอย่างเห็นได้ชัด) ดังนั้นจึงไม่ตอบคำถาม
Calimo

9
@rrs: แยก (งเพดาน (seq_along (ง) / (ความยาว (ง) / n)))
gkcn

ฉันรู้ว่ามันค่อนข้างเก่า แต่มันอาจช่วยคนที่สะดุดได้ แม้ว่าคำถามของ OP คือการแบ่งออกเป็นชิ้น ๆ ขนาดเท่ากันถ้าเวกเตอร์เกิดขึ้นไม่ได้เป็นตัวคูณหลายตัวส่วนสุดท้ายก็จะมีขนาดที่แตกต่างกัน ที่จะแยกออกเป็นผมใช้n-chunks max <- length(d)%/%nฉันใช้สิ่งนี้กับเวกเตอร์จำนวน 31 สตริงและรับรายการ 3 เวกเตอร์ของ 10 ประโยคและหนึ่งใน 1 ประโยค
salvu


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

ฉันชอบสิ่งนี้เพราะมันจะให้ชิ้นส่วนที่มีขนาดเท่ากันเท่าที่จะเป็นไปได้ (เหมาะสำหรับการแบ่งงานใหญ่เช่นเพื่อรองรับ RAM ที่ จำกัด หรือเพื่อทำงานในหลายเธรด)
alexvpickering

3
สิ่งนี้มีประโยชน์ แต่โปรดจำไว้ว่านี่จะใช้ได้กับเวกเตอร์ที่เป็นตัวเลขเท่านั้น
Keith Hughitt

@ KeithHughitt สามารถแก้ไขได้ด้วยปัจจัยและคืนค่าระดับเป็นตัวเลข หรืออย่างน้อยนี่คือวิธีที่ฉันใช้มัน
drmariod

20

ลองใช้ฟังก์ชั่น 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

2
นี้ไม่ได้ทำงานสำหรับการแยกขึ้นx, yหรือzกำหนดไว้ในความคิดเห็นนี้ โดยเฉพาะอย่างยิ่งมันเรียงลำดับผลลัพธ์ซึ่งอาจหรืออาจไม่เป็นไรขึ้นอยู่กับแอปพลิเคชัน
Kalin


18

สิ่งนี้จะแยกความแตกต่างกับสิ่งที่คุณมี แต่ก็ยังค่อนข้างเป็นโครงสร้างรายการที่ดีฉันคิดว่า:

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

ซึ่งจะให้สิ่งต่อไปนี้กับคุณโดยขึ้นอยู่กับรูปแบบที่คุณต้องการ:

> 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

ใช้เวลาสองสามนาทีในการตั้งค่าเหล่านี้:

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

จากนั้นเรามีผลลัพธ์ต่อไปนี้:

> 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 

แก้ไข: การเปลี่ยนจาก as.factor () เป็น as.character () ในฟังก์ชั่นของฉันทำให้มันเร็วขึ้นเป็นสองเท่า


13

ตัวแปรอีกสองสามตัวสำหรับกอง ...

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

โปรดทราบว่าคุณไม่จำเป็นต้องใช้factorฟังก์ชั่นที่นี่ แต่คุณยังต้องการsorto / w เวกเตอร์แรกของคุณจะเป็น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

หรือคุณสามารถกำหนดดัชนีตัวละครให้รองตัวเลขด้วยเครื่องหมายขีดซ้ายด้านบน:

> 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

หรือคุณสามารถใช้ชื่อคำธรรมดาที่เก็บไว้ในเวกเตอร์ โปรดทราบว่าการใช้sortเพื่อให้ได้ค่าต่อเนื่องเป็นxตัวอักษรของป้ายกำกับ:

> 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

ใช้ฐาน R 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

และดังที่ได้กล่าวไปแล้วหากคุณต้องการดัชนีเรียงลำดับเพียง:

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 แนะนำด้วย quantile เพื่อสร้างกลุ่มคู่:

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

สิ่งนี้ให้ผลลัพธ์เดียวกันสำหรับตัวอย่างของคุณ แต่ไม่ใช่สำหรับตัวแปรที่เอียง


7

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

อาจจะชัดเจนกว่านี้ แต่มีความคิดเดียวกัน:
split(x,rep(1:n, ceiling(length(x)/n),length.out = length(x)))

ถ้าคุณต้องการให้สั่งให้โยนเรียงรอบ ๆ


6

ฉันต้องการฟังก์ชั่นเดียวกันและได้อ่านคำตอบก่อนหน้านี้แล้ว แต่ฉันก็ต้องมีส่วนที่ไม่สมดุลให้อยู่ท้ายนั่นคือถ้าฉันมี 10 องค์ประกอบเพื่อแยกพวกมันออกเป็นเวกเตอร์ 3 ตัวแต่ละผลลัพธ์ของฉันควรมีเวกเตอร์ 3 ตัว องค์ประกอบ 3,4 ตามลำดับ ดังนั้นฉันจึงใช้สิ่งต่อไปนี้ (ฉันปล่อยให้โค้ดไม่มีการเพิ่มประสิทธิภาพเพื่อให้อ่านได้ไม่เช่นนั้นไม่จำเป็นต้องมีตัวแปรมากมาย):

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

นี่คือตัวแปรอื่น

หมายเหตุ: ด้วยตัวอย่างนี้คุณกำลังระบุ CHUNK SIZE ในพารามิเตอร์ที่สอง

  1. ชิ้นส่วนทั้งหมดมีรูปแบบเหมือนกันยกเว้นชิ้นสุดท้าย
  2. ความประสงค์สุดท้ายที่เลวร้ายที่สุดจะเล็กกว่าไม่ใหญ่กว่าขนาดก้อน

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

ฟังก์ชั่นง่าย ๆ สำหรับการแยกเวกเตอร์ด้วยการใช้ดรรชนี - ไม่ต้องยุ่งยากอะไรมากไปกว่านี้

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

หากคุณไม่ชอบsplit() และไม่ชอบmatrix()(ด้วย NA ที่ห้อยต่องแต่ง) มีสิ่งนี้:

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

เช่นเดียวกับsplit()มันส่งคืนรายการ แต่ไม่ต้องเสียเวลาหรือเว้นวรรคด้วยฉลากดังนั้นจึงอาจมีประสิทธิภาพมากกว่า



2

หากคุณไม่ชอบsplit()และคุณไม่รังเกียจที่จะขยายส่วนหางสั้นของคุณ:

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

คอลัมน์ของเมทริกซ์ที่ส่งคืน ([, 1: ncol]) เป็นหุ่นที่คุณต้องการ


2

ฉันต้องการฟังก์ชั่นที่รับอาร์กิวเมนต์ของ data.table (ในเครื่องหมายคำพูด) และอาร์กิวเมนต์อื่นที่เป็นขีด จำกัด สูงสุดของจำนวนแถวในชุดย่อยของ data.table เดิม ฟังก์ชั่นนี้สร้าง data.tables จำนวนเท่าใดก็ได้ที่ขีด จำกัด สูงสุดอนุญาตให้:

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

ฟังก์ชั่นนี้ให้ชุดข้อมูล data.tables ชื่อ df_ [number] กับแถวเริ่มต้นจาก data.table ดั้งเดิมในชื่อ data.table ล่าสุดอาจสั้นและเต็มไปด้วย NAs ดังนั้นคุณต้องเซ็ตย่อยที่กลับไปเป็นข้อมูลใด ๆ ที่เหลืออยู่ ฟังก์ชันประเภทนี้มีประโยชน์เนื่องจากซอฟต์แวร์ GIS บางตัวมีข้อ จำกัด เกี่ยวกับจำนวนหมุดที่อยู่ที่คุณสามารถนำเข้าได้ตัวอย่างเช่น ดังนั้นการแบ่ง data.tables เป็นชิ้นเล็ก ๆ อาจไม่แนะนำ แต่อาจหลีกเลี่ยงไม่ได้


2

ขออภัยถ้าคำตอบนี้มาสาย แต่อาจมีประโยชน์สำหรับคนอื่น อันที่จริงมีวิธีแก้ปัญหาที่มีประโยชน์มากสำหรับปัญหานี้อธิบายในตอนท้ายของการแยก

> 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
สิ่งนี้จะแตกถ้ามีจำนวนค่าไม่เท่ากันในแต่ละกลุ่ม!
Matifou

2

ความเป็นไปได้อีกอย่างก็คือsplitIndicesฟังก์ชั่นจากแพ็คเกจparallel:

library(parallel)
splitIndices(20, 3)

ให้:

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

ว้าวคำถามนี้มีแรงฉุดมากกว่าที่คาดไว้

ขอบคุณสำหรับความคิดทั้งหมด ฉันคิดวิธีแก้ปัญหานี้แล้ว:

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"

กุญแจสำคัญคือการใช้พารามิเตอร์ seq (แต่ละ = chunk.size) เพื่อให้มันทำงาน การใช้ seq_along ทำหน้าที่เหมือนอันดับ (x) ในโซลูชันก่อนหน้าของฉัน แต่จริงๆแล้วสามารถสร้างผลลัพธ์ที่ถูกต้องด้วยรายการที่ซ้ำกัน


สำหรับผู้ที่เกี่ยวข้องนั้นตัวแทน (seq_along (x), แต่ละ = elements.per.chunk) อาจมีแรงตึงเกินไปในหน่วยความจำ: ใช่ คุณสามารถลองใช้ข้อเสนอแนะก่อนหน้านี้ที่แก้ไขแล้วของฉันได้: chunk <- function (x, n) split (x, factor (seq_along (x) %% n n))
Sebastian

0

สิ่งนี้แยกเป็นชิ้นขนาด⌊n / k⌋ + 1 หรือ⌊n / k⌋และไม่ใช้การเรียงลำดับ 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))
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.