Что R предполагает относительно порядка в парном t-тесте?

В сигнатуре без формулы t.test функции t.test(x, y, paired=T) я предполагаю, что данные считаются парными, как они упорядочены в двух входах (x и y в документации).

Однако в сигнатуре формулы t.test(values ~ groups, df, paired=T), как функция связывает наблюдения в двух группах как пары? По порядку?

В представлении ниже я создаю фрейм данных с парными данными до и после. Затем я помещаю его в развернутую форму (подходящую для функции t.test) двумя способами: 1) список перед группой в порядке наблюдения, затем список за группой в порядке наблюдения. 2) перечислите все данные в произвольном порядке.

Я провожу парный t-тест для обоих наборов данных. Совершенно очевидно, что в случае 2 функция не имеет абсолютно никакого способа узнать, что после наблюдения идет с каким до наблюдения. Могу ли я предположить, что функция t.test понимает данные, введенные в случае 1, то есть данные до и после находятся в порядке наблюдения?

Я не смог найти ничего об этом ни в документации, ни в каких-либо примерах в Интернете. Поскольку нет аргумента для ключа, который связывает наблюдения в двух группах, функция t.test делает какое-то предположение.

library(tidyverse)

df = data.frame(
  observation = 1:20,
  before = rnorm(20, 10, 2),
  after = rnorm(20, 10.2, 2.3)
)

print.data.frame(df)
#>    observation    before     after
#> 1            1 10.930157 11.818216
#> 2            2 10.870749 10.699232
#> 3            3  9.603120 14.384484
#> 4            4  9.615291  8.777045
#> 5            5  6.714043  9.506421
#> 6            6  9.063117  5.574887
#> 7            7  8.152260 10.357455
#> 8            8  8.256237  8.660646
#> 9            9 12.641977  7.511760
#> 10          10 11.010290  9.391047
#> 11          11 12.545197  9.072856
#> 12          12 12.606526  9.110687
#> 13          13  8.659088 12.445071
#> 14          14  8.958959 10.783168
#> 15          15 11.635443  6.926802
#> 16          16  6.922437 12.419453
#> 17          17 10.326176 10.416757
#> 18          18  7.680960  9.836573
#> 19          19  9.458365  8.083777
#> 20          20  7.235837 12.094290

df_long = 
  df %>% 
  pivot_longer(
    cols = c("before", "after"),
    names_to = "time", 
    values_to="fabulousness"
  )

print.data.frame(df_long)
#>    observation   time fabulousness
#> 1            1 before    10.930157
#> 2            1  after    11.818216
#> 3            2 before    10.870749
#> 4            2  after    10.699232
#> 5            3 before     9.603120
#> 6            3  after    14.384484
#> 7            4 before     9.615291
#> 8            4  after     8.777045
#> 9            5 before     6.714043
#> 10           5  after     9.506421
#> 11           6 before     9.063117
#> 12           6  after     5.574887
#> 13           7 before     8.152260
#> 14           7  after    10.357455
#> 15           8 before     8.256237
#> 16           8  after     8.660646
#> 17           9 before    12.641977
#> 18           9  after     7.511760
#> 19          10 before    11.010290
#> 20          10  after     9.391047
#> 21          11 before    12.545197
#> 22          11  after     9.072856
#> 23          12 before    12.606526
#> 24          12  after     9.110687
#> 25          13 before     8.659088
#> 26          13  after    12.445071
#> 27          14 before     8.958959
#> 28          14  after    10.783168
#> 29          15 before    11.635443
#> 30          15  after     6.926802
#> 31          16 before     6.922437
#> 32          16  after    12.419453
#> 33          17 before    10.326176
#> 34          17  after    10.416757
#> 35          18 before     7.680960
#> 36          18  after     9.836573
#> 37          19 before     9.458365
#> 38          19  after     8.083777
#> 39          20 before     7.235837
#> 40          20  after    12.094290

df_long_not_paired = 
  df_long %>% 
  arrange(fabulousness)

print.data.frame(df_long_not_paired)
#>    observation   time fabulousness
#> 1            6  after     5.574887
#> 2            5 before     6.714043
#> 3           16 before     6.922437
#> 4           15  after     6.926802
#> 5           20 before     7.235837
#> 6            9  after     7.511760
#> 7           18 before     7.680960
#> 8           19  after     8.083777
#> 9            7 before     8.152260
#> 10           8 before     8.256237
#> 11          13 before     8.659088
#> 12           8  after     8.660646
#> 13           4  after     8.777045
#> 14          14 before     8.958959
#> 15           6 before     9.063117
#> 16          11  after     9.072856
#> 17          12  after     9.110687
#> 18          10  after     9.391047
#> 19          19 before     9.458365
#> 20           5  after     9.506421
#> 21           3 before     9.603120
#> 22           4 before     9.615291
#> 23          18  after     9.836573
#> 24          17 before    10.326176
#> 25           7  after    10.357455
#> 26          17  after    10.416757
#> 27           2  after    10.699232
#> 28          14  after    10.783168
#> 29           2 before    10.870749
#> 30           1 before    10.930157
#> 31          10 before    11.010290
#> 32          15 before    11.635443
#> 33           1  after    11.818216
#> 34          20  after    12.094290
#> 35          16  after    12.419453
#> 36          13  after    12.445071
#> 37          11 before    12.545197
#> 38          12 before    12.606526
#> 39           9 before    12.641977
#> 40           3  after    14.384484

df_long_paired = 
  df_long %>% 
  arrange(desc(time))

print.data.frame(df_long_paired)
#>    observation   time fabulousness
#> 1            1 before    10.930157
#> 2            2 before    10.870749
#> 3            3 before     9.603120
#> 4            4 before     9.615291
#> 5            5 before     6.714043
#> 6            6 before     9.063117
#> 7            7 before     8.152260
#> 8            8 before     8.256237
#> 9            9 before    12.641977
#> 10          10 before    11.010290
#> 11          11 before    12.545197
#> 12          12 before    12.606526
#> 13          13 before     8.659088
#> 14          14 before     8.958959
#> 15          15 before    11.635443
#> 16          16 before     6.922437
#> 17          17 before    10.326176
#> 18          18 before     7.680960
#> 19          19 before     9.458365
#> 20          20 before     7.235837
#> 21           1  after    11.818216
#> 22           2  after    10.699232
#> 23           3  after    14.384484
#> 24           4  after     8.777045
#> 25           5  after     9.506421
#> 26           6  after     5.574887
#> 27           7  after    10.357455
#> 28           8  after     8.660646
#> 29           9  after     7.511760
#> 30          10  after     9.391047
#> 31          11  after     9.072856
#> 32          12  after     9.110687
#> 33          13  after    12.445071
#> 34          14  after    10.783168
#> 35          15  after     6.926802
#> 36          16  after    12.419453
#> 37          17  after    10.416757
#> 38          18  after     9.836573
#> 39          19  after     8.083777
#> 40          20  after    12.094290


df_long_not_paired %>%
  t.test(fabulousness ~ time, ., paired=T)
#> 
#>  Paired t-test
#> 
#> data:  fabulousness by time
#> t = 2.0289, df = 19, p-value = 0.05672
#> alternative hypothesis: true difference in means is not equal to 0
#> 95 percent confidence interval:
#>  -0.007878376  0.506318062
#> sample estimates:
#> mean of the differences 
#>               0.2492198

df_long_paired %>% 
  t.test(fabulousness ~ time, ., paired=T)
#> 
#>  Paired t-test
#> 
#> data:  fabulousness by time
#> t = 0.3422, df = 19, p-value = 0.736
#> alternative hypothesis: true difference in means is not equal to 0
#> 95 percent confidence interval:
#>  -1.27509  1.77353
#> sample estimates:
#> mean of the differences 
#>               0.2492198

Создано 24 ноября 2020 г. пакетом REPEX (v0.3.0)

ЗАМЕТКА:

Когда я запускаю это несколько раз, я часто вижу ложные срабатывания в случае, когда я зашифровал групповой порядок.


person abalter    schedule 25.11.2020    source источник
comment
Он вычисляет x-y, которые под нулем должны иметь среднее значение 0   -  person IRTFM    schedule 25.11.2020
comment
Если я правильно понимаю, вы говорите, что предполагается, что каждая группа 1 и группа 2 упорядочены соответствующим образом. Похоже, стоит поместить это в документацию.   -  person abalter    schedule 25.11.2020
comment
Спаривание подразумевает порядок. Нет необходимости констатировать очевидное.   -  person IRTFM    schedule 25.11.2020
comment
При использовании метода формулы для t.test() ответ разделяется по коэффициенту группирования и передается как переменные x и y методу по умолчанию. Итак, да, есть предположение, что первое наблюдение уровня фактора 1 соответствует первому наблюдению уровня фактора 2 и так далее ...   -  person 27 ϕ 9    schedule 25.11.2020
comment
Это не ложное срабатывание, когда вы обнаруживаете разницу после шифрования группового порядка. Вы специально сгенерировали непарные данные с разницей в средних значениях. Для имитации парных данных вы должны сделать что-то вроде before = rnorm(20, 10, 2), а затем after = rnorm(1, before + .2, 2). В качестве альтернативы вы можете выбрать многомерное нормальное распределение и указать корреляцию до и после.   -  person BrianLang    schedule 25.11.2020
comment
Хороший момент @BrianLang. А это говорит о большей вероятности ложноотрицательного результата при взломе.   -  person abalter    schedule 25.11.2020


Ответы (2)


Итак, чтобы узнать, как именно это делается, мы можем посмотреть исходный код.

stats:::t.test.formula дает нам:

g <- factor(mf[[-response]])

где mf - модельный фрейм, а response - переменная ответа. g - это группирующая переменная из вашей формулы (LHS). Затем, позже, мы видим создание объекта DATA, который является разделением mf на основе группирующей переменной g. Затем эти данные передаются в stats:::t.test.default без изменения порядка.

DATA <- setNames(split(mf[[response]], g), c("x", "y"))

Затем мы можем изучить stats:::t.test.default, сосредоточив внимание на том, где paired упоминаются данные.

if (paired) {
      x <- x - y
      y <- NULL
   }
nx <- length(x)
mx <- mean(x)
vx <- var(x) 

Отсюда мы видим, что t.test.default просто вычисляет разницу между парами, а затем выполняет однократный t-тест на разницу.

Из всего этого мы понимаем, что порядок наблюдений должен быть правильным, чтобы иметь правильные пары.

person BrianLang    schedule 25.11.2020
comment
И, судя по всему, их можно зашифровать, если наблюдения в каждой группе в порядке. Например: (a1, a2, b1, a3, a4, b2, b3, b4, b5, a5, b6, ...) - person abalter; 25.11.2020
comment
Да, точно. пока они все в порядке относительно остальной группы, все будет готово. - person BrianLang; 26.11.2020

Чтобы добавить к объяснению кода @BrianLang, парный тест проверяет разницу между вашими образцами и вычисляет разницу по порядку строк. Вы можете проверить это, выполнив:

set.seed(111)

df = data.frame(
  observation = 1:20,
  before = rnorm(20, 10, 2),
  after = rnorm(20, 10.2, 2.3)
) 

t.test(x=df$after,y=df$before,paired=TRUE)

    Paired t-test

data:  df$after and df$before
t = 0.30475, df = 19, p-value = 0.7639
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -1.505079  2.018057
sample estimates:
mean of the differences 
              0.2564887 

Если мы сделаем это с длинными данными:

df_long = 
  df %>% 
  pivot_longer(
    cols = c("before", "after"),
    names_to = "time", 
    values_to="fabulousness"
  )

t.test(fabulousness ~ time,paired=TRUE,data=df_long)

    Paired t-test

data:  fabulousness by time
t = 0.30475, df = 19, p-value = 0.7639
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -1.505079  2.018057
sample estimates:
mean of the differences 
              0.2564887 

Обычно я бы использовал первую формулировку, чтобы избежать всей этой путаницы.

person StupidWolf    schedule 25.11.2020
comment
(обращение к кому-то как к глупому кажется немного странным), второй подход лучше подходит для пайпинга - что я делаю часто. Я вижу обходной путь, при котором я могу расширить столбец группы и сделать что-то вроде df %>% <x and y to columns> %>% t.test(.$x, .$y), но я думаю, что это немного некрасиво. - person abalter; 25.11.2020