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

eivgtgni  于 4个月前  发布在  其他
关注(0)|答案(2)|浏览(43)

因此,对于我为工作而进行的内部研发项目的一部分,我需要以编程方式高效地将某些NA值赋给字符串BMNDITS(其代表“在该集合中未检测到的生物标记物”)。对于上下文,我在一家小型生物技术公司工作,我们提供的服务是扫描客户进行的实验中各种样本类型中存在的小生物标志物(每个都有一个与之关联的唯一样品组ID)。因此,他们会将样品发送给我们,我们扫描各种生物标志物的数据,然后返回热图和实际数据本身。
通常情况下,客户会在一段时间内运行多个实验,以便最终获得足够多的相关数据。如果他们从不同的感兴趣人群中收集到足够多的样本,他们会希望我们合并和堆叠数据,以便所有数据都存储在一个完美的、最终的、合并的数据框中。听起来很容易,对吧?问题是,因为不是所有的生物标记都能在每一项研究中出现,所以会有很多的NAs被引入。一个个体可能存在生物标志物,而另一个个体在其捐献的样品中没有检测到该生物标志物,因此对于该特定个体的该特定生物标志物,它将只是一个NA条目(有时候一对可能会出现在一行中)--这很好,因为很明显,我们无法控制生物标志物何时出现在给定个体中,因为它是完全随机的。
但问题是,当我们将数据相互叠加以创建最终的合并数据框时,如果在给定的群体/样本组ID中没有观察到生物标志物,那么在给定的列中,它将只是大量的连续NA值。在我看来,这不是很好的描述性。所以我试着创建一个R函数,它会把这些值从一个普通的NA值变成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)

字符串
因此,如果您运行上述代码,最终应该会得到类似于以下内容的结果:x1c 0d1x
我已经创建了下面的R函数来尝试更改这些长的NA值,但是我无法让它工作。但是当我尝试将其应用于toy_data_all Dataframe 时,我只得到一个NULL的值。(特别是10,因为这是每个研究中的假参与者的数量)NA值将更改为指定的字符串BMNDITS
我尝试过的方法是基于对每个 Dataframe 使用SSID。具体来说,如果对于任何给定的列,如果特定SSID的值都等于NA,请将它们更改为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"
      }
    }
  }


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

zf9nrax1

zf9nrax11#

我们可以使用分组方法-按'SSID'分组,循环across中的所有列(everything()),然后检查ifall的值是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

字符串

oknrviil

oknrviil2#

基本上@akrun做了什么,但只使用base R:

do.call(rbind, lapply(split(toy_data_all, toy_data_all$SSID), function(df) {
  df[, colSums(is.na(df)) == nrow(df)] <- "BMNDITS"
  df
}))

字符串

相关问题