Часто у меня есть фрейм данных, который содержит числовую переменную и категориальную переменную, и я хочу разделить числовую переменную в соответствии с категориальной переменной, выполнить некоторую операцию и собрать ее обратно в виде фрейма данных. Операция зависит от всей части числового вектора в категории и иногда возвращает вектор другой длины. Я знаю, как сделать это безобразно (см.
пример ниже), но это похоже на обычную операцию, поэтому мне интересно, есть ли более простой способ, о котором я не знаю. Мне было бы особенно интересно узнать, есть ли решение с использованием tidyverse
.
Вот пример того, о чем я говорю.
df = data.frame(y=1:10, g=rep(c("a", "b"), each=5))
Скажем, я хочу стандартизировать переменную y
в диапазоне от 0 до 1 для каждого уровня категориальной переменной . Вот общий способ сделать это:
do.call(
rbind,
lapply(unique(df$g),
function(level) {
y.current = df$y[df$g==level]
## perform some operation
y.new = (y.current-min(y.current))/
(max(y.current)-min(y.current))
return(data.frame(y=y.new,
g=level))
}
)
)
Это требует много печатания и не очень читабельно. Есть ли лучший способ?
Редактировать: Спасибо за отличные ответы. Единственное, что меня по-прежнему интересует, - это полностью общий способ сделать это с помощью tidyverse
. Если мы изменим пример на операцию, в которой размер числового вектора уменьшен, но больше единицы, комбинации group_by
/ mutate
/ summarize
не будут работать. Например, я хочу удалить наибольшее значение в каждой группе. Я могу сделать
library(dplyr)
df = data.frame(y=1:10, g=rep(c("a", "b"), each=5))
trans_df = df %>%
group_by(g) %>%
do(y=.$y[-which.max(.$y)])
Преобразованный фрейм данных trans_df
имеет переменную группировки с одним наблюдением на уровень и преобразованную переменную в виде списка для каждого уровня переменной группировки. Я могу поставить это в исходном формате, используя базу R с
data.frame(g=rep(trans_df$g, times=sapply(trans_df$y, length)),
y=do.call(c, trans_df$y))
но как я могу это сделать, используя tidyverse
?