将向量返回函数应用于按多个因素分组的 data.frame

Apply a vector-returning function to a data.frame grouping by several factors

这是我的数据框示例

charact_fraction    pure_charact    sample  replicate   identity
0.08348135  clean   An006   1   70
0.078947368 clean   An006   1   70
0.090277778 clean   An006   1   70
0.044399596 clean   An006   2   70
0   clean   An006   2   70
0.049348869 clean   An006   2   70
0.218818381 mixed   An011   1   70
0.112068966 mixed   An011   1   70
1   pure    An011   1   70
0   clean   An011   2   70
0.214285714 mixed   An011   2   70
0.2180937   mixed   An011   2   70

我想分箱 charact_fraction 并计算按多个因素分组的分箱频率。生成的数据框应该如下所示

bin_frequency   bin sample  replicate   identity
…   0-0.1   An006   1   70
…   …   …   …   …
…   0.9-1.0 An006   1   70
…   0-0.1   An011   1   70
…   …   …   …   …
…   0.9-1.0 An011   1   70
…   …   …   …   …

我有 return bin 频率的功能。

get_freqs <- function(dat_vector, breaks) {
    hist(dat_vector, breaks=breaks, include.lowest=TRUE, plot=FALSE)$counts
}

我可以生成垃圾箱。

breaks=seq(0,1,by=0.1)
bins = paste(breaks, breaks[-1], sep="-")
bins = bins[-length(ranges)]

我相信这是我目前为止最接近的镜头,但显然与期望的输出相去甚远:

with(df, tapply(charact_part, list(sample, replicate, identity), get_freqs, breaks=breaks))

我有非常难看的 Python 代码来完成这件事,但我想在 R 中有一些更清晰和实用的东西。提前谢谢你。

cut 可能是要走的路:

x <- gsub("\[|\]|\(", "", cut(df$charact_fraction, seq(0,1, .1), include.lowest=T))
df$range <- gsub(",", "-", x)
df
#    charact_fraction pure_charact sample replicate identity   range
# 1        0.08348135        clean  An006         1       70   0-0.1
# 2        0.07894737        clean  An006         1       70   0-0.1
# 3        0.09027778        clean  An006         1       70   0-0.1
# 4        0.04439960        clean  An006         2       70   0-0.1
# 5        0.00000000        clean  An006         2       70   0-0.1
# 6        0.04934887        clean  An006         2       70   0-0.1
# 7        0.21881838        mixed  An011         1       70 0.2-0.3
# 8        0.11206897        mixed  An011         1       70 0.1-0.2
# 9        1.00000000         pure  An011         1       70   0.9-1
# 10       0.00000000        clean  An011         2       70   0-0.1
# 11       0.21428571        mixed  An011         2       70 0.2-0.3
# 12       0.21809370        mixed  An011         2       70 0.2-0.3

如果您也想要计数,可以添加:

lst <- lapply(split(df, df$sample), function(x) {
  within(x, count <- table(range)[match(range, names(table(range)))])
}) 
`rownames<-`(do.call(rbind, lst), NULL)
#    charact_fraction pure_charact sample replicate identity   range count
# 1        0.08348135        clean  An006         1       70   0-0.1     6
# 2        0.07894737        clean  An006         1       70   0-0.1     6
# 3        0.09027778        clean  An006         1       70   0-0.1     6
# 4        0.04439960        clean  An006         2       70   0-0.1     6
# 5        0.00000000        clean  An006         2       70   0-0.1     6
# 6        0.04934887        clean  An006         2       70   0-0.1     6
# 7        0.21881838        mixed  An011         1       70 0.2-0.3     3
# 8        0.11206897        mixed  An011         1       70 0.1-0.2     1
# 9        1.00000000         pure  An011         1       70   0.9-1     1
# 10       0.00000000        clean  An011         2       70   0-0.1     1
# 11       0.21428571        mixed  An011         2       70 0.2-0.3     3
# 12       0.21809370        mixed  An011         2       70 0.2-0.3     3

只需使用 table:

with( dfrm, table( cut( charact_function, breaks=10, include.lowest=TRUE),
       sample, replicate, identity) )

您也可以使用 breaks=breaks,但我只是想演示该参数的不同用法...稍微更紧凑。

这是一个 4 向分类,尽管您可能需要三个双向分类,在这种情况下它是:

cat_char_func <- cut( charact_function, breaks=10, include.lowest=TRUE)
sapply( dfrm[ , c('sample', 'replicate', 'identity')], 
                    function(cat) { table( cat_char_func, cat) }
        )          

cut()ddply() 来自 'plyr' 的组合应该会为您提供一个数据框,其中包含您感兴趣的各种子集的频率。所以像:

library(plyr)
df$bin <- cut(df$charact_fraction, seq(0, 1, 0.1), include.lowest=TRUE)
df$obs <- 1  # Makes counting easy in next step
xtabs <- ddply(df, .(bin, sample, replicate, identity), summarise,
    frequency = sum(obs))

在此处使用 ddply 的一个潜在缺点是生成的数据框将不包含具有零观测值的子集。如果这是一个问题,您可以创建一个完整的矩阵,合并观察到的频率,然后将 NA 替换为 0,如下所示:

xtabs.grid <- with(df, expand.grid(bin = unique(bins), sample = unique(sample),
  replicate = unique(replicate), identity = unique(identity)))
xtabs.full <- merge(xtabs.grid, xtabs, all.x = TRUE)
xtabs.full[is.na(xtabs.full)] <- 0

请注意,为了使合并顺利进行,给 expand.grid() 的变量名称需要与 ddply() 在上一步中生成的变量名称相匹配。

附录:这是一个使用 'dplyr' 函数和管道一次完成所有这些的版本:

df2 <- df %>%
  mutate(bin = cut(charact_fraction, seq(0, 1, 0.1), include.lowest=TRUE)) %>%
  count(bin, sample, replicate, identity) %>%
  left_join(with(df, expand.grid(bin=levels(cut(charact_fraction, seq(0, 1, 0.1), include.lowest=TRUE)), sample=unique(sample), replicate=unique(replicate), identity=unique(identity))), .) %>%
  mutate(n = ifelse(is.na(n)==FALSE, n, 0))