如何按行将数据帧拆分为 n 个块,应用函数并组合?

how do I split a dataframe by row into chunks of n, apply a function and combine?

我有一个 data.frame 的 130,209 行。

> head(dt)

              mLow1 mHigh1 mLow2 mHigh2 meanLow meanHigh        fc     mean
     A_00001  37.00  12.75 99.25  78.50  68.125   45.625 1.4931507  56.8750
     A_00002  31.00  21.50 84.75  53.00  57.875   37.250 1.5536913  47.5625
     A_00003  72.50  26.50 81.75  74.75  77.125   50.625 1.5234568  63.8750

我想将 data.frame 拆分为 12,在 fc 列上应用 scale 函数,然后将其合并。这里没有分组变量,否则我会使用ddply。另外,因为 130,209 不能被 12 完全整除,所以得到的 data.frames 将是不平衡的,即 11 data.frame 将有 10,851 行,最后一个将有 10,848 行,但这没关系。

那么如何将 data.frame 按行拆分为 n 个块(在本例中为 12 个),应用一个函数,然后将它们组合在一起?非常感谢任何帮助。

更新: 使用两个顶级解决方案,我得到不同的结果: 使用@Ben Bolker 的解决方案,

mLow1 mHigh1 mLow2 mHigh2          UID       gene_id meanLow meanHigh mean         fc
  1.5   3.25     1   1.25 MGLibB_00021 0610010K14Rik    1.25     2.25 1.75 -0.5231249
  1.5   3.25     1   1.25 MGLibA_00034 0610037L13Rik    1.25     2.25 1.75 -0.5231249
  1.5   3.25     1   1.25 MGLibB_00058 1100001G20Rik    1.25     2.25 1.75 -0.5231249
  1.5   3.25     1   1.25 MGLibA_00061 1110001A16Rik    1.25     2.25 1.75 -0.5231249
  1.5   3.25     1   1.25 MGLibA_00104 1110034G24Rik    1.25     2.25 1.75 -0.5231249
  1.5   3.25     1   1.25 MGLibA_00110 1110038F14Rik    1.25     2.25 1.75 -0.5231249

使用@MichaelChirico 的回答:

mLow1 mHigh1 mLow2 mHigh2          UID       gene_id meanLow meanHigh mean        fc  fc_scaled
  1.5   3.25     1   1.25 MGLibB_00021 0610010K14Rik    1.25     2.25 1.75 0.5555556 -0.5089608
  1.5   3.25     1   1.25 MGLibA_00034 0610037L13Rik    1.25     2.25 1.75 0.5555556 -0.5089608
  1.5   3.25     1   1.25 MGLibB_00058 1100001G20Rik    1.25     2.25 1.75 0.5555556 -0.5089608
  1.5   3.25     1   1.25 MGLibA_00061 1110001A16Rik    1.25     2.25 1.75 0.5555556 -0.5089608
  1.5   3.25     1   1.25 MGLibA_00104 1110034G24Rik    1.25     2.25 1.75 0.5555556 -0.5089608
  1.5   3.25     1   1.25 MGLibA_00110 1110038F14Rik    1.25     2.25 1.75 0.5555556 -0.5089608

ggplot2 有一个 cut_number() 方便的功能可以为你做这件事。如果您不想加载该包的开销,您可以查看 ggplot2:::breaks 以了解必要的逻辑。

从@MichaelChirico 窃取的可重现示例:

set.seed(100)
KK<-130209L; nn<-12L
library("dplyr")
dt <- data.frame(mLow1=rnorm(KK),mHigh1=rnorm(KK),
               mLow2=rnorm(KK),mHigh2=rnorm(KK),
               meanLow=rnorm(KK),meanHigh=rnorm(KK),
               fc=rnorm(KK),mean=rnorm(KK)) %>% arrange(mean)

对那些不喜欢管道的人表示歉意:

library("ggplot2")  ## for cut_number()
dt %>% mutate(grp=cut_number(mean,12)) %>%
       group_by(grp) %>%
       mutate(fc=c(scale(fc))) %>%
       ungroup() %>%        
       select(-grp) %>%     ## drop grouping variable
       as.data.frame -> dt2 ## convert back to data frame, assign result

事实证明 scale() 周围的 c() 是必要的——否则 fc 变量最终会带有一些混淆 tail() 的属性 ...

同样的逻辑也应该适用于使用 plyr 或基础 R split-apply-combine(关键是使用 cut_number() 来定义分组变量)。

使用 data.table,您可以:

library(data.table)
setDT(dt)[,scale(fc),by=rep(1:nn,each=ceiling(KK/nn),length.out=KK)]

此处,KK为130,209,nn为12。可重现数据:

set.seed(100)
KK<-130209L; nn<-12L
dt<-data.frame(mLow1=rnorm(KK),mHigh1=rnorm(KK),
               mLow2=rnorm(KK),mHigh2=rnorm(KK),
               meanLow=rnorm(KK),meanHigh=rnorm(KK),
               fc=rnorm(KK),mean=rnorm(KK))

因此无需拆分数据并重新组合。

如果您想将其添加到数据框中而不是仅仅提取它,您可以使用 := 运算符通过引用分配:

setDT(dt)[,fc_scaled:=scale(fc)...]

我不确定 dt 的结构有那么重要(如果您没有使用它的任何内部值来进行拆分)。这有帮助吗?

 spl.dt <- split( dt , cut(1:nrow(dt), 12) )

 lapply( spl.dt, my_fun)