Выберите минимальное или максимальное значения в одной ячейке (строка с разделителями) - PullRequest
2 голосов
/ 17 января 2020

У меня есть фрейм данных, где для каждого образца столбцы могут иметь несколько значений, например:

Gene       Pvalue1             Pvalue2              Pvalue3                  Beta
Ace    0.0381, ., 0.00357    0.01755, 0.001385    0.0037, NA , 0.039         -0.03,1,15
NOS          NA                  0.02              0.001, 0.00067              0.00009,25,30

Я хочу применить min() и max() для данных каждого гена (у меня есть тысячи генов всего) в каждом столбце и получить наименьшее значение для значений pvalues, но наибольшее значение для столбцов, таких как бета. Таким образом, выходные данные будут выглядеть следующим образом:

Gene       Pvalue1             Pvalue2              Pvalue3                  Beta
Ace        0.00357              0.001385             0.0037                   15
NOS          NA                  0.02                0.00067                  30

Я новичок в R и не уверен, возможно ли то, что я спрашиваю, если в одной ячейке несколько значений, они рассматриваются как строки?

Ответы [ 5 ]

1 голос
/ 20 января 2020

Другой вариант использует и :

library(data.table)
library(matrixStats)

pval_cols <- grep("Pvalue", names(DT), fixed = TRUE, value = TRUE)

min_fun <- function(x) {
  y <- tstrsplit(x, split = ",", fixed = TRUE)
  y <- rowMins(sapply(y, as.numeric), na.rm = TRUE)
  y <- replace(y, !is.finite(y), NA)
  return(y)
}

DT[, (pval_cols) := lapply(.SD, min_fun)
   , .SDcols = pval_cols][]

, что дает:

> DT
   Gene Pvalue1  Pvalue2 Pvalue3          Beta
1:  Ace 0.00357 0.001385 0.00370    -0.03,1,15
2:  NOS      NA 0.020000 0.00067 0.00009,25,30

Для столбцов Beta вы можете создать аналогичный max_fun: просто замените rowMins на rowMaxs.

1 голос
/ 17 января 2020

Используя data.table , преобразуйте широкий в длинный, разделите запятыми, получите min для P-значений и max для Betas и, наконец, преобразовать обратно в длинное в широкое.

library(data.table)

dt1 <- fread("
Gene       Pvalue1             Pvalue2              Pvalue3                  Beta
Ace    0.0381,.,0.00357    0.01755,0.001385    0.0037,NA,0.039         -0.03,1,15
NOS          NA                  0.02              0.001,0.00067              0.00009,25,30
            ")

dcast(
  melt(dt1, id.vars = "Gene")[, paste0("col", 1:3) := lapply(tstrsplit(value, ","), as.numeric) 
                              ][, MinMax := ifelse(grepl("Pvalue", variable),
                                                   pmin(col1, col2, col3, na.rm = TRUE),
                                                   pmax(col1, col2, col3, na.rm = TRUE)) ],
  Gene ~ variable, value.var = "MinMax")

#    Gene Pvalue1  Pvalue2 Pvalue3 Beta
# 1:  Ace 0.00357 0.001385 0.00370   15
# 2:  NOS      NA 0.020000 0.00067   30
# Warning message:
# In lapply(tstrsplit(value, ","), as.numeric) : NAs introduced by coercion

Примечание: Те же шаги можно применить, используя dplyr/tidyr.

1 голос
/ 17 января 2020

Вот базовое решение R, использующее regmatches + gregexpr для сортировки чисел, т. Е.

dPvalue <- t(apply(df[grep("Pvalue",names(df))], 1, function(v) {
  unlist(Map(function(x) ifelse(length(x)>0, min(as.numeric(x)),NA), regmatches(v, gregexpr("-?\\d+(\\.\\d+)?",v))))
}))

Beta <- apply(df[grep("Beta",names(df))], 1, function(v) {
  unlist(Map(function(x) ifelse(length(x)>0, max(as.numeric(x)),NA), regmatches(v, gregexpr("-?\\d+(\\.\\d+)?",v))))
})

dfout <- cbind(df["Gene"],Pvalue,Beta)

такое, что

> dfout
  Gene Pvalue1  Pvalue2 Pvalue3 Beta
1  Ace 0.00357 0.001385 0.00370   15
2  NOS      NA 0.020000 0.00067   30

DATA

df <- structure(list(Gene = structure(1:2, .Label = c("Ace", "NOS"), class = "factor"), 
    Pvalue1 = structure(c(1L, NA), .Label = "0.0381,.,0.00357", class = "factor"), 
    Pvalue2 = structure(1:2, .Label = c("0.01755,0.001385", "0.02"
    ), class = "factor"), Pvalue3 = structure(2:1, .Label = c("0.001,0.00067", 
    "0.0037,NA,0.039"), class = "factor"), Beta = structure(1:2, .Label = c("-0.03,1,15", 
    "0.00009,25,30"), class = "factor")), class = "data.frame", row.names = c(NA, 
-2L))
1 голос
/ 17 января 2020

Возможное решение с использованием stringr и dplyr:

library(dplyr)
library(stringr)

getmin = function(col) str_extract_all(col,"[0-9\\.-]+") %>%
  lapply(.,function(x) min(as.numeric(x),na.rm = T) ) %>%
  unlist() 

df %>%
  mutate_at(names(df)[-1],getmin)

  Gene Pvalue1  Pvalue2 Pvalue3  Beta
1  Ace 0.00357 0.001385 0.00370 -3e-02
2  NOS     Inf 0.020000 0.00067 9e-05

Warning messages:
1: In FUN(X[[i]], ...) : NAs introduced by coercion
2: In min(as.numeric(x), na.rm = T) :
  no non-missing arguments to min; returning Inf

Функция getmin извлекает число с помощью str_extract_all:

 str_extract_all(df$Pvalue2,"[0-9\\.-]+")

[[1]]
[1] "0.01755"  "0.001385"

[[2]]
[1] "0.02"

Она имеет преимущество быть нечувствительным к пробелу или другим символам, но может извлечь только точку. Затем я l oop в этом списке, чтобы извлечь в каждой ячейке минимум и преобразовать список в вектор с unlist. Используя функцию as.numeric(), преобразуйте возможные извлеченные . в NA.

код df %>% mutate_at(names(df)[-1],getmin), просто примените эту функцию ко всем столбцам, кроме первого


edit : если вы хотите избежать значений inf, вы можете использовать эту слегка измененную версию:

min2 = function(x) if(all(is.na(x))) NA else min(x,na.rm = T)
getmin = function(col) str_extract_all(col,"[0-9\\.-]+") %>%
  lapply(.,function(x)min2(as.numeric(x)) ) %>%
  unlist() 

df %>%
    mutate_at(names(df)[-1],getmin)

  Gene Pvalue1  Pvalue2 Pvalue3  Beta
1  Ace 0.00357 0.001385 0.00370 -3e-02
2  NOS      NA 0.020000 0.00067 9e-05

data:

df <- read.table(text = "
                 Gene       Pvalue1             Pvalue2              Pvalue3                  Beta
Ace    0.0381,.,0.00357    0.01755,0.001385    0.0037,NA,0.039         -0.03,1,15
                 NOS          NA                  0.02              0.001,0.00067              0.00009,25,30
                 ",header = T)
0 голосов
/ 17 января 2020

Вот общая идея.

applyFunctionToString <- function(
    string
  , sep = ","
){
    string <- gsub(" ", "", string)
    string <- unlist(strsplit(string, sep))
    string[string == "NA"] <- NA
    numbers <- as.numeric(string)
    min(numbers, na.rm = TRUE)
}

sapply(c("0.01755, 0.001385", "0.0037, NA , 0.039"), applyFunctionToString)

Вы действительно хотите перейти к строковым операциям, преобразовать каждую строку в вектор цифр c и затем выполнить функцию суммирования (min или max). ).

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

  • Содержат ли ваши строки другие символы, которые необходимо удалить?
  • Какие пропущенные значения представлены в виде?

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

Надеюсь, это все равно немного поможет.

...