получить значения из фрейма данных в другой

Я новичок в программировании на R и только начал его изучать, и мне нужна ваша помощь в этом вопросе, пожалуйста.

У меня есть 2 фрейма данных:

the first(df1):

    V1 V2
    A  A 
    A  B 
    A  C 
    B  A 
    B  B 
    B  C 

etc

второй (df2):

V1  Va   Vb
A   12   23
B   15   53
C   321  543
D   54   325
etc..

Используйте этот код для создания примера данных.

df1 <- data.frame(
  V1 = rep(LETTERS[1:2], each = 3L),
  V2 = rep.int(LETTERS[1:3], 2L)
)
dfr2 <- data.frame(
  Va = c(12, 15, 312, 54),
  Vb = c(23, 53, 543, 325)
)

Мне нужно взять Va и Vb из df2 и поместить их в df1 на основе V1 и V2 из df1.

поэтому я хочу этот вывод:

df3:

V1   V2  Va1  Vb1 Va2 Vb2
A    A   12    23  12  23
A    B   12    23  15  23
A    C   12    23  321 543
B    A   15    23  12   23
B    B   15    23  15   23
B    C   15    23  321  543

надеюсь, что это можно сделать в R без дюжины циклов for :S.


person smack    schedule 23.05.2011    source источник


Ответы (3)


Вы можете использовать для этого силу match(), при условии, что имена в df1$V1 уникальны:

#some data
df1 <- data.frame(
  V1 = rep(c("A","B"),each=3),
  V2 = rep(LETTERS[1:3],2)  
)
df2 <- data.frame(V1=LETTERS[1:3],Va=1:3,Vb=3:1)

out <- cbind(df1,
  df2[match(df1$V1,df2$V1),-1],
  df2[match(df1$V2,df2$V1),-1]
)
names(out)[3:6] <- c("Va1","Vb1","Va2","Vb2")
rownames(out) <- 1:nrow(out)

Дает

> out
    V1 V2 Va1 Vb1 Va2 Vb2
1    A  A   1   3   1   3
2    A  B   1   3   2   2
3    A  C   1   3   3   1
4    B  A   2   2   1   3
5    B  B   2   2   2   2
6    B  C   2   2   3   1

Вам придется переименовать столбцы вручную, так как вы получите несколько столбцов с одинаковыми именами. Хотя это технически возможно в фрейме данных, позже это может вызвать проблемы. Вы можете автоматизировать это, используя что-то вроде:

names(out) <- 
    c("V1","V2",
      sapply(names(df2)[2:3],paste,1:2,sep="")
    )

РЕДАКТИРОВАТЬ: для больших фреймов данных преобразование в матрицы имеет еще одно огромное значение. Следует обратить внимание на внутренние изменения типа для различных переменных. Ускорение связано с тем, что cbind и merge требуют много времени для определения правильных типов для каждой переменной.

Со следующими данными и функциями:

n <- 1e5
df1 <- data.frame(V1 = rep(LETTERS,each=n),V2 = rep(LETTERS,n),
        stringsAsFactors=FALSE)
df2 <- data.frame(V1=LETTERS,Va=1:26,Vb=26:1,stringsAsFactors=FALSE)

fast_JM <- function(df1,df2){
  out <- cbind(
    as.matrix(df2[,-1])[match(df1$V1,df2$V1),],
    as.matrix(df2[,-1])[match(df1$V2,df2$V1),]
  )
  out <- as.data.frame(out)
  names(out) <- sapply(names(df2)[2:3],paste,1:2,sep="")
  out$V1 <- df1$V1
  out$V2 <- df1$V2
  out
}

slow_JM <- function(df1,df2){
  out <- cbind(df1,
    df2[match(df1$V1,df2$V1),-1],
    df2[match(df1$V2,df2$V1),-1]
  )
  names(out)[3:6] <- c("Va1","Vb1","Va2","Vb2")
  out
}


double_merge <- function(df1,df2){
  merge(merge(df1, df2), df2, by.x = "V2", by.y = "V1", suffixes = c("1", "2"))

}

бенчмаркинг становится:

require(rbenchmark)
benchmark(fast_JM(df1,df2),slow_JM(df1,df2),double_merge(df1,df2),
      replications=1,columns=c("test","elapsed","relative"),order="relative")

                    test elapsed relative
1      fast_JM(df1, df2)    0.89  1.00000
2      slow_JM(df1, df2)   12.54 14.08989
3 double_merge(df1, df2)   42.50 47.75281

Таким образом, ускорение более чем в 40 раз по сравнению с двойным слиянием и более чем в 10 раз по сравнению с использованием фреймов данных.

person Joris Meys    schedule 23.05.2011
comment
@Joris Meys: спасибо, я попробую, но у меня есть вопрос, почему вы использовали -1 в конце здесь: df2[match(df1$V2,df2$V1),-1] - person smack; 23.05.2011
comment
@smack : удалить первый столбец, то есть df2$V1. Вам это не нужно в вашем фрейме данных. Если у вас небольшой фрейм данных, метод @Chase может быть проще для понимания. Однако на больших кадрах данных это займет примерно в 4 раза больше времени. - person Joris Meys; 23.05.2011
comment
@Joris Meys: в новом df я получил новый столбец, похожий на id или что-то в этом роде, но значения устарели (449.105, 2161.358, ....) это нормально ??? и как их убрать?? - person smack; 23.05.2011
comment
@smack: это не столбец, это имена строк. Вы можете избавиться от них, используя rownames(out) <- 1:nrow(out) - person Joris Meys; 23.05.2011
comment
Можно ли это адаптировать для неуникальных записей? У меня есть df1$a с набором значений и df2[, c(a, b)] в качестве своего рода ключа -- df2$a содержит каждый уникальный df1$a и соответствующий df2$b. Я хочу заменить каждый df1$a на его совпадение с df2$b. Опять же, некоторые df1$a повторяются - может ли match() работать для этого (думая, что нет)? - person Hendy; 11.06.2014
comment
@Hendy match() должен работать, если df2$a содержит только уникальные значения. match(df1$a, df2$a) даст вам индексы строк df2, которые вы можете использовать для cbind с df1. - person Joris Meys; 13.06.2014

Вы можете использовать merge() дважды, чтобы получить то, что вы хотите. По умолчанию merge ищет общие имена столбцов для объединения. Во втором слиянии мы укажем столбец, с которым мы хотим объединиться:

df1 <- data.frame(V1 = c('A', 'A', 'A', 'B', 'B', 'B'), V2 = c('A', 'B', 'C', 'A', 'B', 'C'))
df2 <- data.frame(V1 = c('A', 'B', 'C', 'D'), Va = c(12, 15, 321, 54), Vb = c(23, 53, 543, 325))

merge(merge(df1, df2), df2, by.x = "V2", by.y = "V1", suffixes = c("1", "2"))
person Chase    schedule 23.05.2011
comment
на больших кадрах данных двойной вызов слияния станет очень дорогостоящим. +1 за суффиксы, хотя - person Joris Meys; 23.05.2011
comment
@Джорис - хорошая мысль. Я часто использую по умолчанию merge(), так как могу мысленно провести параллели между merge() и операторами соединения в SQL, с которыми у меня гораздо больше опыта, чем с R. - person Chase; 23.05.2011
comment
Но слияние допускает неуникальные записи в df1, что может быть полезно. Решение match(), похоже, этого не делает... поэтому +1 за merge() как универсальное, даже если оно дорогое. - person Hendy; 11.06.2014

Я думаю, что в пакете dplyr Хэдли Уикхэма есть несколько отличных инструментов для объединения кадров данных. Это другой способ добраться до того же места.

df1 <- data.frame(
  V1 = rep(LETTERS[1:2], each = 3L),
  V2 = rep.int(LETTERS[1:3], 2L)
)
dfr2 <- data.frame(
  V1 = LETTERS[1:4],
  Va = c(12, 15, 312, 54),
  Vb = c(23, 53, 543, 325)
)

##necessary libraries
library(magrittr, dplyr)

Теперь, используя функции left_join() и rename() из пакета dplyr и оператор канала из пакета magrittr, я думаю, вы сможете сэкономить много нажатий клавиш.

df3 <- df1 %>% #pipe operator (%>%) is from magrittr package
  dplyr::left_join(dfr2, c('V1'="V1")) %>% #merge once
  dplyr::rename(Va1 = Va, Vb1 = Vb) %>%  #rename columns
  dplyr::left_join(dfr2, c('V2'="V1")) %>% #merge on different column
  dplyr::rename(Va2 = Va, Vb2 = Vb) #rename again

Это также можно сделать с помощью одного слияния вместо двух, но мне нравится видеть, что происходит на каждом этапе.

> print(df3)
  V1 V2 Va1 Vb1 Va2 Vb2
1  A  A  12  23  12  23
2  A  B  12  23  15  53
3  A  C  12  23 312 543
4  B  A  15  53  12  23
5  B  B  15  53  15  53
6  B  C  15  53 312 543
person BarkleyBG    schedule 29.04.2016