Например: у меня есть фрейм данных с именем table:

Cn c1 c2 c3 c4
c3 1  3  5  6
c2 4  6  7  9

Я хочу создать новый столбец со значением, содержащимся в столбце, с именем столбца в Cn, чтобы он выглядел так:

Cn c1 c2 c3 c4 NewCol
c3 1  3  5  6  5
c2 4  6  7  9  6

Моя попытка была table$NewCol<-table[,table$Cn]

Однако вместо того, чтобы возвращать 1 значение для каждой строки, table$NewCol[1] представляет собой вектор, содержащий (5, 3), который ссылается на (c3, c2) в столбце Cn, что означает, что для каждой строки все строки Cn ищутся и помещаются в новую переменную.

Я знаю, что могу использовать циклы, но я имею дело с фреймом данных с более чем 7 миллионами записей, и цикл выполняется очень медленно.

У кого-нибудь есть идеи, как с этим бороться?

1
user53646 17 Сен 2014 в 04:08
Может работать что-то вроде df$NewCol <- diag(as.matrix(df[match(df$Cn, names(df))])), где df — данные
 – 
Rich Scriven
17 Сен 2014 в 04:12
Спасибо, я тоже попробую.
 – 
user53646
17 Сен 2014 в 04:28

2 ответа

Лучший ответ

Используйте матричную индексацию нужных значений строки и столбца для извлечения. Я использовал dat в качестве имени вашего data.frame.

dat[-1][cbind(seq_along(dat$Cn),match(as.character(dat$Cn),names(dat[-1])))]
#[1] 5 6

Как в:

sel <- cbind(seq_along(dat$Cn),match(as.character(dat$Cn),names(dat[-1])))
sel    

#      row  col
#     [,1] [,2]
#[1,]    1    3
#[2,]    2    2

dat[-1][sel]
#[1] 5 6

Время для 7M строк и вашего примера с 4 столбцами составляет около 0,4 секунды.

dat2 <- dat[sample(1:2,7e6,replace=TRUE),]
nrow(dat2)
#[1] 7000000
system.time({
  sel <- cbind(seq_along(dat2$Cn),match(as.character(dat2$Cn),names(dat2[-1])))
  dat2$newcol <- dat2[-1][sel]
})
#   user  system elapsed 
#   0.33    0.07    0.39 
2
thelatemail 17 Сен 2014 в 05:30
Качественная работа! Моя единственная защита с использованием set будет заключаться в эффективности использования памяти (нет необходимости создавать 7-миллионную матрицу строк sel)
 – 
mnel
17 Сен 2014 в 05:31
Поскольку кажется, что для data.table нет матричной индексации, вы всегда можете разрезать это решение на управляемые блоки из нескольких сотен тысяч строк в цикле. Запуск по-прежнему займет всего несколько секунд, и это позволит избежать необходимости назначать огромную матрицу.
 – 
thelatemail
17 Сен 2014 в 05:49
Это большое спасибо! Я использовал первый метод, и это заняло несколько минут, так что меня это устраивает.
 – 
user53646
17 Сен 2014 в 22:50

Используйте mapply, чтобы применить [.data.frame при перемещении по каждой строке и d $ Cn.

 table$NewCol <- mapply(i = seq_along(d[['Cn']]),
         j= d[['Cn']],
     FUN = function(i,j,x) x[i,j,drop=TRUE],
     MoreArgs=list(x=d))

Если скорость и эффективность вызывают беспокойство, используйте data.table и set (этот цикл является эффективным)

library(data.table)
setDT(d)

for(i in seq_len(nrow(d))){
  set(d,j='newCol', i=i, value= d[[d[['Cn']][i]]][i])
}
3
mnel 17 Сен 2014 в 04:33
Я зацикливаюсь и надеюсь, что он будет работать в одночасье, если что-то пойдет не так, я обязательно попробую ваши решения. Спасибо!
 – 
user53646
17 Сен 2014 в 04:27
- Вам определенно не понадобится целая ночь, чтобы запустить решение data.table. Я предполагаю, что это будет меньше одной минуты.
 – 
Rich Scriven
17 Сен 2014 в 04:29
Использование data.table и set занимает около 1 минуты с 50 столбцами и 1 миллионом строк....
 – 
mnel
17 Сен 2014 в 04:34