【问题标题】:R: t test over multiple columns using t.test functionR:使用 t.test 函数对多列进行 t 测试
【发布时间】:2018-08-01 03:42:58
【问题描述】:

我尝试对数据框的许多列执行独立 t 检验。例如,我创建了一个数据框

set seed(333)
a <- rnorm(20, 10, 1)
b <- rnorm(20, 15, 2)
c <- rnorm(20, 20, 3)
grp <- rep(c('m', 'y'),10)
test_data <- data.frame(a, b, c, grp)

为了运行测试,我使用了with(df, t.test(y ~ group))

with(test_data, t.test(a ~ grp))
with(test_data, t.test(b ~ grp))
with(test_data, t.test(c ~ grp))

我想要这样的输出

mean in group m mean in group y  p-value
9.747412        9.878820         0.6944
15.12936        16.49533         0.07798 
20.39531        20.20168         0.9027

我想知道如何使用 1.for loop 2.apply() 3. 或许dplyr

此链接R: t-test over all columns 是相关的,但它已有 6 年历史。也许有更好的方法来做同样的事情。

【问题讨论】:

    标签: r loops apply


    【解决方案1】:

    使用select_if 仅选择数字列,然后使用purrr:map_dft.test 应用于grp。最后使用broom:tidy得到整齐格式的结果

    library(tidyverse)
    
    res <- test_data %>% 
      select_if(is.numeric) %>%
      map_df(~ broom::tidy(t.test(. ~ grp)), .id = 'var')
    res
    #> # A tibble: 3 x 11
    #>   var   estimate estimate1 estimate2 statistic p.value parameter conf.low
    #>   <chr>    <dbl>     <dbl>     <dbl>     <dbl>   <dbl>     <dbl>    <dbl>
    #> 1 a       -0.259      9.78      10.0    -0.587   0.565      16.2    -1.19
    #> 2 b        0.154     15.0       14.8     0.169   0.868      15.4    -1.78
    #> 3 c       -0.359     20.4       20.7    -0.287   0.778      16.5    -3.00
    #> # ... with 3 more variables: conf.high <dbl>, method <chr>,
    #> #   alternative <chr>
    

    reprex package (v0.2.1.9000) 于 2019 年 3 月 15 日创建

    【讨论】:

    • 这看起来非常有效,但是我收到以下错误:Error in eval(predvars, data, env) : object 'group 未找到。我认为这可能是因为group 不是数字,但我在这里看到grp 也不是数字。 @Tung 知道这是为什么吗?我有一个包含 1 个组列和 12 个数字列的数据集 - 看起来与我类似。
    • 你能用可重现的代码和数据提出一个单独的问题,然后在这里发布链接吗? How to make a great R reproducible example?
    【解决方案2】:

    只需从t.test 调用中提取估计p-value 结果,同时使用sapply 遍历所有需要的列。从字符向量构建公式并使用t() 转置输出:

    formulas <- paste(names(test_data)[1:(ncol(test_data)-1)], "~ grp")
    
    output <- t(sapply(formulas, function(f) {      
      res <- t.test(as.formula(f))
      c(res$estimate, p.value=res$p.value)      
    }))
    

    输入数据(为重现性播种)

    set.seed(333)
    a <- rnorm(20, 10, 1)
    b <- rnorm(20, 15, 2)
    c <- rnorm(20, 20, 3)
    grp <- rep(c('m', 'y'),10)
    test_data <- data.frame(a, b, c, grp)
    

    输出结果

    #         mean in group m mean in group y   p.value
    # a ~ grp        9.775477        10.03419 0.5654353
    # b ~ grp       14.972888        14.81895 0.8678149
    # c ~ grp       20.383679        20.74238 0.7776188
    

    【讨论】:

    • 令人惊讶的是,它有效,请参阅我对@DaWassi 答案的评论。显然,当索引 R 时没有考虑索引值0。但更正确的是names(test_data)[1:(ncol(test_data)-1)]
    【解决方案3】:

    正如您要求的 for 循环:

      a <- rnorm(20, 10, 1)
      b <- rnorm(20, 15, 2)
      c <- rnorm(20, 20, 3)
      grp <- rep(c('m', 'y'),10)
      test_data <- data.frame(a, b, c, grp)  
    
      meanM=NULL
      meanY=NULL
      p.value=NULL
    
      for (i in 1:(ncol(test_data)-1)){
        meanM=as.data.frame(rbind(meanM, t.test(test_data[,i] ~ grp)$estimate[1]))
        meanY=as.data.frame(rbind(meanY, t.test(test_data[,i] ~ grp)$estimate[2]))
        p.value=as.data.frame(rbind(p.value, t.test(test_data[,i] ~ grp)$p.value))
       }
    
      cbind(meanM, meanY, p.value)
    

    它有效,但我是 R 的初学者。所以也许有更有效的解决方案

    【讨论】:

    • 1:ncol(test_data)-1 是向量 0 1 2 3。您缺少括号:1:(ncol(test_data)-1).
    【解决方案4】:

    使用lapply 这很容易。
    在创建数据集之前,我已经使用set.seed(7060) 测试了代码,以使结果可重现。

    tests_list <- lapply(letters[1:3], function(x) t.test(as.formula(paste0(x, "~ grp")), data = test_data))
    
    result <- do.call(rbind, lapply(tests_list, `[[`, "estimate"))
    pval <- sapply(tests_list, `[[`, "p.value")
    result <- cbind(result, p.value = pval)
    
    result
    #     mean in group m mean in group y   p.value
    #[1,]        9.909818        9.658813 0.6167742
    #[2,]       14.578926       14.168816 0.6462151
    #[3,]       20.682587       19.299133 0.2735725
    

    请注意,现实生活中的应用程序会在第一条lapply 指令中使用names(test_data)[1:3],而不是letters[1:3]

    【讨论】:

    • 我的答案几乎相同,但有多个 apply 调用。当迭代同一个对象时,tests_list,都在同一个方法下。
    • @Parfait 是的,我的想法是首先创建一个包含所有 t.test 结果的对象,然后使用该对象提取所需的内容。
    猜你喜欢
    • 2012-09-13
    • 2020-05-27
    • 1970-01-01
    • 1970-01-01
    • 2016-11-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-24
    • 2015-03-23
    相关资源
    最近更新 更多