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

问题描述 投票:0回答:2

因此,对于我正在从事的内部研发项目的一部分,我需要以编程方式高效地将某些

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)

因此,如果您运行上面的代码,您最终应该得到类似于以下内容的内容:

我创建了以下 R 函数来尝试更改这些长条纹的

NA
值,但我无法让它工作。我可以很好地启动该函数,但是当我尝试将其应用到我的
toy_data_all
数据框时,我只得到
NULL
值作为回报。但我所期待的是那些长条纹(特别是
10
,因为这是每项研究中假参与者的数量)
NA
值将更改为指定的
BMNDITS
字符串。

我尝试的方法是基于对每个单独的数据帧使用 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"
      }
    }
  }

但是,是的,这就是我陷入困境的地方,非常感谢任何人的帮助或意见。非常感谢!

r string function na missing-data
2个回答
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

0
投票

基本上是 @akrun 所做的,但仅使用基本 R:

do.call(rbind, lapply(split(toy_data_all, toy_data_all$SSID), function(df) {
  df[, colSums(is.na(df)) == nrow(df)] <- "BMNDITS"
  df
}))
© www.soinside.com 2019 - 2024. All rights reserved.