使用R对数据子集进行Kruskal-Wallis p值矩阵分析

5
考虑一个数据集Data,其中有几个因子和数值连续变量。其中一些变量,比如slice_by_1(类别为“男”、“女”)和slice_by_2(类别为“悲伤”、“中性”、“快乐”),被用来将数据切分成子集。对于每个子集,应该在由其他因子变量compare_by分组的length、preasure、pulse变量上运行Kruskal-Wallis测试。在R中是否有一种快速的方法完成这项任务并将计算出的p值放入矩阵中?
我使用了dplyr包准备数据。
样例数据集:
library(dplyr)
set.seed(123)
Data <- tbl_df(
   data.frame(
       slice_by_1 = as.factor(rep(c("Male", "Female"), times = 120)),
       slice_by_2 = as.factor(rep(c("Happy", "Neutral", "Sad"), each = 80)),
       compare_by = as.factor(rep(c("blue", "green", "brown"), times = 80)),
       length   = c(sample(1:10, 120, replace=T), sample(5:12, 120, replace=T)),
       pulse    = runif(240, 60, 120),
       preasure = c(rnorm(80,1,2),rnorm(80,1,2.1),rnorm(80,1,3))
   )
   ) %>%
group_by(slice_by_1, slice_by_2)

让我们来看看数据:

Source: local data frame [240 x 6]
Groups: slice_by_1, slice_by_2

   slice_by_1 slice_by_2 compare_by length     pulse     preasure
1        Male      Happy       blue     10  69.23376  0.508694601
2      Female      Happy      green      1  68.57866 -1.155632020
3        Male      Happy      brown      8 112.72132  0.007031799
4      Female      Happy       blue      3 116.61283  0.383769524
5        Male      Happy      green      7 110.06851 -0.717791526
6      Female      Happy      brown      8 117.62481  2.938658488
7        Male      Happy       blue      9 105.59749  0.735831389
8      Female      Happy      green      2  83.44101  3.881268679
9        Male      Happy      brown      5 101.48334  0.025572561
10     Female      Happy       blue     10  62.87331 -0.715108893
..        ...        ...        ...    ...       ...          ...

期望的输出示例:

    Data_subsets    length  preasure     pulse
1     Male_Happy <p-value> <p-value> <p-value>
2   Female_Happy <p-value> <p-value> <p-value>
3   Male_Neutral <p-value> <p-value> <p-value>
4 Female_Neutral <p-value> <p-value> <p-value>
5       Male_Sad <p-value> <p-value> <p-value>
6     Female_Sad <p-value> <p-value> <p-value>
2个回答

4

你已经使用了 group_by,现在只需要执行 do 操作:

Data %>%
    do({
        data.frame(
            Data_subsets=paste(.$slice_by_1[[1]], .$slice_by_2[[1]], sep='_'),
            length=kruskal.test(.$length, .$compare_by)$p.value,
            preasure=kruskal.test(.$preasure, .$compare_by)$p.value,
            pulse=kruskal.test(.$pulse, .$compare_by)$p.value,
            stringsAsFactors=FALSE)
    }) %>%
    ungroup() %>%
    select(-starts_with("slice_"))
## Source: local data frame [6 x 4]
##     Data_subsets    length  preasure     pulse
## 1   Female_Happy 0.4369918 0.1937327 0.8767561
## 2 Female_Neutral 0.3750688 0.8588069 0.2858796
## 3     Female_Sad 0.7958502 0.6274940 0.5801208
## 4     Male_Happy 0.3099704 0.6929493 0.3796494
## 5   Male_Neutral 0.4953853 0.2986860 0.2418708
## 6       Male_Sad 0.7159970 0.8528201 0.5686672

您需要执行ungroup()以删除slice*列,因为group_by列不会被删除(我想说“永远不会被删除”,但我不确定)。


3
我们可以在do内使用Map来执行多列的kruskal.test,然后使用library(tidyr)中的unite将'slice_by_1'和'slice_by_2'列连接到一个单独的列'Data_subsets'中。
library(dplyr)
library(tidyr)
nm1 <- names(Data)[4:6]
f1 <- function(x,y) kruskal.test(x~y)$p.value

Data %>% 
     do({data.frame(Map(f1, .[nm1], list(.$compare_by)))}) %>% 
     unite(Data_subsets, slice_by_1, slice_by_2, sep="_")
#     Data_subsets    length     pulse  preasure
#1   Female_Happy 0.4369918 0.8767561 0.1937327
#2 Female_Neutral 0.3750688 0.2858796 0.8588069
#3     Female_Sad 0.7958502 0.5801208 0.6274940
#4     Male_Happy 0.3099704 0.3796494 0.6929493
#5   Male_Neutral 0.4953853 0.2418708 0.2986860
#6       Male_Sad 0.7159970 0.5686672 0.8528201

我们可以使用 data.table 来完成这个任务。首先将 'data.frame' 转换为 'data.table' (setDT(Data)),然后通过 paste 函数创建分组变量 ('Data_subsets'),分组依据为 'slice_by_1' 和 'slice_by_2' 两列的组合。接着,我们从数据集中选择所需的列,并将其作为输入传递给 Map 函数进行计算。再对结果进行 krusal.test 计算,并提取出 p.value

library(data.table)    
setDT(Data)[, Map(f1, .SD[, nm1, with=FALSE], list(compare_by)) ,
             by = .(Data_subsets= paste(slice_by_1, slice_by_2, sep='_'))]
#     Data_subsets    length     pulse  preasure
#1:     Male_Happy 0.3099704 0.3796494 0.6929493
#2:   Female_Happy 0.4369918 0.8767561 0.1937327
#3:   Male_Neutral 0.4953853 0.2418708 0.2986860
#4: Female_Neutral 0.3750688 0.2858796 0.8588069
#5:       Male_Sad 0.7159970 0.5686672 0.8528201
#6:     Female_Sad 0.7958502 0.5801208 0.6274940

1
很好的使用了 Map,而且我还没有将 unite 合并到我的扫描中,这个知道了不错。 - r2evans
@r2evans 我之前考虑过你的方法,但是如果有更多的列,它会变得重复。 - akrun
当我在使用lapply时陷入了类似的困境(还没有想到mapply),我觉得我应该简化它并发布出来。通常情况下,我更喜欢像你这样的通用方法,而不是我发布的那种方法。我有点懒。 - r2evans

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接