deparse (replace ()) на lapply - PullRequest
       64

deparse (replace ()) на lapply

0 голосов
/ 29 мая 2020

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

У меня есть следующий код:

df1 <- data.frame(c(1,2,3), c(3,4,5))
df2 <- data.frame(c(6,7,8), c(9,10,11))

f.generate.name <- function(x) {
  x$name <- deparse(substitute(x))
  return(x)
  }

my_list <- list(df1, df2) 


# This works fine.
f.generate.name(df1)

# This does not work.
lapply(my_list, f.generate.name)

, который дает следующий результат

[[1]]
  c.1..2..3. c.3..4..5.   name
1          1          3 X[[i]]
2          2          4 X[[i]]
3          3          5 X[[i]]

[[2]]
  c.6..7..8. c.9..10..11.   name
1          6            9 X[[i]]
2          7           10 X[[i]]
3          8           11 X[[i]]

Вместо этого я хочу:

[[1]]
  c.1..2..3. c.3..4..5.   name
1          1          3 df1
2          2          4 df1
3          3          5 df1

[[2]]
  c.6..7..8. c.9..10..11.   name
1          6            9 df2
2          7           10 df2
3          8           11 df2

Как лучше всего сделать это без использования циклов? Как я могу настроить функцию lapply () или созданную мной функцию, чтобы добиться желаемого результата?

Ответы [ 2 ]

1 голос
/ 29 мая 2020

Base R

lapply() не может перебирать более одного аргумента. В этом случае вы можете использовать mapply() или его оболочку Map(), которая всегда возвращает список.

Map(f = function(x, y){
    x$name <- y
    x }, 
  my_list, 
  names(my_list))
$df1
  c.1..2..3. c.3..4..5. name
1          1          3  df1
2          2          4  df1
3          3          5  df1

$df2
  c.6..7..8. c.9..10..11. name
1          6            9  df2
2          7           10  df2
3          8           11  df2

Tidyverse

Если вы открыты для решения purrr, вы можно использовать imap(). Это делает имена объектов доступными. Тогда нет необходимости писать функцию:

my_list <- list(df1 = df1, df2 = df2) 

imap(my_list, ~{
  .x$name <- .y
  .x
  })
$df1
  c.1..2..3. c.3..4..5. name
1          1          3  df1
2          2          4  df1
3          3          5  df1

$df2
  c.6..7..8. c.9..10..11. name
1          6            9  df2
2          7           10  df2
3          8           11  df2
0 голосов
/ 29 мая 2020

Действительно вопрос, откуда взялись имена? Безымянный список, например my_list в вопросе, потерял имена df1 и df2, как мы можем видеть, посмотрев на его внутреннюю структуру:

dput(my_list)  # no df1 or df2 seen
## list(structure(list(c.1..2..3. = c(1, 2, 3), c.3..4..5. = c(3, 
## 4, 5)), class = "data.frame", row.names = c(NA, -3L)), structure(list(
##     c.6..7..8. = c(6, 7, 8), c.9..10..11. = c(9, 10, 11)), class = 
## "data.frame", row.names = c(NA, 
## -3L)))

Таким образом, нам нужно создать именованный список в в первую очередь или укажите вектор имен. Мы показываем оба, используя только базовый R.

Именованный список

Сначала создайте именованный список фреймов данных, а затем используйте Map, как показано:

L <- mget(ls("^df"))  # create named list
Map(data.frame, L, name = names(L))

Безымянный список

В качестве альтернативы, если все, что у вас есть, это безымянный список, мы можем сопоставить его и вектор имен:

my_list <- list(df1, df2) # unnamed list as in question
Map(data.frame, my_list, name = c("df1", "df2"))

Передать отдельные фреймы данных

Еще один подход - передавать отдельные фреймы данных вместо списка. Поскольку мы не уничтожили исходные имена, создав безымянный список, мы все еще можем их получить. В R 4.0 и более поздних версиях deparse1 можно дополнительно использовать вместо deparse в коде.

add_names <- function(...) {
  mc <- match.call()
  Map(data.frame, list(...), names = sapply(mc[-1], deparse))
}

add_names(df1, df2)
...