2015-05-13 7 views
5

einen "leeren" Indikatoren Datenrahmen Gegeben:Eine effiziente Möglichkeit, mehrere Indikatorvariablen pro Zeile anzugeben?

Index Ind_A Ind_B 
    1  0  0 
    2  0  0 
    3  0  0 
    4  0  0 

und einen Datenrahmen von Werten:

Index Indicators 
    1   Ind_A 
    3   Ind_A 
    3   Ind_B 
    4   Ind_A 

Ich mag am Ende mit:

Index Ind_A Ind_B 
    1  1  0 
    2  0  0 
    3  1  1 
    4  1  0 

Gibt es eine Möglichkeit zu tun dies ohne eine for-Schleife?

+0

Sie sollten erwägen eine neue Frage aus der in Ihrem bearbeiten beschriebene Situation zu machen. – Frank

+0

@Frank Danke Ich habe http: // stackoverflow.com/questions/30243920/An-effiziente-Weg-zu-Multiple-Indikator-Variablen-pro-Zeile-mit-Composite – lapolonio

Antwort

1
indicator <- data.frame(Index=1:4,Ind_A=rep(0,4),Ind_B=rep(0,4)); 
values <- data.frame(Index=c(1,3,3,4),Indicators=c('Ind_A','Ind_A','Ind_B','Ind_A')); 
indicator[cbind(match(values$Index,indicator$Index),match(values$Indicators,names(indicator)))] <- 1; 
indicator; 
## Index Ind_A Ind_B 
## 1  1  1  0 
## 2  2  0  0 
## 3  3  1  1 
## 4  4  1  0 

Die wichtigste Änderung in Ihrem bearbeiten ist, dass indicator$Index jetzt tut nicht eindeutige Werte (zumindest nicht auf eigene) enthalten, so dass eine einfache match()values$Index-indicator$Index unzureichend ist. Stattdessen müssen wir einen outer() Gleichheitstest sowohl auf Index als auch auf Index2 ausführen, um eine Matrix von logischen Daten zu erhalten, die angeben, welche Zeilen in indicator Zeile values auf beiden Schlüsseln übereinstimmen. Unter der Annahme, dass der zweispaltige zusammengesetzte Schlüssel eindeutig ist, können wir dann den Zeilenindex in indicator aus dem linearen (Vektor-) Index berechnen, der von which() zurückgegeben wird.

indicator[cbind((which(outer(values$Index,indicator$Index,`==`)&outer(values$Index2,indicator$Index2,`==`))-1)%/%nrow(values)+1,match(values$Indicators,names(indicator)))] <- 1; 
indicator; 
## Index Index2 Ind_A Ind_B 
## 1  1  10  1  1 
## 2  1  11  1  0 
## 3  2  10  0  1 
## 4  2  12  1  0 
## 5  3  10  1  0 
## 6  3  12  1  0 
## 7  4  10  1  1 
## 8  4  12  1  0 

Hier ist eine andere Lösung mit merge():

indicator[cbind(merge(values,cbind(indicator,row=1:nrow(indicator)))$row,match(values$Indicators,names(indicator)))] <- 1; 
indicator; 
## Index Index2 Ind_A Ind_B 
## 1  1  10  1  1 
## 2  1  11  1  0 
## 3  2  10  0  1 
## 4  2  12  1  0 
## 5  3  10  1  0 
## 6  3  12  1  0 
## 7  4  10  1  1 
## 8  4  12  1  0 

Leistung

Die erste Lösung performanter ist:

first <- function() indicator[cbind((which(outer(values$Index,indicator$Index,`==`)&outer(values$Index2,indicator$Index2,`==`))-1)%/%nrow(values)+1,match(values$Indicators,names(indicator)))] <<- 1; 
second <- function() indicator[cbind(merge(values,cbind(indicator,row=1:nrow(indicator)))$row,match(values$Indicators,names(indicator)))] <<- 1; 
N <- 10000; 
system.time({ replicate(N,first()); }); 
## user system elapsed 
## 2.032 0.000 2.041 
system.time({ replicate(N,first()); }); 
## user system elapsed 
## 2.047 0.000 2.038 
system.time({ replicate(N,second()); }); 
## user system elapsed 
## 12.578 0.000 12.592 
system.time({ replicate(N,second()); }); 
## user system elapsed 
## 12.64 0.00 12.66 
+0

können Sie meine Bearbeitung ansehen und diese auch beantworten? Ich habe versucht, mit denen mit mehreren Bedingungen, aber das hat nicht geholfen .... – lapolonio

+0

@ blazinazin215 siehe bearbeiten. – bgoldst

1

I Matrizen verwenden würde:

ind_mat <- as.matrix(ind_df[,-1]); rownames(ind_mat) <- ind_df[,1] 
val_mat <- cbind(match(val_df$Index,ind_df[,1]),match(val_df$Indicators,names(ind_df[-1]))) 

ind_mat[val_mat] <- 1L 
# Ind_A Ind_B 
# 1  1  0 
# 2  0  0 
# 3  1  1 
# 4  1  0 

Sie brauchen wahrscheinlich nicht "Index" als eine Spalte und können nur diese als rownames setzen. Wenn (i) Ihre Wertematrix relativ zur Indexmatrix klein ist und (ii) Ihre Indexspalte gleich 1:nrow(ind_df) ist, sollten Sie in einer dünn besetzten Matrix speichern.


In Bezug auf Zwang zu einer Matrix, dauert es sehr wenig Zeit und den Aufwand, zu zwingen, später für alle Matrixoperationen vermeiden. Hier ein Beispiel:

n = 1e4 
nind = 1e3 
y <- rnorm(n) 
x <- matrix(sample(0:1,size=n*nind,replace=TRUE),ncol=nind) 
xd <- data.frame(1:nrow(x),x) 

# timing: 0.04 seconds on my computer 
system.time(as.matrix(xd[,-1])) 

# messiness, e.g., for OLS y~0+x: immense 
solve(t(as.matrix(xd[,-1]))%*%as.matrix(xd[,-1]))%*%(t(as.matrix(xd[,-1]))%*%y) 

Die letzte Zeile können Sie einen matrix um zu vermeiden halten; Ich verstehe den Punkt nicht.

+2

Ich glaube nicht, dass es einen großen Unterschied zwischen den Antworten gibt. Außerdem könnte 'as.matrix' einen Overhead haben. Das größte Problem hierbei ist, dass Sie nicht mit der Spalte "Indx" im ersten Datensatz übereinstimmen, die nicht zwingend 1: 4 sein muss. –

+0

@DavidArenburg Okay, jetzt stimmt es mit der Indexspalte überein. Es gibt einen ziemlich großen Unterschied zwischen der Verwendung einer Matrix und der Verwendung eines data.frame für Matrixberechnungen. Zwang und Zwang vermeiden. – Frank

+0

Um, downvoter: Ich habe das "nicht passende" Ding, das David erwähnt hat, korrigiert. – Frank

1

ich tun würde, direkt:

df = transform(df, Index=factor(Index, level=min(Index):max(Index))) 
as.data.frame.matrix(table(df)) 
# Ind_A Ind_B 
#1  1  0 
#2  0  0 
#3  1  1 
#4  1  0 

Daten:

df = structure(list(Index = c(1, 3, 3, 4), Indicators = c("Ind_A", 
"Ind_A", "Ind_B", "Ind_A")), .Names = c("Index", "Indicators" 
), row.names = c(NA, -4L), class = "data.frame") 
Verwandte Themen