Применение dcast странным образом
Я хочу разделить переменные в соответствии с "ведущей" переменной. х3 в следующем случае:
set.seed(2)
df = data.frame(x1 = sample(4), x2 = sample(4), x3 = sample(letters[1:2], size = 4, replace = TRUE))
df
# x1 x2 x3
# 1 1 4 a
# 2 3 3 b
# 3 2 1 b
# 4 4 2 a
# Desired output
# x3 x1.a x2.a x1.b x2.b
# a 1 4 NA NA
# b NA NA 3 3
# b NA NA 2 1
# a 4 2 NA NA
Я как-то чувствую, что это может быть достигнуто с reshape2::dcast()
но я мог заставить его работать только для двух переменных:
reshape2::dcast(df[,2:3], seq_along(x3) ~ x3, value.var = "x2")[, -1]
# a b
# 1 2 NA
# 2 NA 1
# 3 NA 3
# 4 4 NA
Но может быть, это всего лишь полное злоупотребление dcast
, Есть ли элегантное решение этой проблемы, без расщепления и слияния df
?
РЕДАКТИРОВАТЬ: Некоторые люди упоминали, что сделать это ужасная идея, и что я, вероятно, не должен делать такие вещи. Позвольте мне уточнить, когда это может иметь смысл.
Представить x3
это переключатель для конкретного алгоритма. В этом случае a
а также b
варианты. более того x1
а также x2
параметры, которые могут принимать оба алгоритма. К сожалению, оба алгоритма ведут себя по-разному при одинаковых настройках параметров для x1
а также x2
поэтому имеет смысл обращаться с ними как с отдельными функциями, чтобы учитывать их некорролируемость.
4 ответа
Вот решение, использующее создание фиктивных терминов взаимодействия с X3
, Вероятно, можно поместить весь этот код в одну строку, используя dplyr
или же data.table
но вот оно:
temp <- model.matrix( ~ (x1+x2):x3-1, df)
temp[model.matrix( ~ (I(x1+1)+I(x2+1)):x3-1, df) == temp] <- NA
data.frame(df$x3, temp)
#### df.x3 x1.x3a x1.x3b x3a.x2 x3b.x2
#### a 1 NA 4 NA
#### b NA 3 NA 3
#### b NA 2 NA 1
#### a 4 NA 2 NA
Окончательное название и порядок столбцов немного отличается от вас.
Примечание: (назначение второй строки кода). функция model.matrix создает нули вместо NAs
Таким образом, невозможно определить разницу с уже существующими нулями. Вторая строка - это хитрость, чтобы найти только окончательные NA (она работает, создавая вторую матрицу модели, изменяя ее значения с помощью +1
).
Это может быть достигнуто с помощью melt
а также dcast
если добавить еще один столбец и сделать посредника melt
,
library(reshape2)
library(magrittr)
set.seed(2)
df = data.frame(x1 = sample(4), x2 = sample(4), x3 = sample(letters[1:2], size = 4, replace = TRUE))
df$row <- 1:nrow(df)
melt(df,
id.vars = c("row", "x3"),
measure.vars = c("x1", "x2")) %>%
dcast(row ~ x3 + variable,
value.var = "value")
Тем не менее, он работает в 2-3 раза медленнее, чем решение Agenis, даже когда я увеличиваю размер фрейма данных до 10000 строк. (8 против 16 миллисекунд).
Основное решение, которое я придумал сам:
cat.var = "x3"
cont.vars = setdiff(colnames(df), cat.var)
categories = unique(df[[cat.var]])
res = lapply(categories, function(x) {
this.df = df[, cont.vars, drop = FALSE]
this.df[df[[cat.var]] != x,] = NA
setNames(this.df, paste0(x,".",colnames(this.df)))
})
res = do.call(cbind, c(list(df[, cat.var, drop=FALSE]), res))
res
# x3 a.x1 a.x2 b.x1 b.x2
# 1 a 1 4 NA NA
# 2 b NA NA 3 3
# 3 b NA NA 2 1
# 4 a 4 2 NA NA
Вы могли бы использовать tidyr
library(tidyr);library(dplyr)
df <- df %>% mutate(rows=rownames(.)) %>%
gather(., key="vars", value= "val", -x3,-rows) %>%
mutate(vars= paste(x3,vars, sep=".")) %>%
spread(., key = vars, value = val) %>%
select(-rows)
Он собирает набор данных в длинную форму, удерживая переменную x3 отдельно, а затем, после создания необходимых заголовков переменных, снова распространяет данные.