如何通过保持id完好无损将df拆分成更小的部分

时间:2015-02-01 20:17:27

标签: r

如何将大型数据框拆分为约的较小部分。相等的长度使得一些具有相同id的行不会落入下一部分。

这里有一些玩具数据:

x <- data.frame(id=c(rep(1,3),rep(2,2),rep(3,3),rep(4,2)),r1=rep(1,10),r2=rep(2,10))

那么如何将上面的df分成约。相同大小的df(df&#39; s列表),以便id保持不变?

所需的输出:here we split x into approx. 3 equal parts

[1] 
    id r1 r2 
 1   1  1  2 
 2   1  1  2 
 3   1  1  2 

[2]
     id r1 r2 
 4   2  1  2 
 5   2  1  2 
 9   4  1  2 
 10  4  1  2

[3]
    id r1 r2 
 6   3  1  2 
 7   3  1  2 
 8   3  1  2 

编辑1 我们假设我们将x分开,以便每个部分包含约。 3行原始df。所以这就是我不想要的:

seqrow <- seq(1,nrow(x),3)
splts <- rep_len(1:length(seqrow), nrow(x))
lstdf <- split(x, f=splts)

lstdf
$`1`
  id r1 r2
1  1  1  2
5  2  1  2
9  4  1  2

$`2`
   id r1 r2
2   1  1  2
6   3  1  2
10  4  1  2

$`3`
  id r1 r2
3  1  1  2
7  3  1  2

$`4`
  id r1 r2
4  2  1  2
8  3  1  2

所以我们这里有最大值。每个df有3行,但我们看到id's分散在每个部分上。

1 个答案:

答案 0 :(得分:1)

我假设您估计每个子集中的预期行数,即bin。

以下代码开始填充垃圾箱,从最大的id-subsets开始。然后连续地将仍然适合的id-subsets添加到bin中,直到达到bin中的最大行数。

x <- data.frame(id=c(rep(1,2),rep(2,2),rep(3,3),rep(4,2)),r1=rep(1,9),r2=rep(2,9))

splitDfId <- function(
    ### Split dataframe into subsets, 
    ### keeping rows with same identifier together
    x               ##<< dataframe to split
    ,id             ##<< factor so that lines of equal level are grouped into the same subsets
    ,maxBinSize=4   ##<< number of rows in each subset
){
    dfCntBin <- data.frame(cnt=sort(table(id), decreasing =TRUE), bin=0L )
    # bins must be as big as the maximum number of equal level
    maxBinSize <- max( dfCntBin$cnt, maxBinSize)  
    #
    toBin <- 1:nrow(dfCntBin)
    while( length(toBin) > 0 ){
        binNr <- max(dfCntBin$bin)+1
        binCnt <- 0
        # first entry in toSort is the first matching entry
        # matching: still suiting into the bin
        matchFirst <- 1
        while( !is.na(matchFirst) ){
            i <- toBin[matchFirst]
            dfCntBin$bin[i] <- binNr
            toBin <- toBin[-matchFirst]
            binCnt <- binCnt + dfCntBin$cnt[i]
            freeCnt <- maxBinSize - binCnt
            matchFirst <- if(freeCnt==0) NA else {
                which( dfCntBin$cnt[toBin] <= freeCnt)[1]}
        }
    }
    #
    dfCntBin$id <- rownames(dfCntBin)
    xBin <- merge(x, dfCntBin)
    ##value<< a list of subsets of the data frame
    split(x, xBin$bin)
}

splitDfId(x, x$id)
splitDfId(x, x$id, 6)
splitDfId(x, x$id, 3)

如果性能有问题,那么搜索第一个匹配的子集:which(dfCntBin$cnt[toBin] <= freeCnt)[1]仍然可以是speeded up