Как исправить / стандартизировать имена переменных, если их формат не соответствует - PullRequest
0 голосов
/ 02 мая 2018

Я пишу скрипт, который загружает файлы RData, содержащие результаты предыдущих экспериментов, и анализирует сохраненные в них кадры данных. Я заметил, что, хотя имена переменных не согласованы, например, иногда symbol называется gene_name или gene_symbol. Порядок переменных также различен для разных фреймов данных, поэтому я не могу просто переименовать их все с помощью colnames(df) <- c('a', 'b', ...)

Я ищу способ переименования переменных на основе их имени, который не будет выдавать ошибку, если эта переменная не найдена. Вот что я хочу сделать ниже, но (в идеале) без десятков условных выражений.

if ('gene_name' %in% colnames(df)) {
    df <- df %>% dplyr::rename('symbol' = gene_name)
}

В приведенном ниже примере я хотел бы найти элегантный способ переименования переменной b в D, который можно безопасно использовать для фреймов данных, в которых отсутствует переменная b

x <- data.frame('a' = c(1,2,3), 'b' = c(4,5,6))
y <- data.frame('a' = c(1,2,3), 'c' = c(4,5,6))
dfs <- list(x,y)
dfs.fixed <- lapply(dfs, function(x) ?????)

Желаемый результат:

dfs.fixed
[[1]]
  a D
1 1 4
2 2 5
3 3 6

[[2]]
  a c
1 1 4
2 2 5
3 3 6

Ответы [ 3 ]

0 голосов
/ 02 мая 2018

Мы можем использовать rename_at с map

map(dfs, ~ .x %>%
               rename_at(b, sub, pattern = "^b$", replacement = "D"))
#[[1]]
#  a D
#1 1 4
#2 2 5
#3 3 6

#[[2]]
#  a c
#1 1 4
#2 2 5
#3 3 6
0 голосов
/ 02 мая 2018

Вот подход, который по своей концепции похож на Terru_theTerror, но расширяет его, разрешая регулярные выражения. Это может быть излишним, но ...

Сначала мы определяем простую «карту», ​​которая отображает в желаемое имя (первая строка в каждом векторе списка) из любая строка (оставшиеся строки в каждом векторе) , Функция, которая выполняет сопоставление, принимает аргумент fixed=FALSE, и в этом случае 2-я и оставшаяся строка могут быть регулярными выражениями, что дает больше власти и ответственности.

При использовании fixed=TRUE (по умолчанию) карта может выглядеть следующим образом:

colnamemap <- list(
  c("symbol", "gene_name", "gene_symbol"),
  c("D", "c", "quux"),
  c("bbb", "b", "ccc")
)

, где "gene_name" и "gene_symbol" будут заменены на "symbol" и т. Д. Однако, если вы хотите использовать шаблоны (fixed=FALSE), вы должны быть максимально точными для исключить ошибочные или множественные совпадения (по столбцам).

colnamemapptn <- list(
  c("symbol", "^gene_(name|symbol)$"),
  c("D", "^D$", "^c$", "^quux$"),
  c("bbb", "^b$", "^ccc$")
)

Функция, которая выполняет фактическое переназначение:

fixfunc <- function(df, namemap, fixed = TRUE, ignore.case = FALSE) {
  compare <- if (fixed) `%in%` else grepl
  downcase <- if (ignore.case) tolower else c
  newcn <- cn <- colnames(df)
  newnames <- sapply(namemap, `[`, 1L)
  matches <- sapply(namemap, function(nmap) {
    apply(outer(downcase(nmap[-1]), downcase(cn), Vectorize(compare)), 2, any)
  }) # dims: 1=cn; 2=map-to
  for (j in seq_len(ncol(matches))) {
    if (sum(matches[,j]) > 1) {
      warning("rule ", sQuote(newnames[j]), " matches multiple columns: ",
              paste(sQuote(cn[ matches[,j] ]), collapse=","))
      matches[,j] <- FALSE
    }
  }
  for (i in seq_len(nrow(matches))) {
    rowmatches <- sum(matches[i,])
    if (rowmatches == 1) {
      newcn[i] <- newnames[ matches[i,] ]
    } else if (rowmatches > 1) {
      warning("column ", sQuote(cn[i]), " matches multiple rules: ",
              paste(sQuote(newnames[ matches[i,]]), collapse=","))
      matches[i,] <- FALSE
    }      
  }
  if (any(matches)) colnames(df) <- newcn
  df
}

(Вы можете расширить его, чтобы обеспечить уникальность, используя make.names и / или make.unique. Есть также ignore.case, здесь не совсем проверено, но я думаю, что это легко сделать.)

Я собираюсь расширить ваши образцы данных, включив в них данные, которые будут соответствовать нескольким шаблонам, что приведет к неоднозначности:

x <- data.frame('a' = c(1,2,3), 'b' = c(4,5,6))
y <- data.frame('a' = c(1,2,3), 'c' = c(4,5,6))
z <- data.frame('cc' = 1:3, 'ccc' = 2:4)
dfs <- list(x,y,z)

где третий data.frame имеет два столбца, которые соответствуют моему третьему не шаблонному вектору. Когда есть несколько совпадений, я думаю, что безопаснее всего предупредить об этом и не менять ни одного из них.

Это правильно, только с фиксированными строками:

lapply(dfs, fixfunc, colnamemap, fixed=TRUE)
# [[1]]
#   a bbb
# 1 1   4
# 2 2   5
# 3 3   6
# [[2]]
#   a D
# 1 1 4
# 2 2 5
# 3 3 6
# [[3]]
#   cc bbb
# 1  1   2
# 2  2   3
# 3  3   4

Это неправильно использует строки в качестве шаблонов, что приводит к тому, что один из них предупреждает о нескольких совпадениях:

lapply(dfs, fixfunc, colnamemap, fixed=FALSE)
# Warning in FUN(X[[i]], ...) :
#   rule 'D' matches multiple columns: 'cc','ccc'
# [[1]]
#   a bbb
# 1 1   4
# 2 2   5
# 3 3   6
# [[2]]
#   a D
# 1 1 4
# 2 2 5
# 3 3 6
# [[3]]
#   cc bbb
# 1  1   2
# 2  2   3
# 3  3   4

Лучшее использование fixed=FALSE со строгими шаблонами вместо:

lapply(dfs, fixfunc, colnamemapptn, fixed=FALSE)
# same output as the first call
0 голосов
/ 02 мая 2018

Попробуйте этот подход:

ШАГ 1 Функция, заменяющая список имен столбцов другой строкой (оба параметра параметризованы):

colnames_rep<-function(df,to_find,to_sub)
{
  colnames(df)[which(colnames(df) %in% to_find)]<-to_sub
  return(df)
}

ШАГ 2 Используйте lapply, чтобы применить функцию к каждому data.frame:

lapply(dfs,colnames_rep,to_find=c("b"),to_sub="D")
[[1]]
  a D
1 1 4
2 2 5
3 3 6

[[2]]
  a c
1 1 4
2 2 5
3 3 6

Спасибо divibisan за предложение

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...