我如何在 R 中编写测试以便它们打印得很好?

How do I program tests in R so that they print nicely?

R 中的统计测试生成列表,但是当您调用测试时,这些列表的打印提供了一个特殊的用户友好结构来协助 reader。要了解我在说什么,请考虑使用 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 的输出?如果我创建一个统计测试的输出列表(例如,像上面那样),我如何设置对象以这种方式打印?

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

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 的 follow-up 问题:

"Each" class的数据有打印方法。正如我在回答中概述的那样,print 函数查看 TEST,因为它是 htest 的 class,它使用 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

这个答案是根据其他用户的有用评论和答案汇总而成的,但我想在这里给出一个详细的答案,使事情更加明确,以使那些还不熟悉其中一些问题的用户受益。 t.test函数创建的对象是全局环境中classhtest, and this type of object has a special method of printing under the print.htest设置的对象。该打印方法从列表中提取信息,但以您在问题输出中看到的 user-friendly 方式打印。

如果您想为自己编写的新统计测试复制这种类型的打印,那么您将需要构建新测试,以便它输出一个 htest 对象,其中包含所需的元素的列表,以及所需的 class。这是另一个 answer where a hypothesis test set out in 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 的列表。在代码中包含命令 class(TEST) <- "htest" 很重要,这样创建的对象就不仅仅是一个常规列表。包含该命令将确保输出对象是正确的 class,因此它将以 user-friendly 方式打印。为了看到这一点,我们可以生成一些数据并应用测试:

#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

这里我们看到我们新创建的 hypothesis-testing 函数为我们提供了与 t.test 具有相似 user-friendly 结构的输出。在此示例中,我们为测试方法和测试元素指定了不同的名称,这些名称在打印时出现在描述性输出中。