Заполните новый столбец в кадре данных поиском из двойной матрицы - PullRequest
8 голосов
/ 22 марта 2012

У меня есть датафрейм df:

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

И двойная матрица m с именованными строками / столбцами

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

Я бы хотел добавить новый столбец в DF, чтобы получить:

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

Я пытался сделать это с помощью следующего кода, но, похоже, он не работает:

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

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

Ответы [ 6 ]

7 голосов
/ 22 марта 2012

Довольно простой (и быстрый!) Альтернативой является использование матрицы для индексации в вашей матрице:

# 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

Функция match используется для поиска соответствующего числового индекса для конкретной строки.

Обратите внимание, что в более новой версии R (2.13 и новее, я думаю) вы можете использовать символьные строки в индексной матрице.К сожалению, столбцы цвета и формы обычно factors, и cbind это не нравится (используются целочисленные коды), поэтому вам необходимо привести их к as.character:

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

... Я подозреваю, что использование match более эффективно.

РЕДАКТИРОВАТЬ Я измерил, и кажется, что использование match примерно на 20% быстрее:

# 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
5 голосов
/ 22 марта 2012

Я думаю, что я мог бы выиграть конкурс на самый короткий ответ, если это будут векторы персонажей, а не факторы, которые могут быть более ожидаемыми, если вы не приложите особых усилий, чтобы избежать.На самом деле он добавляет только cbind, чтобы преобразовать два "символьных" вектора df в матрицу из двух столбцов, ожидаемую функцией [.matrix, которую вы очень близко использовали к успеху.(И это также кажется достаточно выразительным.)

# 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
2 голосов
/ 22 марта 2012

Другой ответ Использование пакетов reshape2 и plyr (необязательно только для объединения).

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

Надеюсь, что эта помощь

1 голос
/ 22 марта 2012

Вы также можете преобразовать матрицу m в вектор, а затем сопоставить идентификатор со значениями цвета и формы:

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)

Следующий шаг сопоставляет цвет в df с соответствующим dimname в матрице m, затем добавляет целое число, соответствующее форме. Результат в индексе вектора m с соответствующим идентификатором.

df$ID<-mVec[match(df$colour, dimnames(m)[[1]]) + (dim(m)[1]*
  (match(df$shape, dimnames(m)[[2]]) - 1))]
1 голос
/ 22 марта 2012

merge() ваш друг здесь. Чтобы использовать его, нам необходим соответствующий фрейм данных, содержащий объединенную версию вашей матрицы идентификаторов. Я создаю это как newdf с кодом ниже:

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)))

Что приводит к:

> 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

Затем с вашими исходными данными в объекте df2, определенном с помощью

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

использование 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

Вы можете сохранить это и переставить столбцы, если вам это нужно:

> 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
0 голосов
/ 22 марта 2012
#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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...