Компактный способ выполнения нескольких парных тестов (например, t-теста) с одной переменной, разделенной на несколько категорий в длинном формате.

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

В моем случае у меня есть одна переменная и несколько групп (в конечном итоге много). Расширение на этот пример:

library(reshape)

# Create a dataset
mu=34
stdv=5
Location=rep(c("Area_A","Area_B","Area_C"),5) 
distro=rnorm(length(Location),mu,stdv) 
id=seq(1:length(Location))
sample_long=data.frame(id,Location,distro)
sample_long
   id Location   distro
1   1   Area_A 34.95737
2   2   Area_B 31.30298
3   3   Area_C 35.86569
4   4   Area_A 40.45378
5   5   Area_B 36.12060
6   6   Area_C 28.29649
7   7   Area_A 30.64495
8   8   Area_B 29.70668
9   9   Area_C 33.22874
10 10   Area_A 25.29148
11 11   Area_B 32.35511
12 12   Area_C 34.69159
13 13   Area_A 26.89791
14 14   Area_B 35.30717
15 15   Area_C 40.64628

Я хотел бы выполнить тесты «все против всех» среди областей, т. е. тест (область_A, область_B), тест (область_A, область_C) и тест (область_B, область_C) (в более общем случае все i<j возможные тесты).

Простой способ — преобразовать данные в широкий формат:

# Reshape to wide format
sample_wide=reshape(sample_long,direction="wide",idvar="id",timevar="Location")
sample_wide
   id distro.Area_A distro.Area_B distro.Area_C
1   1      34.95737            NA            NA
2   2            NA      31.30298            NA
3   3            NA            NA      35.86569
4   4      40.45378            NA            NA
5   5            NA      36.12060            NA
6   6            NA            NA      28.29649
7   7      30.64495            NA            NA
8   8            NA      29.70668            NA
9   9            NA            NA      33.22874
10 10      25.29148            NA            NA
11 11            NA      32.35511            NA
12 12            NA            NA      34.69159
13 13      26.89791            NA            NA
14 14            NA      35.30717            NA
15 15            NA            NA      40.64628

а затем зациклиться на столбцах «все против всех», для которых я видел несколько аппроксимации больше похожи на R, чем следующая, в которой я использую циклы for:

# Now compute the test
test.out=list()
k=0
for(i in 2:(dim(sample_wide)[2]-1)){ # All against  all var groups
  for(j in (i+1):dim(sample_wide)[2]){
    k=k+1
    test.out[[k]]=t.test(sample_wide[,i], 
                    sample_wide[,j]) # store results in a list
  }
}

Но мой вопрос не в том, какое решение является лучшим с учетом широкого формата, а в том, можно ли найти решение проблемы, работающее из исходного длинного формата, в соответствует решениям, найденным для ссылок, которые я предоставил выше, которые используют dplyr, broom и т. д.


person Alf Pascu    schedule 07.08.2019    source источник
comment
Думаю, вы можете gather перевести в "длинный" формат, а затем сделать pairwise.t.test   -  person akrun    schedule 07.08.2019
comment
@akrun Я думаю, что они пытаются начать с длинного формата, который у них уже есть, так что не нужно собирать   -  person camille    schedule 07.08.2019
comment
если ваш тест является t.test, вы, вероятно, захотите сделать постфактум-тест Тьюки: r TukeyHSD(aov(distro ~ Location, data=sample_long)) Тогда вам не нужно зацикливаться или что-то еще, просто вызовите функцию.   -  person ssayols    schedule 07.08.2019
comment
Хороший вопрос @ssayols, но моя цель здесь больше узнать, какие стили программирования больше подходят для каких задач в R, возможно, мне следует отредактировать заголовок, чтобы убрать акцент на тесте? Это был именно тот способ, которым я закончил думать над этой проблемой.   -  person Alf Pascu    schedule 07.08.2019


Ответы (2)


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

library(dplyr)
library(tidyr)
library(purrr)

set.seed(111)
# same data creation code

grps <- as.data.frame(t(combn(levels(sample_long$Location), 2))) %>%
  mutate(pair = row_number()) %>%
  gather(key, value = loc, -pair) %>%
  select(-key)

grps
#>   pair    loc
#> 1    1 Area_A
#> 2    2 Area_A
#> 3    3 Area_B
#> 4    1 Area_B
#> 5    2 Area_C
#> 6    3 Area_C

Присоединение поиска к фрейму данных удваивает строки — они будут различаться в зависимости от того, сколько уровней вы объединяете. Обратите также внимание, что я удалил столбец с вашим идентификатором, так как сейчас он не казался необходимым. Вложите, проведите t-тест и упорядочите результаты.

sample_long %>%
  select(-id) %>%
  inner_join(grps, by = c("Location" = "loc")) %>%
  group_by(pair) %>%
  nest() %>%
  mutate(t_test = map(data, ~t.test(distro ~ Location, data = .)),
         tidied = map(t_test, broom::tidy)) %>%
  unnest(tidied)
#> # A tibble: 3 x 13
#>    pair data  t_test estimate estimate1 estimate2 statistic p.value
#>   <int> <lis> <list>    <dbl>     <dbl>     <dbl>     <dbl>   <dbl>
#> 1     1 <tib… <htes…   -0.921      31.8      32.7    -0.245   0.816
#> 2     2 <tib… <htes…   -1.48       31.8      33.3    -0.383   0.716
#> 3     3 <tib… <htes…   -0.563      32.7      33.3    -0.305   0.769
#> # … with 5 more variables: parameter <dbl>, conf.low <dbl>,
#> #   conf.high <dbl>, method <chr>, alternative <chr>

Если вам нужно, вы можете сделать что-то, чтобы показать, какие местоположения находятся в каждой паре — объединение с таблицей поиска было бы одним из способов сделать это.

Я также понимаю, что вы упомянули о желании использовать функции broom впоследствии, но не указали, что вам нужен вызов broom::tidy. В этом случае просто удалите последние 2 строки.

person camille    schedule 07.08.2019
comment
tidyverse словесный понос делает некоторые проблемы более доступными/читабельными/обслуживаемыми/и т. д., но, конечно, не все. Вот хороший пример. - person ssayols; 07.08.2019
comment
@ssayols, если вы думаете, что это плохо, вы можете поставить минус. Но ваша точка зрения выше хороша тем, что если все, что они хотят, это сравнить t-тесты, им лучше использовать апостериорный тест. - person camille; 07.08.2019
comment
не поймите меня неправильно, я просто имел в виду, что приведенный выше код является хорошим примером закона инструмента (если все, что у вас есть, это молоток, все выглядит как гвоздь). - person ssayols; 08.08.2019

Немного базы R поможет:

combn(x=unique(sample_long$Location), m=2, simplify=FALSE,
      FUN=function(l) { 
        t.test(distro ~ Location, data=subset(sample_long, Location %in% l))
      })

combn будет генерировать все комбинации элементов x, взятых m одновременно (sic). В сочетании с subset вы примените свой тест к подмножествам вашего data.frame.

person ssayols    schedule 07.08.2019
comment
Я не могу заставить ваш код работать (при условии, что под тестом вы имеете в виду, например, t.test). - person Alf Pascu; 07.08.2019