Вот подход, который по своей концепции похож на 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