Пользовательские уровни факторов в составной строке

У меня есть факторная переменная, которая состоит из двух подстрок, разделенных _, например 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

Если я не определю уровни факторов, они будут расположены в алфавитном порядке. Теперь я хочу установить уровни строк слева и справа от разделителя _, например

  1. PH <COND <DBO слева (LHS).
  2. 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, ...)), но у меня больше уровней, чем показано выше, так что это будет выглядеть нелепо.

Примечание: Я не хочу менять порядок моих данных, только порядок уровней.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
0
176
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Как упирается что-то вроде

x <- with(expand.grid(x = c("DBO", "PH", "COND"), y = c("A", "B", "C")),
          factor(paste(x, y, sep = "_"), levels = paste(x, y, sep = "_")))

Вам не нужно записывать все возможные уровни, только уровни одной и другой стороны.

Вы можете сказать, как нет? Учитывая, как вы использовали expand.grid, вставка будет упорядочена сначала по x, а затем по y, так я понял, что вам нужно.

Joseph Clark McIntyre 06.01.2019 19:28

Дело не в том, как я производю свой x. Это просто воспроизводимые данные. Пожалуйста, прочтите мой ожидаемый результат.

Darren Tsai 06.01.2019 19:31

Используя 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 для получения двух ожидаемых данных.
Darren Tsai 06.01.2019 19:44

@DarrenTsai Я не понимаю, x даётся, а y нет? Хотите получить y из x? Функция в моем ответе делает то, что вы описываете в вопросе.

Rui Barradas 06.01.2019 19:49

Да так здорово !! Большое Вам спасибо.

Darren Tsai 06.01.2019 20:08

Но как я могу получить второй ожидаемый результат? custom_fct2(x, b, a)?

Darren Tsai 06.01.2019 20:12
Ответ принят как подходящий

Для этого мы можем использовать 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

эй, у вас отличный метод, но вы меняете мои исходные данные. Вы можете сравнить мои ожидаемые данные и ваш результат.

Darren Tsai 06.01.2019 19:55

@DarrenTsai Извините, я забыл проверить вывод. Спасибо что подметил это. Починил это

akrun 06.01.2019 20:00

Использование функций удобства 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

Это то, что мне нужно. Большое спасибо за этот ответ и исправление моего вопроса.

Darren Tsai 06.01.2019 21:49

Вижу. Работает как хочу. Я очень благодарен за вашу доброту.

Darren Tsai 07.01.2019 06:45

Другие вопросы по теме