2012-03-21 19 views
8

Mam df dataframe:Wypełnianie nową kolumnę w dataframe z odnośnika z podwójnej osnowie

colour shape 
'red' circle 
'blue' square 
'blue' circle 
'green' sphere 

i podwójną m matrycy z wymienionych wierszy/kolumn

 circle square sphere 
red 1  4  7 
blue 2  5  8 
green 3  6  9 

ja jak dodać nową kolumnę do DF takie, że otrzymamy:

id colour shape 
1 'red' circle 
5 'blue' square 
2 'blue' circle 
9 'green' sphere 

próbowałem robić to z następującego kodu, ale nie wydaje się działać:

df$id <- m[df$colour,df$shape] 

Próbowałem również apply(); i podobne, ale bez powodzenia. Czy ktoś może mi powiedzieć właściwe podejście do tego bez użycia pętli?

+0

Dziękuję wszystkim za pomoc. Obie odpowiedzi @Tommy poniżej, a poniżej odpowiedzi DWin działają świetnie. Poszedłem z DWinem na mój scenariusz, ponieważ miałem wektory postaci w moich prawdziwych danych. – Ina

Odpowiedz

5

myślę, że wygram najkrótszą konkurs odpowiedzi tutaj tak długo, jak te, które są wektorami charakter raczej niż czynniki, które mogą być bardziej spodziewane, chyba że dokonane wysiłku specifid uniknąć. To naprawdę tylko dodaje cbind, aby przekonwertować dwa wektory znakowe "df" na matrycę z dwiema kolumnami oczekiwaną przez funkcję [.matrix, która była bardzo bliska powodzenia w użyciu. (A także wydaje się dość wyrazisty).

# Data construct 
d <- data.frame(color=c('red','blue','blue','green'), 
shape=c('circle','square','circle','sphere'), stringsAsFactors=FALSE) 
m <- matrix(1:9, 3,3, dimnames=list(c('red','blue','green'), c('circle','square','sphere'))) 
# Code: 

d$id <- with(d, m [ cbind(color, shape) ]) 
d 
    color shape id 
1 red circle 1 
2 blue square 5 
3 blue circle 2 
4 green sphere 9 
+0

Należy zauważyć, że działa to tylko wtedy, gdy poziomy w 'd' mają taką samą kolejność jak rownames/colnames wm. Próbowałem to wyjaśnić w mojej odpowiedzi. Spróbuj jeszcze raz z 'm <-m [3: 1,]' i zobacz, czy to się nie udaje ... – Tommy

+0

Och, przepraszam, nie czytałem wystarczająco uważnie: biorąc pod uwagę, że 'd' zawiera wektory znaków, a nie czynniki, które faktycznie działa ... Moje rozwiązanie działa jednak w obu przypadkach ;-) – Tommy

+2

Można również użyć 'm [cbind (as.character (d $ color), as.character (d $ shape)]' który moim zdaniem jest zarówno ogólny, jak i bardziej jasne: –

1

merge() jest tu twoim przyjacielem. Aby z niego skorzystać, potrzebujemy odpowiedniej ramki danych do scalenia z zawartą w stosie wersją macierzy ID. Tworzę że newdf z poniższym kodem:

df <- data.frame(matrix(1:9, ncol = 3)) 
colnames(df) <- c("circle","square","sphere") 
rownames(df) <- c("red","blue","green") 

newdf <- cbind.data.frame(ID = unlist(df), 
          expand.grid(colour = rownames(df), 
             shape = colnames(df))) 

co skutkuje:

> newdf 
     ID colour shape 
circle1 1 red circle 
circle2 2 blue circle 
circle3 3 green circle 
square1 4 red square 
square2 5 blue square 
square3 6 green square 
sphere1 7 red sphere 
sphere2 8 blue sphere 
sphere3 9 green sphere 

potem z oryginalnych danych w obiekcie df2, zdefiniowanego przy użyciu

df2 <- data.frame(colour = c("red","blue","blue","green"), 
        shape = c("circle","square","circle","sphere")) 

użycie merge()

> merge(newdf, df2, sort = FALSE) 
    colour shape ID 
1 red circle 1 
2 blue circle 2 
3 blue square 5 
4 green sphere 9 

można zapisać, że i zmienić kolejność kolumn, jeśli trzeba, że: (! I szybki)

> res <- merge(newdf, df2, sort = FALSE) 
> res <- res[,c(3,1,2)] 
> res 
    ID colour shape 
1 1 red circle 
2 2 blue circle 
3 5 blue square 
4 9 green sphere 
7

Raczej prostą alternatywą jest zastosowanie matrycy do indeksu do swojej matrycy:

# Your data 
d <- data.frame(color=c('red','blue','blue','green'), shape=c('circle','square','circle','sphere')) 
m <- matrix(1:9, 3,3, dimnames=list(c('red','blue','green'), c('circle','square','sphere'))) 

# Create index matrix - each row is a row/col index 
i <- cbind(match(d$color, rownames(m)), match(d$shape, colnames(m))) 

# Now use it and add as the id column... 
d2 <- cbind(id=m[i], d) 

d2 
# id color shape 
#1 1 red circle 
#2 5 blue square 
#3 2 blue circle 
#4 9 green sphere 

The Funkcja match służy do znalezienia odpowiedniego indeksu numerycznego dla określonego ciągu znaków.

Zauważ, że w nowszej wersji R (2.13 i nowszej, myślę), możesz użyć ciągów znaków w macierzy indeksu. Niestety, kolumny kolor i kształt są zwykle factors i cbind nie podoba (używa kodów Integer), więc trzeba zmusić je as.character:

i <- cbind(as.character(d$color), as.character(d$shape)) 

... Podejrzewam, że za pomocą match jest jednak bardziej wydajny.

EDIT, że mierzy się i wydaje się, że około 20% krótszy w użyciu match:

# Make 1 million rows 
d <- d[sample.int(nrow(d), 1e6, TRUE), ] 

system.time({ 
    i <- cbind(match(d$color, rownames(m)), match(d$shape, colnames(m))) 
    d2 <- cbind(id=m[i], d) 
}) # 0.46 secs 


system.time({ 
    i <- cbind(as.character(d$color), as.character(d$shape)) 
    d2 <- cbind(id=m[i], d) 
}) # 0.55 secs 
+0

Tak długo, jak @Tommy go podnosi, rozwiązanie konwersji m na wektor zajmuje 0,14 sekundy w porównaniu do 0,50 sekundy dla pierwszego przykładu powyżej na moim komputerze;) – BenBarnes

+0

Mam zaznaczone odpowiedź @DWin są poprawne, ponieważ jest to jedna Użyłem (wolę prostotę i nie mam ograniczeń czasowych), ale ta odpowiedź również działa świetnie i naprawdę doceniam wysiłek, jaki w nią włożyłam. Dzięki! – Ina

1

Można także przekształcić m matrycy z wektorem, a następnie dopasować się identyfikator do wartości koloru i kształtu :

df<-data.frame(colour=c("red","blue","blue","green"), 
    shape=c("circle","square","circle","sphere")) 


m<-matrix(1:9,nrow=3,dimnames=list(c("red","blue","green"), 
    c("circle","square","sphere"))) 


mVec<-as.vector(m) 

Kolejny krok dopasowuje kolor w df do odpowiedniego dimname w matrycy, następnie dodaje liczbę całkowitą odpowiadającą kształtowi. Wynik w indeksie wektora m z odpowiednim ID.

df$ID<-mVec[match(df$colour, dimnames(m)[[1]]) + (dim(m)[1]* 
    (match(df$shape, dimnames(m)[[2]]) - 1))] 
+0

+1 za bycie najszybszym! – Tommy

2

Inna odpowiedź Używanie reshape2 i plyr (opcja tylko dla przyłączenia) opakowaniach.

require(plyr) 
require(reshape2) 

Df <- data.frame(colour = c("red", "blue", "blue", "green"), 
        shape = c("circle", "square", "circle", "sphere")) 

Mat <- matrix(1:9, dimnames = list(c("red", "blue", "green"), 
            c("circle", "square", "sphere")), 
        nrow = 3) 

Df2 <- melt.array(Mat, varnames = c("colour", "shape")) 

join(Df, Df2) 
result <- join(Df, Df2) 

join(Df, Df2) 
Joining by: colour, shape 
    colour shape value 
1 red circle  1 
2 blue square  5 
3 blue circle  2 
4 green sphere  9 

Nadzieja ta pomoc

0
#recreating your data 
dat <- read.table(text="colour shape 
'red' circle 
'blue' square 
'blue' circle 
'green' sphere", header=TRUE) 

d2 <- matrix(c(1:9), ncol=3, nrow=3, byrow=TRUE) 
dimnames(d2) <-list(c('circle', 'square', 'sphere'), 
c("red", "blue", "green")) 
d2<-as.table(d2) 

#make a list of matching to the row and column names of the look up matrix 
LIST <- list(match(dat[, 2], rownames(d2)), match(dat[, 1], colnames(d2))) 
#use sapply to index the lookup matrix using the row and col values from LIST 
id <- sapply(seq_along(LIST[[1]]), function(i) d2[LIST[[1]][i], LIST[[2]][i]]) 
#put it all back together 
data.frame(id=id, dat) 
Powiązane problemy