Использование mapply для косвенной адресации во фрейме данных

Со следующими двумя кадрами данных

> d1
  keystr keynum 
1    abc      5
2    def      2
3    def      7 
4    abc      3 

> d2
   HD  2  3  5  7
1 abc  H  I  J  K
2 def  L  M  N  P

Я хотел бы вставить столбец d1$val, который использует строку в keystr и число в keynum в качестве индексов во фрейме данных d2. Результат должен быть:

> d1
  keystr keynum  val
1    abc      5    J
2    def      2    L
3    def      7    P 
4    abc      3    I 

Это должно быть косвенное применение mapply. Как я могу сделать код ниже

d1 <- data.frame("keystr"=c("abc","def","def","abc"), "keynum"=c(5,2,7,3))
d2 <- data.frame("HD"=c("abc","def"),
                 "2"=c("H","L"), "3"=c("I","M"),
                 "5"=c("J","N"), "7"=c("K","P"))
d1$val <- mapply(function(kstr,knum) d2[kstr,knum],
                 d1$keystr, d1$keynum )

получить доступ к записям таким (косвенным) способом?


person Vrokipal    schedule 05.04.2018    source источник
comment
Вы очень близки к использованию mapply для получения решения. Просто подкорректируйте выбор строки и используйте paste0 для выбора столбца, чтобы найти решение. Посмотрите на мой ответ.   -  person MKR    schedule 06.04.2018


Ответы (6)


Вы можете изменить форму и присоединиться к data.frames, используя базу R:

d1 <- read.table(text = 'keystr keynum 
1    abc      5
2    def      2
3    def      7 
4    abc      3', stringsAsFactors = FALSE)

d2 <- read.table(text = 'HD  2  3  5  7
1 abc  H  I  J  K
2 def  L  M  N  P', stringsAsFactors = FALSE, check.names = FALSE)

d2 <- reshape(d2, idvar = "HD", varying = names(d2)[-1], v.names = "val",
              times = names(d2)[-1], direction = "long")
merge(d1, d2, by.x = c("keystr", "keynum"), by.y = c("HD", "time"))
#>   keystr keynum val
#> 1    abc      3   I
#> 2    abc      5   J
#> 3    def      2   L
#> 4    def      7   P
person Ralf Stubner    schedule 05.04.2018

Мы можем преобразовать фрейм данных, а затем провести слияние с помощью tidyr и dplyr.

library(dplyr)
library(tidyr)

d3 <- d2 %>%
  gather(keynum, letter, -HD) %>%
  mutate(keynum = as.numeric(sub("X", "", keynum)))

d4 <- d1 %>%
  left_join(d3, by = c("keystr" = "HD", "keynum"))
d4
#   keystr keynum letter
# 1    abc      5      J
# 2    def      2      L
# 3    def      7      P
# 4    abc      3      I

ДАННЫЕ

Обратите внимание, что я установил stringsAsFactors = FALSE при создании фреймов данных.

d1 <- data.frame("keystr"=c("abc","def","def","abc"), "keynum"=c(5,2,7,3),
                 stringsAsFactors = FALSE)
d2 <- data.frame("HD"=c("abc","def"),
                 "2"=c("H","L"), "3"=c("I","M"),
                 "5"=c("J","N"), "7"=c("K","P"),
                 stringsAsFactors = FALSE)
person www    schedule 05.04.2018

Если вы не обязаны использовать mapply, вы можете выполнить соединение:

Код:

library(tidyverse)
d1 <- data.frame("keystr"=c("abc","def","def","abc"), "keynum"=c(5,2,7,3))
d2 <- data.frame("HD"=c("abc","def"),
                 "2"=c("H","L"), "3"=c("I","M"),
                 "5"=c("J","N"), "7"=c("K","P"))

d2 %>%
  gather(keynum, value, -HD) %>%
  mutate(keynum = as.numeric(gsub(keynum, pattern = "X", replacement = ""))) %>%
  left_join(y = ., x = d1, by = c("keystr" = "HD", "keynum"))

Вывод:

  keystr keynum value
1    abc      5     J
2    def      2     L
3    def      7     P
4    abc      3     I
person Martin Schmelzer    schedule 05.04.2018

Я думаю, что OP правильно подумал, что mapply может предоставить ему прямое решение. Он довольно близок к рабочему решению со своим mapply подходом. Просто логика сравнения для выбора строки должна быть исправлена, а затем paste0 будет использоваться для выбора столбца из d2.

d1$val <- mapply(function(x,y)d2[d2$HD==x,paste0("X",y)],d1$keystr, d1$keynum)
d1
#    keystr keynum val
# 1    abc      5   J
# 2    def      2   L
# 3    def      7   P
# 4    abc      3   I
# 
person MKR    schedule 05.04.2018
comment
Если бы я использовал check.names=FALSE, вам бы даже не понадобился paste0; это правильно? - person Vrokipal; 06.04.2018
comment
Я думаю, что это не сработает. Поскольку значение y является числовым, оно будет рассматриваться как индекс столбца. Например, 7 станет недопустимым столбцом. Но вы можете заменить paste0 на as.character(y) в этом случае, и это должно сработать. - person MKR; 06.04.2018

Вы можете использовать столбцы d1 для индексации значений символов в d2[-1], если вы конвертируете в матрицу и cbind значения символов столбца. Он создает двумерную таблицу поиска, в которую вы передаете индексы как для строки, так и для столбца одновременно. Затем вы также можете передать ему двумерную матрицу, чтобы сгенерировать вектор выходных данных. (Можно также использовать индексирование 3 или 4 или выше D с массивами R, в которые on=e будет передавать матрицы столбцов с номерами 3,4 или выше):

( m2 <- sapply(d2[ , -1], as.character) )
#------
     2   3   5   7  
[1,] "H" "I" "J" "K"
[2,] "L" "M" "N" "P"

rownames(m2) <- as.character(d2[[1]])
m2
#--------
    2   3   5   7  
abc "H" "I" "J" "K"
def "L" "M" "N" "P"


(d1$val <-  m2[ cbind(as.character(d1[[1]]),as.character(d1[[2]])) ])
[1] "J" "L" "P" "I"

 d1
#--------
  keystr keynum val
1    abc      5   J
2    def      2   L
3    def      7   P
4    abc      3   I

Обратите внимание на необходимость многократного использования as.character, потому что это были столбцы фактора. Лучшей конструкцией было бы построить ваши data.frames с помощью stringsAsFactors=FALSE. Построение матрицы будет быстрым, а индексация, вероятно, будет очень эффективной.

person IRTFM    schedule 05.04.2018
comment
Я должен сказать, что это самый умный ответ. Как ты можешь думать об этом долго? Удивительно. - person MKR; 06.04.2018
comment
Это тот ответ, который вы получаете, пытаясь учиться у Г. Гротендейка или Мартина Мехлера. - person IRTFM; 06.04.2018
comment
Мне нравится ясность и очевидная эффективность фактического однострочного решения (но, как и вам, нет необходимости в as-character). Но нет ли способа индексировать имена столбцов d1, а не индексы 1, 2? Предоставляет ли R гарантию эффективности конструкции, которую вы использовали? - person Vrokipal; 06.04.2018
comment
Использование d1[['keynum']] или d1$keynum не приведет к потере эффективности. И я согласен, что это было бы более читабельно. Первая версия более адаптирована к ситуациям программирования. Нет никакой гарантии, но в целом векторизованные стратегии (а та, которую я предложил, является векторизованной) будут более эффективными, чем циклы, и все sapply, lapply и mapply в основном являются циклами for под капотом. Если вы вводите все данные с помощью stringsAsFactors =FALSE, вы избавите себя от многих as.character головных болей. Его можно установить по умолчанию. См. ?options. - person IRTFM; 06.04.2018

Добавлен check.names = False, чтобы включить имена столбцов data.frame, начинающиеся с цифр. Индекс с матрицей cbind() из двух столбцов, пары i, j будут извлечены все сразу.

d1 <- data.frame("keystr"=c("abc","def","def","abc"), "keynum"=c(5,2,7,3))
d2 <- data.frame("HD"=c("abc","def"),
                 "2"=c("H","L"), "3"=c("I","M"),
                 "5"=c("J","N"), "7"=c("K","P"), check.names=FALSE)

d1$val <- mapply(function(kstr,knum) d2[cbind(match(kstr, d1$keystr),
                                              match(knum, names(d2)))],
                 d1$keystr,
                 d1$keynum)

  keystr keynum val
1    abc      5   J
2    def      2   L
3    def      7   P
4    abc      3   I
person Vlo    schedule 05.04.2018
comment
Я все еще смотрю на ваше решение. Я получаю часть match(kstr, d1$keystr), но не часть match(knum, names(d2), и, что особенно важно, сопоставление knum с d2, а не с d1, даже работает. Что происходит? - person Vrokipal; 06.04.2018
comment
Когда вы используете [ для index. Если вы подаете ему целое число, он выводит этот индекс. Если вы подаете ему два вектора i и j, он извлекает i-е строки и j-е столбцы, указанные в i и j. Однако, если вы подаете ему матрицу с двумя столбцами, он извлекает значение для каждой пары i, j (строк матрицы cbind). Первое совпадение указывает i, второе совпадение указывает j. Вместе мы вытягиваем i, j пар из d2. - person Vlo; 06.04.2018