большой кадр данных: повторный t-тест между группами для тысячи факторов

Я прочитал много сообщений, связанных с обработкой данных и «повторным» t-тестом, но я не могу понять, как это сделать в моем случае.

Вы можете получить мой пример набора данных для StackOverflow здесь: //www.dropbox.com/s/0b618fs1jjnuzbg/dataset.example.stckovflw.txt?dl=0

У меня есть большой кадр данных выражения gen, например:

> b<-read.delim("dataset.example.stckovflw.txt")

> head(b)
      animal            gen condition tissue    LogFC
1 animalcontrol1         kjhss1   control  brain 7.129283
2 animalcontrol1          sdth2   control  brain 7.179909
3 animalcontrol1     sgdhstjh20   control  brain 9.353147
4 animalcontrol1 jdygfjgdkydg21   control  brain 6.459432
5 animalcontrol1  shfjdfyjydg22   control  brain 9.372865
6 animalcontrol1      jdyjkdg23   control  brain 9.541097

> str(b)
'data.frame':   21507 obs. of  5 variables:
 $ animal   : Factor w/ 25 levels "animalcontrol1",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ gen      : Factor w/ 1131 levels "dghwg1041","dghwg1086",..: 480 761 787    360 863 385 133 888 563 738 ...
 $ condition: Factor w/ 5 levels "control","treatmentA",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ tissue   : Factor w/ 2 levels "brain","heart": 1 1 1 1 1 1 1 1 1 1 ...
 $ LogFC    : num  7.13 7.18 9.35 6.46 9.37 ...

Каждая группа состоит из 5 животных, и у каждого животного количественно определено много родов. (Однако у каждого животного может быть различный набор количественных родов, но также многие роды будут общими между животными и группами).

Я хотел бы выполнить t-тест для каждого поколения между моей обработанной группой (A, B, C или D) и контролем. Данные должны быть представлены в виде таблицы, содержащей p-значение для каждого поколения в каждой группе.

Поскольку у меня так много родов (тысячи), я не могу подмножить каждый род.

Вы знаете, как я могу автоматизировать процедуру?

Я думал о цикле, но я абсолютно не уверен, что он может достичь того, чего я хочу, и как действовать дальше.

Кроме того, я больше просматривал эти сообщения, используя функцию apply: Применить t-тест ко многим столбцам в фрейме данных, разделенному по факторам и Перебор t.tests для подмножеств фреймов данных в r

# ################ additionnal information after reading first comments and answers :

@andrew_reece: Большое спасибо за это. Это почти то, что я искал. Однако я не могу найти способ сделать это с помощью t-теста. ANOVA — это интересная информация, но тогда мне нужно будет знать, какая из обработанных групп значительно отличается от моей контрольной группы. Также мне нужно было бы знать, какая обработанная группа значительно отличается друг от друга, «два на два».

Я пытался использовать ваш код, изменив «aov (..)» в «t.test (…)». Для этого сначала я реализую подмножество (b, условие == «контроль» | условие == «лечениеA»), чтобы сравнить только две группы. Однако при экспорте таблицы результатов в файл csv таблица непонятна (без имени гена, без p-значений и т. д., только числа). Я буду продолжать искать способ сделать это правильно, но пока я застрял.

@42:

Большое спасибо за эти советы. Это просто пример набора данных, давайте предположим, что нам нужно использовать отдельные t-тесты.

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

# Visualisation of shared genes by VennDiagrams :
# let's simplify and consider only 3 conditions :

b<-read.delim("dataset.example.stckovflw.txt")
b<- subset(b, condition == "control" | condition == "treatmentA" | condition == "treatmentB")

b1<-table(b$gen, b$condition)

b1

b2<-subset(data.frame(b1, "control" > 2 
              |"treatmentA" > 2 
              |"treatmentB" > 2 ))

b3<-subset(b2, Freq>2) # select only genes that have been quantified in at     least 2 animals per group
b3
b4 = within(b3, {
  Freq = ifelse(Freq > 1, 1, 0)
}) # for those observations, we consider the gene has been detected so we     change the value 0 regardless the freq of occurence (>2)


b4

b5<-table(b4$Var1, b4$Var2)
write.csv(b5, file = "b5.csv")

# make an intermediate file .txt (just add manually the name of the cfirst     column title)
# so now we have info
bb5<-read.delim("bb5.txt")

nrow(subset(bb5, control == 1))
nrow(subset(bb5, treatmentA == 1))
nrow(subset(bb5, treatmentB == 1))

nrow(subset(bb5, control == 1 & treatmentA == 1))
nrow(subset(bb5, control == 1 & treatmentB == 1))
nrow(subset(bb5, treatmentA == 1 & treatmentB == 1))

nrow(subset(bb5, control == 1 & treatmentA == 1 & treatmentB == 1))

library(grid)
library(futile.logger)
library(VennDiagram)

venn.plot <- draw.triple.venn(area1 = 1005, 
                          area2 = 927, 
                          area3 = 943, 
                          n12 = 843, 
                          n23 = 861, 
                          n13 = 866, 
                          n123 = 794, 
                          category = c("controls", "treatmentA",     "treatmentB"),  
                          fill = c("red", "yellow", "blue"),
                          cex = 2,
                          cat.cex = 2,
                          lwd = 6,
                          lty = 'dashed',
                          fontface = "bold",
                          fontfamily = "sans",
                          cat.fontface = "bold",
                          cat.default.pos = "outer",
                          cat.pos = c(-27, 27, 135),
                          cat.dist = c(0.055, 0.055, 0.085),
                          cat.fontfamily = "sans",
                          rotation = 1);

введите здесь описание изображения


person SkyR    schedule 12.05.2018    source источник
comment
Не могли бы вы привести пример одного t-теста, который вы хотели бы выполнить? Я не уверен, что полностью понял вопрос   -  person shuckle    schedule 13.05.2018
comment
Я предполагаю, что ваше использование терминов gen и gens действительно требует рассмотрения этих значений как гена, а гены как генетические.   -  person IRTFM    schedule 13.05.2018
comment
Не ответ на вашу проблему, но я не думаю, что t-тест является наиболее подходящим статистическим тестом для использования здесь, о чем вы можете спросить на stats.stackexchange.com.   -  person hpesoj626    schedule 13.05.2018
comment
@shuckle : b‹-read.delim(dataset.example.stckovflw.txt) › b1‹- подмножество (b, условие == контроль | условие == обработка A ) › b2‹- подмножество (b1, gen == kjhss1) › t.test(LogFC ~ условие, +paired=FALSE, + data=b1) Данные t-теста Welch Two Sample: LogFC по условию t = 0,23853, df = 9235, p-значение = 0,8115 альтернативная гипотеза: истинная разница в средних не равно 0 95-процентный доверительный интервал: -0,3407286 0,4351405 выборочные оценки: среднее в контрольной группе среднее в лечебной группеA 9,966224 9,919019   -  person SkyR    schedule 13.05.2018
comment
@42: да, действительно, это ген, а не ген (я был сбит с толку, потому что по-испански это ген...)   -  person SkyR    schedule 13.05.2018
comment
@hpesoj626: меня просто интересует техника R здесь, чтобы фактически применить ее к другой биологической конечной точке, это просто поддельный набор данных для примера, чтобы ознакомиться с методами R. Я согласен с тем, что t-тест обычно не рекомендуется в геномике… извините за этот плохой пример. Давайте просто предположим, что нам нужно использовать t-тесты и что это еще одна биологическая конечная точка.   -  person SkyR    schedule 13.05.2018
comment
@SkyR, обратите внимание, что @ ссылки в вашем фактическом сообщении не помечаются в учетных записях пользователей. Я бы не увидел ваш обновленный запрос, если бы не проверял. Лучше публиковать ответы, относящиеся к конкретному ответу, в виде комментариев к данному ответу.   -  person andrew_reece    schedule 13.05.2018


Ответы (2)


Обновление (согласно комментариям ОП):
Попарным сравнением condition можно управлять с помощью апостериорного теста ANOVA, такого как честное значимое различие Тьюки (stats::TukeyHSD()). (Есть и другие, это всего лишь один из способов демонстрации PoC.)

results <- b %>%
  mutate(condition = factor(condition)) %>%
  group_by(gen) %>%
  filter(length(unique(condition)) >= 2) %>%
  nest() %>%
  mutate(
    model = map(data, ~ TukeyHSD(aov(LogFC ~ condition, data = .x))),
    coef = map(model, ~ broom::tidy(.x))
  ) %>%
  unnest(coef) %>% 
  select(-term)

    results
# A tibble: 7,118 x 6
   gen        comparison            estimate conf.low conf.high adj.p.value
   <chr>      <chr>                    <dbl>    <dbl>     <dbl>       <dbl>
 1 kjhss1     treatmentA-control       1.58     -20.3      23.5       0.997
 2 kjhss1     treatmentC-control      -3.71     -25.6      18.2       0.962
 3 kjhss1     treatmentD-control       0.240    -21.7      22.2       1.000
 4 kjhss1     treatmentC-treatmentA   -5.29     -27.2      16.6       0.899
 5 kjhss1     treatmentD-treatmentA   -1.34     -23.3      20.6       0.998
 6 kjhss1     treatmentD-treatmentC    3.95     -18.0      25.9       0.954
 7 sdth2      treatmentC-control      -1.02     -21.7      19.7       0.991
 8 sdth2      treatmentD-control       3.25     -17.5      24.0       0.909
 9 sdth2      treatmentD-treatmentC    4.27     -16.5      25.0       0.849
10 sgdhstjh20 treatmentC-control      -7.48     -30.4      15.5       0.669
# ... with 7,108 more rows

Исходный ответ
Вы можете использовать tidyr::nest() и < a href="https://www.rdocumentation.org/packages/purrr/versions/0.2.4/topics/map" rel="nofollow noreferrer">purrr::map() для выполнения технической задачи группировки по gen, а затем провести статистические тесты, сравнивая эффекты condition (предположительно с LogFC в качестве DV).

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

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

require(tidyverse)

results <- b %>%
  mutate(condition = factor(condition)) %>%
  group_by(gen) %>%
  filter(length(unique(condition)) >= 2) %>%
  nest() %>%
  mutate(
    model = map(data, ~ aov(LogFC ~ condition, data = .x)),
    coef = map(model, ~ broom::tidy(.x))
  ) %>%
  unnest(coef) 

Несколько косметических корректировок, чтобы приблизиться к вашему первоначальному видению (просто таблица с gen и p-значениями), хотя обратите внимание, что это действительно упускает много важной информации, и я не советую вам ограничивать свои результаты таким образом. .

results %>%
  filter(term!="Residuals") %>%
  select(gen, df, statistic, p.value)

results
# A tibble: 1,111 x 4
   gen               df statistic p.value
   <chr>          <dbl>     <dbl>   <dbl>
 1 kjhss1            3.     0.175   0.912
 2 sdth2             2.     0.165   0.850
 3 sgdhstjh20        2.     0.440   0.654
 4 jdygfjgdkydg21    2.     0.267   0.770
 5 shfjdfyjydg22     2.     0.632   0.548
 6 jdyjkdg23         2.     0.792   0.477
 7 fckjhghw24        2.     0.790   0.478
 8 shsnv25           2.     1.15    0.354
 9 qeifyvj26         2.     0.588   0.573
10 qsiubx27          2.     1.14    0.359
# ... with 1,101 more rows

Примечание: я не могу доверять этому подходу - он почти дословно взят из примера, который я видел, как Хэдли привел вчера вечером на purrr. Вот ссылка на общедоступный репозиторий демо. код, который он использовал, который охватывает аналогичный вариант использования.

person andrew_reece    schedule 13.05.2018
comment
1111 — это количество строк в результатах перед вторым фильтром, где df был > 4. Я думаю, что наибольший интерес, по крайней мере для начала, представляют те, где df был намного выше, возможно, 15 или больше. - person IRTFM; 13.05.2018
comment
За исключением того, что попытка доступа к тем, что в табличке, вызывает только NA. Цвет меня озадачил. results[ results$df == 20 , ] # Тиббл: 0 x 4 - person IRTFM; 13.05.2018
comment
Всего 5 групп - управление и A-D - поэтому не может быть больше 4 степеней свободы (т.е. max(results$df) == 4). На что обратить внимание 20? - person andrew_reece; 13.05.2018
comment
Если вы сделаете table( results$df ), вы получите гораздо больший диапазон результатов. Мне интересно, какая из F-статистических степеней свободы (группа или остатки) сообщалась, ... да неважно, я использовал первый набор results. - person IRTFM; 13.05.2018
comment
Большое спасибо andrew_reece за обновленный ответ. Это действительно полезно для меня. Чтобы завершить свое первоначальное желание «хорошего обращения» с командами, я пытался использовать ваш код с непараметрическим тестом. С kruskal.test работает нормально, но при реализации апостериорного теста для получения попарных сравнений не работает: … model = map(data, ~ dunnTest(LogFC ~ condition, data = .x, method=bh)) получаю следующее сообщение об ошибке: Ошибка в mutate_impl(.data, dots): Ошибка оценки: неправильное количество измерений. - person SkyR; 13.05.2018
comment
Я знаю, что я упрям, но я все еще застрял с использованием t.test в вашем коде, чтобы получить обзор таблицы попарных сравнений. Когда я использую t.test в вашем примере кода (вместо aov), я получаю следующую ошибку: Ошибка в mutate_impl(.data, dots): Ошибка оценки: коэффициент группировки должен иметь ровно 2 уровня. Если я использую b‹- подмножество (b, условие == контроль | условие == обработка A), то я все еще получаю сообщение об ошибке: Ошибка в FUN (X [[i]], ...): объект «термин» не найден … - person SkyR; 13.05.2018
comment
Попробуйте понять решение здесь, а не просто заменять функции другими функциями. Например, вы можете видеть, что term даже не включено в исходный ответ — это потому, что результаты aov и TukeyHSD различны. Все, что вам нужно сделать, чтобы обнаружить это, это удалить select в конце и посмотреть, какие выходные данные функции. Что касается t-тестов, неясно, зачем они вам нужны, если вам также нужны непараметрические тесты. - person andrew_reece; 13.05.2018
comment
Этот ответ решает вашу проблему автоматизации сгруппированных попарных сравнений. Он предоставляет p-значения с поправкой на сравнение (которых не дают t-тесты), и весь смысл HSD как апостериорного теста заключается в том, что он сообщает статистику теста, основанную на студенческом анализе. распределение диапазона, которое в любом случае приближается к распределению t. Если у вас есть вопросы о том, когда и как использовать параметрические и непараметрические тесты, напишите в CrossValidated. Если у вас есть дополнительные вопросы о том, как использовать другие логические тесты из конкретных пакетов, откройте новый вопрос на StackOverflow. - person andrew_reece; 13.05.2018

У вас есть 25 животных в 5 разных лечебных группах с разным числом gen-значений (предположительно активности генетических зондов) в двух разных тканях:

table(b$animal, b$condition)

                    control treatmentA treatmentB treatmentC treatmentD
  animalcontrol1       1005          0          0          0          0
  animalcontrol2        857          0          0          0          0
  animalcontrol3        959          0          0          0          0
  animalcontrol4        928          0          0          0          0
  animalcontrol5       1005          0          0          0          0
  animaltreatmentA1       0        927          0          0          0
  animaltreatmentA2       0        883          0          0          0
  animaltreatmentA3       0        908          0          0          0
  animaltreatmentA4       0        861          0          0          0
  animaltreatmentA5       0        927          0          0          0
  animaltreatmentB1       0          0        943          0          0
  animaltreatmentB2       0          0        841          0          0
  animaltreatmentB3       0          0        943          0          0
  animaltreatmentB4       0          0        910          0          0
  animaltreatmentB5       0          0        943          0          0
  animaltreatmentC1       0          0          0        742          0
  animaltreatmentC2       0          0          0        724          0
  animaltreatmentC3       0          0          0        702          0
  animaltreatmentC4       0          0          0        698          0
  animaltreatmentC5       0          0          0        742          0
  animaltreatmentD1       0          0          0          0        844
  animaltreatmentD2       0          0          0          0        776
  animaltreatmentD3       0          0          0          0        812
  animaltreatmentD4       0          0          0          0        783
  animaltreatmentD5       0          0          0          0        844

Согласитесь, вам нужно каким-то образом «автоматизировать» это, но я думаю, что вам нужна более общая стратегия статистического вывода, а не попытка выбрать отношения, применяя отдельные t-тесты. Вы можете рассмотреть либо смешанные модели, либо один из вариантов случайного леса. Я думаю, вам следует обсудить это со статистиком. В качестве примера того, когда ваши надежды не оправдаются, взгляните на имеющуюся у вас информацию о первом «поколении» среди 1131 значения:

str( b[ b$gen == "dghwg1041", ])
'data.frame':   13 obs. of  5 variables:
 $ animal   : Factor w/ 25 levels "animalcontrol1",..: 1 6 11 2 7 12 3 8 13 14 ...
 $ gen      : Factor w/ 1131 levels "dghwg1041","dghwg1086",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ condition: Factor w/ 5 levels "control","treatmentA",..: 1 2 3 1 2 3 1 2 3 3 ...
 $ tissue   : Factor w/ 2 levels "brain","heart": 1 1 1 1 1 1 1 1 1 1 ...
 $ LogFC    : num  4.34 2.98 4.44 3.87 2.65 ...

У вас есть справедливое число с «полным представлением:

gen_length <- ave(b$LogFC, b$gen, FUN=length)
Hmisc::describe(gen_length)
#--------------
gen_length 
       n  missing distinct     Info     Mean      Gmd      .05      .10 
   21507        0       18    0.976    20.32    4.802       13       14 
     .25      .50      .75      .90      .95 
      18       20       24       25       25 

Value          5     8     9    10    12    13    14    15    16    17
Frequency    100    48   288   270    84   624   924  2220    64   527
Proportion 0.005 0.002 0.013 0.013 0.004 0.029 0.043 0.103 0.003 0.025

Value         18    19    20    21    22    23    24    25
Frequency    666  2223  3840    42   220  1058  3384  4925
Proportion 0.031 0.103 0.179 0.002 0.010 0.049 0.157 0.229

Вы можете начать с просмотра всех «gen», у которых есть полные данные:

head( gen_tbl[ gen_tbl == 25 ], 25)
#------------------
   dghwg1131     dghwg546     dghwg591     dghwg636     dghwg681 
          25           25           25           25           25 
    dghwg726    dgkuck196    dgkuck286    dgkuck421    dgkuck691 
          25           25           25           25           25 
   dgkuck736 dgkukdgse197 dgkukdgse287 dgkukdgse422 dgkukdgse692 
          25           25           25           25           25 
dgkukdgse737       djh592       djh637       djh682       djh727 
          25           25           25           25           25 
   dkgkjd327    dkgkjd642    dkgkjd687    dkgkjd732  fckjhghw204 
          25           25           25           25           25 
person IRTFM    schedule 12.05.2018