2014-10-18 3 views
1

Скажем, у меня есть следующий dataframe:Преобразование dataframe в расширенной матрицы в г

dfx <- data.frame(Var1=c("A", "B", "C", "D", "B", "C", "D", "C", "D", "D"), 
      Var2=c("E", "E", "E", "E", "A", "A", "A", "B", "B", "C"), 
      Var1out = c(1,-1,-1,-1,1,-1,-1,1,-1,-1), 
      Var2out= c(-1,1,1,1,-1,1,1,-1,1,1)) 

dfx 

    Var1 Var2 Var1out Var2out 
1  A E  1  -1 
2  B E  -1  1 
3  C E  -1  1 
4  D E  -1  1 
5  B A  1  -1 
6  C A  -1  1 
7  D A  -1  1 
8  C B  1  -1 
9  D B  -1  1 
10 D C  -1  1 

То, что вы видите здесь 10 строк, которые соответствуют Лицу между игроками A, B, C, D и E. Они играйте друг с другом один раз, а победитель каждого совпадения обозначается +1, а проигравший каждого совпадения обозначается -1 (помещается в соответствующий столбец Player Var1 приводит к Var1out, Player Var2 приводит к Var2out) ,

Желаемый результат.

Я хочу, чтобы преобразовать эту dataframe к этой матрице выходов (порядок строк не важны для меня, но, как вы можете видеть, каждая строка относится к уникальному матч-):

 A  B  C  D  E 
1  1  0  0  0 -1 
2  0 -1  0  0  1 
3  0  0 -1  0  1 
4  0  0  0 -1  1 
5  -1  1  0  0  0 
6  1  0 -1  0  0 
7  1  0  0 -1  0 
8  0 -1  1  0  0 
9  0  1  0 -1  0 
10  0  0  1 -1  0 

Что я сделал:

Мне удалось сделать эту матрицу обходным путем. Поскольку обходные пути имеют тенденцию быть медленными и менее удовлетворительными, мне было интересно, сможет ли кто-нибудь найти лучший способ.

Сначала я убедился, что в моих двух столбцах, содержащих игроков, есть уровни факторов, которые содержат все возможные игроки, которые когда-либо случаются (например, вы заметите, что игрок E никогда не встречается в Var1).

# Making sure Var1 and Var2 have same factor levels 
levs <- unique(c(levels(dfx$Var1), levels(dfx$Var2))) #get all possible levels of factors 
dfx$Var1 <- factor(dfx$Var1, levels=levs) 
dfx$Var2 <- factor(dfx$Var2, levels=levs) 

Затем я разделить dataframe на два - один для Var1 и Var1out, и один для Переменная2 и Var2out:

library(dplyr) 
temp.Var1 <- dfx %>% select(Var1, Var1out) 
temp.Var2 <- dfx %>% select(Var2, Var2out) 

Здесь я использую model.matrix расширить колонки от уровня фактора:

mat.Var1<-with(temp.Var1, data.frame(model.matrix(~Var1+0))) 
mat.Var2<-with(temp.Var2, data.frame(model.matrix(~Var2+0))) 

Затем я заменяю для каждой строки столбец «1», указывающий на наличие этого фактора, с правильным результатом и добавляем эти матрицы:

mat1 <- apply(mat.Var1, 2, function(x) ifelse(x==1, x<-temp.Var1$Var1out, x<-0) ) 
mat2 <- apply(mat.Var2, 2, function(x) ifelse(x==1, x<-temp.Var2$Var2out, x<-0) ) 

matX <- mat1+mat2 

matX 

    Var1A Var1B Var1C Var1D Var1E 
1  1  0  0  0 -1 
2  0 -1  0  0  1 
3  0  0 -1  0  1 
4  0  0  0 -1  1 
5  -1  1  0  0  0 
6  1  0 -1  0  0 
7  1  0  0 -1  0 
8  0 -1  1  0  0 
9  0  1  0 -1  0 
10  0  0  1 -1  0 

Хотя это работает, у меня есть ощущение, что, вероятно, я пропустил более простые решения этой проблемы. Благодарю.

ответ

2

Создайте пустую матрицу и использовать матрицу индексацию для заполнения соответствующих значений в:

cols <- unique(unlist(dfx[1:2])) 
M <- matrix(0, nrow = nrow(dfx), ncol = length(cols), dimnames = list(NULL, cols)) 
M[cbind(sequence(nrow(dfx)), match(dfx$Var1, cols))] <- dfx$Var1out 
M[cbind(sequence(nrow(dfx)), match(dfx$Var2, cols))] <- dfx$Var2out 
M 
#  A B C D E 
# [1,] 1 0 0 0 -1 
# [2,] 0 -1 0 0 1 
# [3,] 0 0 -1 0 1 
# [4,] 0 0 0 -1 1 
# [5,] -1 1 0 0 0 
# [6,] 1 0 -1 0 0 
# [7,] 1 0 0 -1 0 
# [8,] 0 -1 1 0 0 
# [9,] 0 1 0 -1 0 
# [10,] 0 0 1 -1 0 
2

Другой способ заключается в использовании acast

library(reshape2) 
    #added `use.names=FALSE` from @Ananda Mahto's comments 
dfy <- data.frame(Var=unlist(dfx[,1:2], use.names=FALSE), 
      VarOut=unlist(dfx[,3:4], use.names=FALSE), indx=1:nrow(dfx)) 


acast(dfy, indx~Var, value.var="VarOut", fill=0) 
# A B C D E 
#1 1 0 0 0 -1 
#2 0 -1 0 0 1 
#3 0 0 -1 0 1 
#4 0 0 0 -1 1 
#5 -1 1 0 0 0 
#6 1 0 -1 0 0 
#7 1 0 0 -1 0 
#8 0 -1 1 0 0 
#9 0 1 0 -1 0 
#10 0 0 1 -1 0 

Или используйте spread

library(tidyr) 
spread(dfy,Var, VarOut , fill=0)[,-1] 
# A B C D E 
#1 1 0 0 0 -1 
#2 0 -1 0 0 1 
#3 0 0 -1 0 1 
#4 0 0 0 -1 1 
#5 -1 1 0 0 0 
#6 1 0 -1 0 0 
#7 1 0 0 -1 0 
#8 0 -1 1 0 0 
#9 0 1 0 -1 0 
#10 0 0 1 -1 0 
+0

После просмотра этот ответ я понял, что если OP не против переименования своих столбцов, они могут использовать 'merged.st ack' из моего пакета splitstackshape. См. [This Gist] (https://gist.github.com/mrdwab/98175d209b642ee39ae9) с таймингами. – A5C1D2H2I1M1N2O1R2T1

+0

@ Ананда Махто Спасибо за тайминги. Я был немного удивлен, что «распространение» не так быстро. Возможно, 'dcast.data.table' станет лучшей альтернативой. – akrun

+0

В настоящее время ~ 45% времени в вашей функции расходуется на шаг «unlist». Это резко падает *** *** с помощью 'use.names = FALSE' ... но все еще не имеет шанса против подхода к индексированию матрицы :-) – A5C1D2H2I1M1N2O1R2T1

Смежные вопросы