【问题标题】:How do I program tests in R so that they print nicely?如何在 R 中编写测试以便它们打印得很好?
【发布时间】:2019-10-13 11:41:25
【问题描述】:

R 中的统计测试会生成列表,但是当您调用测试时,这些列表的打印会提供一个特殊的用户友好结构来帮助读者。要了解我在说什么,请考虑一个使用 stats 包中的 t.test 函数的示例。

#Run a T-test on some example data
X <- c(30, 32, 40, 28, 29, 35, 30, 34, 31, 39);
Y <- c(19, 20, 44, 45, 8, 29, 26, 59, 35, 50);
TEST <- stats::t.test(X,Y);

#Show structure of the TEST object
str(TEST);
List of 9
 $ statistic  : Named num -0.134
  ..- attr(*, "names")= chr "t"
 $ parameter  : Named num 10.2
  ..- attr(*, "names")= chr "df"
 $ p.value    : num 0.896
 $ conf.int   : num [1:2] -12.3 10.9
  ..- attr(*, "conf.level")= num 0.95
 $ estimate   : Named num [1:2] 32.8 33.5
  ..- attr(*, "names")= chr [1:2] "mean of x" "mean of y"
 $ null.value : Named num 0
  ..- attr(*, "names")= chr "difference in means"
 $ alternative: chr "two.sided"
 $ method     : chr "Welch Two Sample t-test"
 $ data.name  : chr "X and Y"
 - attr(*, "class")= chr "htest"

这个对象是一个包含九个元素的列表,其中一些是通过属性命名的。但是,当我打印TEST 对象时,返回的信息的结构与标准的列表打印方式不同。

#Print the TEST object
TEST;

        Welch Two Sample t-test

data:  X and Y
t = -0.13444, df = 10.204, p-value = 0.8957
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -12.27046  10.87046
sample estimates:
mean of x mean of y 
     32.8      33.5 

如您所见,此打印输出比列表的标准打印更加用户友好。我希望能够在R 中编写统计测试,生成与上述类似的输出列表,但以这种用户友好的方式打印。


我的问题: 为什么R 以这种特殊方式打印列表TEST 的输出?如果我创建一个统计测试的输出列表(例如,如上所述),我该如何设置对象以这种方式打印?

【问题讨论】:

  • 查看结果的类,然后查看'methods(print)'。

标签: r pretty-print


【解决方案1】:

这个答案是由有用的 cmets 和其他用户的答案组合而成的,但我想在这里给出一个详细的答案,以使事情更明确,以使那些还不熟悉其中一些问题的用户受益。 t.test函数创建的对象是htest类的对象,这种对象在全局环境下print.htest设置下有特殊的打印方法。该打印方法从列表中提取信息,但以您在问题输出中看到的用户友好方式打印它。

如果您想为自己编写的新统计测试复制这种类型的打印,那么您需要构建新测试,使其输出htest 对象,以及所需的列表元素,和所需的课程。这是另一个answer 的示例,其中Tarone (1979) 中设置的假设检验被编程为htest 对象:

Tarone.test <- function(N, M) {

    #Check validity of inputs
    if(any(M > N)) { stop("Error: Observed count value exceeds binomial trials"); }

    #Set hypothesis test objects
    method      <- "Tarone's Z test";
    alternative <- "greater";
    null.value  <- 0;
    attr(null.value, "names") <- "dispersion parameter";
    data.name   <- paste0(deparse(substitute(M)), " successes from ", 
                          deparse(substitute(N)), " counts");

    #Calculate test statistics
    estimate    <- sum(M)/sum(N);
    attr(estimate, "names") <- "proportion parameter";

    S           <- sum((M - N*estimate)^2/(estimate*(1 - estimate)));
    statistic   <- (S - sum(N))/sqrt(2*sum(N*(N-1))); 
    attr(statistic, "names") <- "z";

    p.value     <- 2*pnorm(-abs(statistic), 0, 1);
    attr(p.value, "names") <- NULL;

    #Create htest object
    TEST        <- list(statistic = statistic, p.value = p.value, estimate = estimate, 
                        null.value = null.value, alternative = alternative, 
                        method = method, data.name = data.name);
    class(TEST) <- "htest";

    TEST; }

在本例中,函数计算htest 对象的所有必需元素,然后将该对象创建为具有该类的列表。在代码中包含命令class(TEST) &lt;- "htest" 很重要,这样创建的对象就不仅仅是一个常规列表。包含该命令将确保输出对象属于正确的类,因此它将以用户友好的方式打印。为了看到这一点,我们可以生成一些数据并应用测试:

#Generate example data
N <- c(30, 32, 40, 28, 29, 35, 30, 34, 31, 39);
M <- c( 9, 10, 22, 15,  8, 19, 16, 19, 15, 10);

#Apply Tarone's test to the example data
TEST <- Tarone.test(N, M);
TEST;

        Tarone's Z test

data:  M successes from N counts
z = 2.5988, p-value = 0.009355
alternative hypothesis: true dispersion parameter is greater than 0
sample estimates:
proportion parameter 
           0.4359756

在这里,我们看到我们新创建的假设检验函数为我们提供了与t.test 具有相似用户友好结构的输出。在此示例中,我们为测试方法和测试元素赋予了不同的名称,这些名称在打印时出现在描述性输出中。

【讨论】:

  • 这不是我的反对意见,但我明白为什么会这样。您正在制作一个所谓的自定义测试 (MY.TEST),它的变量(输出)为 t.test。你没看到吗?您刚刚再次创建了t.test,但现在,您不是自动创建输出结构,而是手动将它们放入htest。那有什么意义呢?如果您可以想到一个统计测试,稍后它的结果可以用作您的函数的输入,您会在答案中将其作为示例展示,这样您就可以避免进一步的否决。老实说,我扭动自己的手臂不点击那个向下箭头。干杯。
  • 它不需要和t.test有相同的输出变量。这可能是一个完全不同的假设检验。我已经在指定的链接上给出了一个不同测试的例子(再次给出here)。重点是允许您创建一种不同类型的假设检验,但仍将其框定为htest 对象,以便打印得很好。 (这些输出是htest 对象的强制输出。它们不特定于t.test。)
  • 好吧,我会在问题中包含您再次与我分享的链接中的一些信息(而不是作为链接),以便匆忙接近一天结束的人们将获得更多洞察力;)
【解决方案2】:

使用以下最能满足您需求的方法之一。

X <- c(30, 32, 40, 28, 29, 35, 30, 34, 31, 39)
Y <- c(19, 20, 44, 45, 8, 29, 26, 59, 35, 50)
TEST <- stats::t.test(X,Y)

#default; printing data of htest class
print(TEST) 

#printing every element of the list
lapply(TEST, print) 
print.listof(TEST)

#printing the results as a dataframe
broom::tidy(TEST) #output of this one is included just for illustration


    # A tibble: 1 x 10
  estimate estimate1 estimate2 statistic p.value parameter conf.low conf.high method                  alternative
     <dbl>     <dbl>     <dbl>     <dbl>   <dbl>     <dbl>    <dbl>     <dbl> <chr>                   <chr>      
1     -0.7      32.8      33.5    -0.134   0.896      10.2    -12.3      10.9 Welch Two Sample t-test two.sided 

解决 OP 的后续问题:

“每个” 类数据都有一种打印方法。正如我在回答中概述的那样,print 函数查看 TEST 并且因为它是 htest 的类,它使用 print.htest

class(TEST)
# [1] "htest"

head(methods(print))
# [1] "print.acf"         "print.AES"         "print.all_vars"    "print.anova"
# [5] "print.anova.lme"   "print.ansi_string"

在我刚打开的 R 会话中,我有 185 种不同的方法。随着您加载库,这个数字会越来越高。

如果你想深入挖掘,那么你需要查看print的源代码,可以在这里找到:R source code on GitHub

【讨论】:

  • 感谢您的回答。虽然这当然很有趣,但它并没有解释为什么 R 会按照默认方式打印 T 检验,或者如何在另一个列表中模拟它。
  • @Ben 确实如此。 “每一类”数据都有一种打印方法。正如我在回答中概述的那样,print 查看TEST,因为它是htest 的类,它使用print.htest。您可以检查您的数据类别class(TEST) 并查看打印methods(print) 的各种方法。如果你想深入挖掘,你需要查看print的源代码,可以在这里找到:github.com/wch/r-source/blob/trunk/src/library/base/R/print.R
猜你喜欢
  • 1970-01-01
  • 2023-01-03
  • 2022-11-15
  • 1970-01-01
  • 2013-04-29
  • 1970-01-01
  • 2020-11-28
  • 1970-01-01
  • 2015-11-08
相关资源
最近更新 更多