Обработка столбца кадра данных с помощью str_split с использованием значений в другом столбце R

#r #dataframe #vectorization

Вопрос:

Мне интересно, есть ли более приятный способ решить следующую проблему

У меня есть фрейм данных со следующей структурой примера:

Split_key этикетка вложенная метка
A_B_C 7 «»
A_B_C 7 «»
A_B_C 8 «»
A_B_C 8 «»
A_B_C 10 «»
A_B_C 10 «»
D_E_F 2 «»
D_E_F 7 «»
D_E_F 15 «»
G_H_I 1 «»
G_H_I 2 «»
G_H_I 3 «»

Я хочу заполнить sub_label значением, которое соответствует разделению значения в Split_key на символ «_» и захватывает правильный элемент на основе метки. Правильный элемент-это индекс значения в метке в уникальном отсортированном массиве меток, имеющих одинаковое значение в Split_key.

Правильный конечный результат показан здесь.

Split_key этикетка вложенная метка
A_B_C 7 A
A_B_C 7 A
A_B_C 8 B
A_B_C 8 B
A_B_C 10 C
A_B_C 10 C
D_E_F 2 D
D_E_F 7 E
D_E_F 15 F
G_H_I 1 G
G_H_I 2 H
G_H_I 3 Я

Вот моя первая попытка.

 for (row_n in 1:nrow(df)){  df%gt;%filter(`Split_key`==df[row_n,"Split_key"][[1]])-gt;duplicates  shiftlt;-which(sort(unique(duplicates$label))==df[row_n,"label"][[1]])  df[row_n,"sub_label"]lt;-str_split(df[row_n,"Split_key"],"_")[[1]][shift] }  

Это решение работает, но медленнее, чем хотелось бы, с большими кадрами данных. Есть ли способ выполнить эту задачу без использования цикла for?

Комментарии:

1. Не могли бы вы подробнее рассказать, как label используется для определения того, из какого компонента извлекать Split key ?

2. Я могу почти гарантировать, что есть лучший способ извлечь это, чем for цикл, повторяющий каждую строку. Но… брать "A_B_C" 7 и каким-то образом придумывать "A" -это для меня потеряно.

3. @r2evans Я думаю, что ответ кроется в этом абзаце: «Я хочу заполнить sub_label значение, которое соответствует разделению значения на Split_key "_" символ и захватывает правильный элемент на основе label . Правильный элемент-это индекс значения in label в уникальном отсортированном массиве label s, в котором используется одно и то же значение Split_key

4. То есть… действительно непонятно …

5. Думаю, я понял, @Грег, я действительно ценю, что ты остался со мной там … Я думаю, что мой ответ отражает то, что вы там говорите. Спасибо!

Ответ №1:

Мы можем использовать factor маршрут, т. е. после группировки по «Split_key», scan first элемент «Split_key» и использовать integer преобразованный factor столбец «метка» в качестве индекса

 library(dplyr) df %gt;%  group_by(Split_key) %gt;%   mutate(sub_label = scan(text = first(Split_key), what = "",   sep="_", quiet = TRUE)[as.integer(factor(label))]) %gt;%  ungroup  

-выход

 # A tibble: 12 × 3  Split_key label sub_label  lt;chrgt; lt;intgt; lt;chrgt;   1 A_B_C 7 A   2 A_B_C 7 A   3 A_B_C 8 B   4 A_B_C 8 B   5 A_B_C 10 C   6 A_B_C 10 C   7 D_E_F 2 D   8 D_E_F 7 E   9 D_E_F 15 F  10 G_H_I 1 G  11 G_H_I 2 H  12 G_H_I 3 I   

данные

 df lt;- structure(list(Split_key = c("A_B_C", "A_B_C", "A_B_C", "A_B_C",  "A_B_C", "A_B_C", "D_E_F", "D_E_F", "D_E_F", "G_H_I", "G_H_I",  "G_H_I"), label = c(7L, 7L, 8L, 8L, 10L, 10L, 2L, 7L, 15L, 1L,  2L, 3L), sub_label = c(NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,  NA, NA)), class = "data.frame", row.names = c(NA, -12L))  

Ответ №2:

Это будет быстрее, чем цикл:

 library(dplyr) dat %gt;%  group_by(Split_key) %gt;%  mutate(sub_label2 = strsplit(Split_key[1], "_")[[1]][ match(label, sort(unique(label))) ]) %gt;%  ungroup() # # A tibble: 12 x 4 # Split_key label sub_label sub_label2 # lt;chrgt; lt;intgt; lt;chrgt; lt;chrgt;  # 1 A_B_C 7 A A  # 2 A_B_C 7 A A  # 3 A_B_C 8 B B  # 4 A_B_C 8 B B  # 5 A_B_C 10 C C  # 6 A_B_C 10 C C  # 7 D_E_F 2 D D  # 8 D_E_F 7 E E  # 9 D_E_F 15 F F  # 10 G_H_I 1 G G  # 11 G_H_I 2 H H  # 12 G_H_I 3 I I   

Если внутри закодировано меньше элементов Split_key , чем в отдельных значениях sub_label , то вы получите NA для этих строк.

Проходной:

  • group_by(Split_key) : поскольку нам нужно отслеживать уникальность label для каждого Split_key , мы группируемся в этом поле и упрощаем обработку до одной группы за раз;
  • strsplit(Split_key[1], ")")[[1]] : в пределах определенной группы нам нужно разделить только одно из Split_key значений, а не все из них (поскольку все они идентичны), это приводит к внутреннему вектору, такому как c("A", "B", "C") в первой группе;
  • match(label, sort(unique(label))) переводит (первая группа), в match(c(7,7,8,8,10,10), c(7,8,10)) которую переводится c(1,1,2,2,3,3) ; это используется для индексирования вектора c("A","B","C") из предыдущего маркера

Комментарии:

1. Хорошая работа по векторизации решения (насколько это возможно)!

2. Очень элегантное решение, я не знал о комбинации функций группы и разгруппировки, которая действительно делает это возможным. Спасибо!

3. Я согласен: понимание того, как использовать группировку при обработке данных, является огромным преимуществом; не случайно все три ответа используют ее.

Ответ №3:

Вот альтернативный способ, которым вы можете достичь своей цели: Логика:

  1. сгруппировать по Split_key
  2. Создайте группирующую переменную для label использования rleid функции из data.table
  3. Используя некоторые функции stringr, чтобы получить цель:
 library(dplyr) library(stringr) library(data.table) df %gt;%   group_by(Split_key) %gt;%   mutate(group = rleid(label)) %gt;%  mutate(sub_label= str_sub(str_replace_all(Split_key, "[^[:alnum:]]", ""), group, group), .keep="unused")  
 Split_key label sub_label  lt;chrgt; lt;intgt; lt;chrgt;   1 A_B_C 7 A   2 A_B_C 7 A   3 A_B_C 8 B   4 A_B_C 8 B   5 A_B_C 10 C   6 A_B_C 10 C   7 D_E_F 2 D   8 D_E_F 7 E   9 D_E_F 15 F  10 G_H_I 1 G  11 G_H_I 2 H  12 G_H_I 3 I