代码之家  ›  专栏  ›  技术社区  ›  Matthew Graham

我应该如何以编程方式仅将某些NA值更改为在R中选择的指定字符串?

  •  1
  • Matthew Graham  · 技术社区  · 2 年前

    部分内部研发;我正在为工作而工作的D项目,我需要以高效和编程的方式分配某些 NA 字符串的值, BMNDITS (代表“本组未检测到的生物标记物”)。就上下文而言,我在一家小型生物技术公司工作,我们提供的服务是扫描客户正在进行的实验中各种样本类型中存在的小生物标记物(每个都有一个与之相关的唯一样本集ID)。所以,他们会给我们发送样本,我们扫描各种生物标记物的数据,然后我们返回给他们热图和实际数据本身。

    通常,客户会随着时间的推移进行多次实验,以便最终获得足够的相关数据。好吧,如果他们从各种感兴趣的群体中收集足够的样本,他们会希望我们合并并堆叠数据,以便所有数据都存储在一个漂亮的、最终确定的、合并的数据框架中。听起来很简单,对吧?问题是因为不是所有的生物标记物都存在于每项研究中, 很多 属于 NAs 获得介绍。的确,在任何给定的研究中,一个人可能存在一个生物标记物,而另一个人不会在他们捐赠的样本中检测到它,所以对于那个特定生物标记物的特定个体来说,它只是一个 不适用 条目(有时一对可能连续出现)——这很好,因为很明显,我们无法控制生物标记物何时出现在给定的个体中,因为它是完全随机的。

    但问题是,当我们将数据堆叠在一起以创建最终的合并数据框架时,目前,如果在给定的总体/样本集ID中没有观察到生物标记物,那么它将只是大量的序列 不适用 给定列中的值。在我看来,这不是一个很好的描述,所以我试图创建一个R函数,它将进入并改变那些值,而不仅仅是一个常规的旧值 不适用 说话的价值 BMNDITS公司 ,这样,当研究人员查看实际数据本身并想对其进行处理时,他们可以过滤出真正缺失的值和不存在的值,这些值仅仅是因为在给定的人群中没有观察到它们。

    因此,我创建了一些虚假数据,用于模拟我们可能从三个单独的实验中获得的数据(这些数据存储在我在下面提供的代码中创建的三个“玩具”数据框中)。如果你运行我在下面创建的内容,它将在最后生成一个“全部”数据框,由30个(假)个体的30个观察结果组成,其中每个生物标记物都是一列标记为“x1”、“x2”等。同样,由于这里的重点是尝试模拟真实数据,我这样做是为了有时一个生物标记存在于一个集合中,而不是所有其他集合中。这就是列名不完全相同的原因,有些列名在其他列名中不存在。

    # loading dplyr
    library(dplyr)
    
    # making a couple toy data frames
    set.seed(42)
    toy_df1 <- as.data.frame(matrix(data = rnorm(n = 100, mean = 0, sd = 1), nrow = 10, ncol = 10))
    toy_df2 <- as.data.frame(matrix(data = rnorm(n = 100, mean = 0, sd = 1), nrow = 10, ncol = 10))
    toy_df3 <- as.data.frame(matrix(data = rnorm(n = 100, mean = 0, sd = 1), nrow = 10, ncol = 10))
    
    # assigning the names of the various "biomarkers" for this fake data
    names(toy_df1) <- c("x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10")
    names(toy_df2) <- c("x1", "x2", "x3", "x5", "x6", "x7", "x8", "x9", "x10", "x11")
    names(toy_df3) <- c("x1", "x3", "x4", "x5", "x7", "x8", "x9", "x10", "x11", "x13")
    
    # adding a dummy SSID to each toy dataframe
    toy_df1$SSID <- as.numeric(rep(24001, nrow(toy_df1))) # Sample set ID from the first study
    toy_df2$SSID <- as.numeric(rep(24002, nrow(toy_df2))) # Sample set ID from the second study
    toy_df3$SSID <- as.numeric(rep(24003, nrow(toy_df3))) # Sample set ID from the third study
    
    # Creating the NA insertion/MakeNA() function I'll need
    # to help simulate the randomness that the NA values have
    # regarding where they exist in the data
    NA_Insert_Inator <- function(x) {
      x %>% mutate(
        across(
          starts_with("x"), 
          function(.x, probMiss) {
            ifelse(runif(nrow(.)) < probMiss, NA, .x)
          },
          probMiss=0.1
        )
      )
    }
    
    # Using the above function to randomly replace values in each toy dataframe with NA
    toy_df1 <- NA_Insert_Inator(toy_df1)
    toy_df2 <- NA_Insert_Inator(toy_df2)
    toy_df3 <- NA_Insert_Inator(toy_df3)
    
    # merging the toy data sheets into the "Data All"-esque file; 
    # this takes each dataframe and stacks  
    # them on top of each other in sequential order of the SSIDs. 
    # (Also, lastly I move the SSID columns to be the last columns in the toy_data_all dataframe)
    toy_data_all <- bind_rows(toy_df1, toy_df2, toy_df3)
    toy_data_all <- toy_data_all %>% select(-SSID, SSID)
    

    因此,如果您运行上述代码,最终应该会得到类似的结果: See those long streaks of NA values? Those are the ones I'm trying to change

    我创建了以下R函数来尝试更改 不适用 价值观,但我无法让它发挥作用。我可以很好地启动该函数,但当我尝试将其应用于 toy_data_all 数据帧我只得到一个值 NULL 作为回报但我所期待的是那些长串的(特别是 10 因为这是每个研究中假参与者的数量) 不适用 值将更改为指定的字符串 BMNDITS公司 .

    我尝试的方法是基于对每个单独的数据帧使用SSID。具体来说,如果对于任何给定列,如果特定SSID的值都等于 不适用 ,将其更改为 BMNDITS公司 . 我不确定这里到底出了什么问题,也许有更好、更有效的方法来解决这个问题。在此尝试:

    BMNDITS_Inator <- function(freshly_merged_df){
      some_new_df <- freshly_merged_df
      for (i in unique(some_new_df[['SSID']])){
        for (j in 1:ncol(some_new_df)){
          if (all(is.na(some_new_df[which(some_new_df[['SSID']] == i), j]))){
            some_new_df[which(some_new_df[['SSID']] == i), j] <- "BMNDITS"
          }
        }
      }
    

    但是,是的,这就是我被困的地方,非常感谢任何人的帮助或投入。非常感谢!

    1 回复  |  直到 2 年前
        1
  •  2
  •   akrun    2 年前

    我们可以使用分组方法-按“SSID”分组,在所有列上循环( everything() )在中 across ,然后检查 if , all 值为 NA ,然后替换为 "BMNDITS" else 返回字符转换值(如示例所示,列为 numeric 类别)

    library(dplyr)
    toy_data_all %>%
       group_by(SSID) %>% 
       mutate(across(everything(), ~ if(all(is.na(.x))) "BMNDITS" else 
               as.character(.x))) %>%
       ungroup