#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
. Правильный элемент-это индекс значения inlabel
в уникальном отсортированном массиве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:
Вот альтернативный способ, которым вы можете достичь своей цели: Логика:
- сгруппировать по
Split_key
- Создайте группирующую переменную для
label
использованияrleid
функции изdata.table
- Используя некоторые функции 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