t-тест одной группы против многих групп в Tidyverse - PullRequest
2 голосов
/ 06 апреля 2020

У меня есть следующий тиббл

test_tbl <- tibble(name = rep(c("John", "Allan", "George", "Peter", "Paul"), each = 12),
                   category = rep(rep(LETTERS[1:4], each = 3), 5),
                   replicate = rep(1:3, 20),
                   value = sample.int(n = 1e5, size = 60, replace = T))


# A tibble: 60 x 4
   name  category replicate value
   <chr> <chr>        <int> <int>
 1 John  A                1 71257
 2 John  A                2 98887
 3 John  A                3 87354
 4 John  B                1 25352
 5 John  B                2 69913
 6 John  B                3 43086
 7 John  C                1 24957
 8 John  C                2 33928
 9 John  C                3 79854
10 John  D                1 32842
11 John  D                2 19156
12 John  D                3 50283
13 Allan A                1 98188
14 Allan A                2 26208
15 Allan A                3 69329
16 Allan B                1 32696
17 Allan B                2 81240
18 Allan B                3 54689
19 Allan C                1 77044
20 Allan C                2 97776
# … with 40 more rows

Я хочу group_by(name, category) и выполнить 3 вызова t.test, сравнивая category B, C и D с category A.

Я хотел бы сохранить estimate и p.value с выхода. Ожидаемый результат примерно такой:

# A tibble: 5 x 7
  name   B_vs_A_estimate B_vs_A_p_value C_vs_A_estimate C_vs_A_p_value D_vs_A_estimate D_vs_A_p_value
  <chr>            <dbl>          <dbl>           <dbl>          <dbl>           <dbl>          <dbl>
1 John            -0.578         0.486            0.198          0.309           0.631         0.171 
2 Allan            0.140         0.644            0.728          0.283           0.980         0.485 
3 George          -0.778         0.320           -0.424          0.391          -0.154         0.589 
4 Peter           -0.435         0.470           -0.156          0.722           0.315         0.0140
5 Paul             0.590         0.0150          -0.473          0.475           0.681         0.407

Я бы предпочел решение с использованием tidyverse и / или broom.

Ответы [ 4 ]

1 голос
/ 06 апреля 2020

Есть много способов достичь желаемого результата, но, возможно, этот является более интуитивным и простым в отладке (вы можете поставить browser() где угодно)

test_tbl %>%
  group_by(name) %>%
  do({
    sub_tbl <- .
    expand.grid(g1="A", g2=c("B", "C", "D"), stringsAsFactors = FALSE) %>%
      mutate(test=as.character(glue::glue("{g1}_vs_{g2}"))) %>%
      rowwise() %>%
      do({
        gs <- .
        t_res <- t.test(sub_tbl %>% filter(category == gs$g1) %>% pull(value), 
                        sub_tbl %>% filter(category == gs$g2) %>% pull(value))
        data.frame(test=gs$test, estimate=t_res$statistic, p_value=t_res$p.value, 
                   stringsAsFactors = FALSE)
      })
  }) %>%
  ungroup() %>%
  gather(key="statistic", value="val", -name, -test) %>%
  mutate(test_statistic = paste(test, statistic, sep = "_")) %>%
  select(-test, -statistic) %>%
  spread(key="test_statistic", value="val")

Результат

# A tibble: 5 x 7
  name   A_vs_B_estimate A_vs_B_p_value A_vs_C_estimate A_vs_C_p_value A_vs_D_estimate A_vs_D_p_value
  <chr>            <dbl>          <dbl>           <dbl>          <dbl>           <dbl>          <dbl>
1 Allan           -0.270          0.803         -1.03            0.396           1.55           0.250
2 George           0.201          0.855          0.221           0.838           1.07           0.380
3 John            -1.59           0.249          0.0218          0.984          -0.410          0.704
4 Paul             0.116          0.918         -1.62            0.215          -1.53           0.212
5 Peter            0.471          0.664          0.551           0.611           0.466          0.680

Группирует записи по имени, затем применяет функцию (do # 1). Сохраните подкадр данных в sub_tbl, разверните все контрольные примеры (expand.grid) и создайте имя test, объединяя две буквы. Теперь для каждой комбинации примените функцию для запуска t-тестов (do # 2). Эта анонимная функция выполняет тест между группой 1 (g1) и группой 2 (g2) и возвращает фрейм данных с результатами. Вторая часть в основном переставляет столбцы, чтобы получить окончательный результат.

0 голосов
/ 06 апреля 2020

Мы можем использовать

library(dplyr)
library(purrr)
library(stringr)
library(tidyr)
test_tbl %>%
    split(.$name) %>% 
    map_dfr(~ {
         Avalue <- .x$value[.x$category == 'A']
        .x %>% 
           filter(category != 'A') %>% 
           group_by(category) %>%
           summarise(out = t.test(value, Avalue)$p.value) %>% 
           mutate(category = str_c(category, '_vs_A_p_value'))}, .id = 'name') %>%
   pivot_wider(names_from = category, values_from = out)
0 голосов
/ 06 апреля 2020
test_tbl %>%
  dplyr::group_by(name) %>%
  dplyr::summarise(estimate_AB = 
    t.test(value[category == "A"| category == "B"] ~ category[category == "A" | category == "B"]) %>% (function(x){x$estimate[1] - x$estimate[2]}), 
    pvalue_AB = t.test(value[category == "A"| category == "B"] ~ category[category == "A" | category == "B"]) %>% (function(x){x$p.value})
  )

Вот то, что я сделал для проверки группы А против группы В. Я думаю, что вы могли бы расширить мой подход или попытаться включить код из первого решения.

0 голосов
/ 06 апреля 2020

РЕДАКТИРОВАТЬ: код уборщика

map(unique(test_tbl$name),function(nm){test_tbl %>% filter(name == nm)}) %>% 
  map2(unique(test_tbl$name),function(dat,nm){
    map(LETTERS[2:4],function(cat){
      dat %>% 
        filter(category == "A") %>%
        pull %>% 
        t.test(dat %>% filter(category == cat) %>% pull)
    }) %>%
      map_dfr(broom::glance) %>% 
      select(statistic,p.value) %>% 
      mutate(
        name = nm,
        cross_cat = paste0(LETTERS[2:4]," versus A")
      )
  }) %>%
  {do.call(rbind,.)}
...