2016-09-09 2 views
2

Я хочу разделять переменные в соответствии с «ведущей» переменной. x3 в следующем случае:Применение dcast странным способом

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?


EDIT: Некоторые люди сказали, что сделать это ужасная идея, и что я, вероятно, не следует делать такие вещи. Позвольте мне уточнить, когда это может иметь смысл.

Представьте себе, что x3 является коммутатором для конкретного алгоритма. В этом случае возможны варианты a и b. Кроме того, x1 и x2 являются параметрами, которые могут выполнять оба алгоритма. К сожалению, оба алгоритма ведут себя по-разному при одних и тех же параметрах параметров для x1 и x2, поэтому имеет смысл обрабатывать их как отдельные функции, чтобы отнести их неконтролируемость к учетной записи.

+5

Ну, есть 'merge (df [df $ x3 ==" a ",], df [df $ x3 ==" b ",], by =" x3 ", all = TRUE)', но это естественно что не было бы изящного способа получить такой беспорядочный выход. – Frank

+0

@agenis Это исправлено. –

+0

@Frank Это становится беспорядочным, если я не сейчас дискретные значения 'x3'. –

ответ

5

Вот решение, использующее создание фиктивных условий взаимодействия с 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).

+1

Мне нравится ваш подход model.matrix. Интересно, насколько это яснее (хотя и медленнее): 'm <- model.matrix (~ 0 + x3, df); m [m == 0] <- NA; data.frame (df $ x3, apply (m, 2, "*", df [-3])) '<не полностью протестировано> – user20650

+1

@ user20650 спасибо за ваш вклад, это действительно приятное альтернативное решение. Вы можете опубликовать его в качестве ответа. Мне было интересно, можем ли мы свести это в oneliner – agenis

+1

, и вы также можете использовать 'model.matrix (~ 0 + x3, df)^NA' ,, а не явно устанавливать нуль в NA. Итак, в одной строке 'data.frame (df $ x3, apply (model.matrix (~ 0 + x3, df)^NA, 2," * ", df [-3]))', но на самом деле нельзя назвать один лайнер, так как это несколько расчетов. – user20650

3

Это может быть достигнуто путем использования 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 миллисекунд).

+0

хорошее использование переменной строки – agenis

2

Основное решение, которое я придумал себе:

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 
+0

Я использую это примерно в 3 раза быстрее, чем решение @ agenis, и в 5 раз быстрее, чем мой. Красиво сделано. – Benjamin

1

Вы можете использовать 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 отдельно, то после создания переменного требуемые заголовки, снова распространяет данные.

+0

Интересное решение, но вы должны отметить, что он переупорядочивает строки и сортирует их по «x3». –

+0

Правда, если порядок строк важен, вы можете добавить 'arr (rows)%>%' перед оператором select. Это все еще не самый быстрый. –