У меня есть факторная переменная, которая состоит из двух подстрок, разделенных _, например string1_string2. Я хочу установить уровни факторов для префикса («строка1») и суффикса («строка2») отдельно, а затем определить общий набор уровней факторов для объединенной строки. Кроме того, приоритет уровней в первой подстроке по сравнению со второй может различаться.
Небольшой пример того, чего я хочу добиться:
# reproducible data
x <- factor(c("DBO_A", "PH_A", "COND_A", "DBO_B", "PH_B", "COND_B", "DBO_C", "PH_C", "COND_C"))
[1] DBO_A PH_A COND_A DBO_B PH_B COND_B DBO_C PH_C COND_C
Levels: COND_A COND_B COND_C DBO_A DBO_B DBO_C PH_A PH_B PH_C
Если я не определю уровни факторов, они будут расположены в алфавитном порядке. Теперь я хочу установить уровни строк слева и справа от разделителя _, например
PH <COND <DBO слева (LHS).B <A <C с правой стороны (RHS).Кроме того, я хочу указать, какая сторона, левая или правая, имеет приоритет над другой. В зависимости от того, какая сторона имеет приоритет, общий порядок уровней будет отличаться:
(1) Если уровни на LHS являются прецедентными:
[1] DBO_A PH_A COND_A DBO_B PH_B COND_B DBO_C PH_C COND_C
Levels: PH_B PH_A PH_C COND_B COND_A COND_C DBO_B DBO_A DBO_C
(2) Если уровни RHS являются прецедентными:
[1] DBO_A PH_A COND_A DBO_B PH_B COND_B DBO_C PH_C COND_C
Levels: PH_B COND_B DBO_B PH_A COND_A DBO_A PH_C COND_C DBO_C
Теперь у меня только одна мысль решить эту проблему, например, factor(x, levels = c(xx, xx, ...)), но у меня больше уровней, чем показано выше, так что это будет выглядеть нелепо.
Примечание: Я не хочу менять порядок моих данных, только порядок уровней.





Как упирается что-то вроде
x <- with(expand.grid(x = c("DBO", "PH", "COND"), y = c("A", "B", "C")),
factor(paste(x, y, sep = "_"), levels = paste(x, y, sep = "_")))
Вам не нужно записывать все возможные уровни, только уровни одной и другой стороны.
Дело не в том, как я производю свой x. Это просто воспроизводимые данные. Пожалуйста, прочтите мой ожидаемый результат.
Используя CRAN-пакет forcats, вы можете комбинировать список факторов. Функция ниже ожидает в качестве входных данных 2 вектора, prefix и suffix, в том порядке, в котором они вам нужны.
Аргумент sep = "_" по умолчанию установлен на разделитель в вопросе. Вы можете передать другой разделитель, если хотите.
library(forcats)
custom_fct <- function(prefix, suffix, sep = "_"){
lst <- lapply(prefix, function(p){
f <- paste(p, suffix, sep = sep)
factor(f, levels = f)
})
fct_c(!!!lst)
}
x <- c("PH", "COND", "DBO")
y <- c("B", "A", "C")
custom_fct(x, y)
Редактировать.
Другой способ увидеть проблему, который я понял только после комментария OP, - это иметь вектор входных данных x, который должен быть приведен к фактору и двум векторам, одному из префиксов и одному из суффиксов. Следующая функция создает такой вектор и не требует внешнего пакета.
custom_fct2 <- function(x, prefix, suffix, sep = "_"){
lst <- lapply(prefix, function(p){
paste(p, suffix, sep = sep)
})
factor(x, levels = unlist(lst))
}
x <- c("DBO_A", "PH_A", "COND_A", "DBO_B",
"PH_B", "COND_B", "DBO_C", "PH_C", "COND_C")
a <- c("PH", "COND", "DBO")
b <- c("B", "A", "C")
custom_fct2(x, a, b)
#[1] DBO_A PH_A COND_A DBO_B PH_B COND_B DBO_C PH_C
#[9] COND_C
#9 Levels: PH_B PH_A PH_C COND_B COND_A COND_C DBO_B ... DBO_C
x в моем вопросе - это заданные данные. Я хочу, как использовать x для получения двух ожидаемых данных.
@DarrenTsai Я не понимаю, x даётся, а y нет? Хотите получить y из x? Функция в моем ответе делает то, что вы описываете в вопросе.
Да так здорово !! Большое Вам спасибо.
Но как я могу получить второй ожидаемый результат? custom_fct2(x, b, a)?
Для этого мы можем использовать base R. Используя sub, удалите подстроку в levels вектора, с помощью match создайте числовой индекс, проверив те значения, которые находятся в настраиваемом порядке, переназначьте levels из factor с помощью order, установив последовательность levels вектора на основе индекса matching
i1 <- match(sub("_.*", "", levels(x)), c("PH", "COND", "DBO"))
i2 <- match(sub(".*_", "", levels(x)), c("B", "A", "C"))
factor(x, levels = levels(x)[seq_along(levels(x))[order(i1, i2)]])
Во втором случае просто переверните индекс в order
factor(x, levels = levels(x)[seq_along(levels(x))[order(i2, i1)]])
Для многократного использования может быть заключен в функцию
f1 <- function(vec, lvls1, lvls2, flag = "former") {
i1 <- match(sub("_.*", "", levels(vec)), lvls1)
i2 <- match(sub(".*_", "", levels(vec)), lvls2)
if (flag == 'former') {
factor(vec, levels = levels(vec)[seq_along(levels(vec))[order(i1, i2)]])
} else {
factor(vec, levels = levels(vec)[seq_along(levels(vec))[order(i2, i1)]])
}
}
f1(x, c("PH", "COND", "DBO"), c("B", "A", "C"))
#[1] DBO_A PH_A COND_A DBO_B PH_B COND_B DBO_C PH_C COND_C
#Levels: PH_B PH_A PH_C COND_B COND_A COND_C DBO_B DBO_A DBO_C
f1(x, c("PH", "COND", "DBO"), c("B", "A", "C"), flag = "latter")
#[1] DBO_A PH_A COND_A DBO_B PH_B COND_B DBO_C PH_C COND_C
#Levels: PH_B COND_B DBO_B PH_A COND_A DBO_A PH_C COND_C DBO_C
эй, у вас отличный метод, но вы меняете мои исходные данные. Вы можете сравнить мои ожидаемые данные и ваш результат.
@DarrenTsai Извините, я забыл проверить вывод. Спасибо что подметил это. Починил это
Использование функций удобства data.tabletstrsplit и setorderv.
Создайте вектор (произвольных) имен столбцов для подстрок (cols <- c("V1", "V2")). Преобразуйте вектор в data.table (d <- data.table(x)). Разделите вектор на два столбца ((cols) := tstrsplit(x, split = "_")). Установите факторные уровни подстрок (factor(V1, levels = l1)). Упорядочивайте данные либо по первой подстроке, затем по второй подстроке, либо по второй, а затем по первой (setorderv(d, if (prec == 1) cols else rev(cols))). Используйте упорядоченный столбец «x» из таблицы data.table как уровни факторов вектора «x» (factor(x, levels = d$x)).
library(data.table)
f <- function(x, l1, l2, prec){
cols <- c("V1", "V2")
d <- data.table(x)
d[ , (cols) := tstrsplit(x, split = "_")]
d[ , `:=`(
V1 = factor(V1, levels = l1),
V2 = factor(V2, levels = l2))]
setorderv(d, if (prec == 1) cols else rev(cols))
factor(x, levels = d$x)
}
# First substring has precedence
f(x, l1 = c("PH", "COND", "DBO"), l2 = c("B", "A", "C"), prec = 1)
# [1] DBO_A PH_A COND_A DBO_B PH_B COND_B DBO_C PH_C COND_C
# Levels: PH_B PH_A PH_C COND_B COND_A COND_C DBO_B DBO_A DBO_C
# Second substring has precedence
f(x, l1 = c("PH", "COND", "DBO"), l2 = c("B", "A", "C"), prec = 2)
# [1] DBO_A PH_A COND_A DBO_B PH_B COND_B DBO_C PH_C COND_C
# Levels: PH_B COND_B DBO_B PH_A COND_A DBO_A PH_C COND_C DBO_C
Альтернатива base в том же духе, но вместо этого помещает подстроки в матрицу. Используйте стандартное регулярное выражение (см., Например, здесь) для захвата подстрок. Преобразуйте в коэффициент и установите уровни. Создайте индекс столбца (i <- c(1, 2, 1)[prec:(prec + 1)]). Уровни заказа «x» (as.character(x)[order(m[ , i[1]], m[ , i[2]])])).
f2 <- function(x, l1, l2, prec){
m <- cbind(factor(sub("_.*", "", x), l1), factor(sub(".*_", "", x), l2))
i <- c(1, 2, 1)[prec:(prec + 1)]
factor(x, levels = as.character(x)[order(m[ , i[1]], m[ , i[2]])])}
f2(x, l1, l2, prec = 1)
# [1] DBO_A PH_A COND_A DBO_B PH_B COND_B DBO_C PH_C COND_C
# Levels: PH_B PH_A PH_C COND_B COND_A COND_C DBO_B DBO_A DBO_C
f2(x, l1, l2, prec = 2)
# [1] DBO_A PH_A COND_A DBO_B PH_B COND_B DBO_C PH_C COND_C
# Levels: PH_B COND_B DBO_B PH_A COND_A DBO_A PH_C COND_C DBO_C
Это то, что мне нужно. Большое спасибо за этот ответ и исправление моего вопроса.
Вижу. Работает как хочу. Я очень благодарен за вашу доброту.
Вы можете сказать, как нет? Учитывая, как вы использовали expand.grid, вставка будет упорядочена сначала по x, а затем по y, так я понял, что вам нужно.