Select 一个单元格内的最小值或最大值(分隔字符串)

Select min or max values within one cell (delimited string)

我有一个数据框,其中每个样本的列可以有多个值,例如:

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 的新手,不确定我的问题是否可行,如果一个单元格中有多个值,它们是否被视为字符串?

使用 stringrdplyr 的可能解决方案:

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

函数getminstr_extract_all提取数字:

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

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

[[2]]
[1] "0.02"

它的优点是对space或其他字符不敏感,但只能提取一个点。然后我在这个列表上循环以在每个单元格中提取最小值,并将列表转换为具有 unlist 的向量。使用 as.numeric() 函数将可能提取的 . 转换为 NA.

代码 df %>% mutate_at(names(df)[-1],getmin) 仅将此函数应用于除第一列以外的所有列


编辑:如果你想避免 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

数据:

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)

这是大致的想法。

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)

您实际上想要进行字符串运算,将每个字符串转换为数值向量,然后执行汇总函数(minmax)。

我在这里写的代码适用于这种情况,但你必须考虑更多因素:

  • 您的字符串是否包含其他需要删除的字符?
  • 缺失值代表什么?

你也可以传递你想要应用的函数(例如min),但是你还有其他问题,比如你如何将额外的参数传递给那个函数(使用...) - 这将超出范围。

希望,还是有点帮助。

这是一个基本的 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

数据

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))

使用 data.table,将宽转换为长,以逗号分隔,得到 P 值的 minmax for 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 应用相同的步骤。

另一个选项是使用 and :

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