цикл for изменяет и добавляет результат - PullRequest
1 голос
/ 26 марта 2019

У меня есть простой for-loop, который работает, как я хотел бы для векторов, я хотел бы использовать мой for-loop для столбца dataframe, сгруппированного по другому столбцу в dataframe, например:

# here is my for-loop working as expected on a simple vector:

vect <- c(0.5, 0.7, 0.1) 
res <- vector(mode = "numeric", length = 3) 

for (i in 1:length(vect)) {
  res[i] <- sum(exp(-2 * (vect[i] - vect[-i])))
}

res
[1] 1.9411537 0.9715143 5.5456579

А вот псевдо-код, пытающийся сделать это для столбца кадра данных:

#Example data
my.df <- data.frame(let = rep(LETTERS[1:3], each = 3), 
    num1 = 1:3, vect = c(0.5, 0.7, 0.1), num3 = NA)

 my.df
   let num1 vect num3
1   A    1  0.5   NA
2   A    2  0.7   NA
3   A    3  0.1   NA
4   B    1  0.5   NA
5   B    2  0.7   NA
6   B    3  0.1   NA
7   C    1  0.5   NA
8   C    2  0.7   NA
9   C    3  0.1   NA

# My attempt:

require(tidyverse)

  my.df <- my.df %>%
      group_by(let) %>%
      mutate(for (i in 1:length(vect)) {
        num3[i] <- sum(exp(-4 * (vect[i] - vect[-i])))
  })

Как должен выглядеть результат (но мой код псевдо выше не работает):

   let num1 vect    num3
1   A    1  0.5 1.9411537
2   A    2  0.7 0.9715143
3   A    3  0.1 5.5456579
4   B    1  0.5 1.9411537
5   B    2  0.7 0.9715143
6   B    3  0.1 5.5456579
7   C    1  0.5 1.9411537
8   C    2  0.7 0.9715143
9   C    3  0.1 5.5456579

Мне кажется, что я не использую tidyverse логику, пытаясь иметь for-loop внутри mutate, любые предложения очень ценятся.

Ответы [ 4 ]

2 голосов
/ 26 марта 2019

Мы можем использовать map_dbl из purrr и применять формулу для расчета.

library(dplyr)
library(purrr)

my.df %>%
  group_by(let) %>%
  mutate(num3 = map_dbl(seq_along(vect), ~ sum(exp(-2 * (vect[.] - vect[-.])))))


#   let    num1  vect  num3
#  <fct> <int> <dbl> <dbl>
#1  A         1   0.5 1.94 
#2  A         2   0.7 0.972
#3  A         3   0.1 5.55 
#4  B         1   0.5 1.94 
#5  B         2   0.7 0.972
#6  B         3   0.1 5.55 
#7  C         1   0.5 1.94 
#8  C         2   0.7 0.972
#9  C         3   0.1 5.55 
2 голосов
/ 26 марта 2019

Простое решение - создать пользовательскую функцию и передать ее mutate.Рабочее решение:

custom_func <- function(vec) {
  res <- vector(mode = "numeric", length = 3)
  for (i in 1:length(vect)) {
    res[i] <- sum(exp(-2 * (vect[i] - vect[-i])))
  }
  res
}

library(tidyverse)

my.df %>%
  group_by(let) %>%
  mutate(num3 = custom_func(vect))

#> # A tibble: 9 x 4
#> # Groups:   let [3]
#>   let    num1  vect  num3
#>   <fct> <int> <dbl> <dbl>
#> 1 A         1   0.5 1.94 
#> 2 A         2   0.7 0.972
#> 3 A         3   0.1 5.55 
#> 4 B         1   0.5 1.94 
#> 5 B         2   0.7 0.972
#> 6 B         3   0.1 5.55 
#> 7 C         1   0.5 1.94 
#> 8 C         2   0.7 0.972
#> 9 C         3   0.1 5.55 

Мне интересно, возможна ли более элегантная версия пользовательской функции - возможно, кто-то умнее меня может сказать, может ли, например, purrr::map предоставить альтернативу.

1 голос
/ 26 марта 2019

Или используя data.table

library(data.table)
setDT(my.df)[, num3 := unlist(lapply(seq_len(.N), 
         function(i) sum(exp(-2 * (vect[i] - vect[-i]))))), let]
my.df
#   let num1 vect      num3
#1:   A    1  0.5 1.9411537
#2:   A    2  0.7 0.9715143
#3:   A    3  0.1 5.5456579
#4:   B    1  0.5 1.9411537
#5:   B    2  0.7 0.9715143
#6:   B    3  0.1 5.5456579
#7:   C    1  0.5 1.9411537
#8:   C    2  0.7 0.9715143
#9:   C    3  0.1 5.5456579
1 голос
/ 26 марта 2019

Вы можете превратить ваш for -петл в sapply -колл, а затем использовать его в mutate.sapply берет функцию и применяет ее к каждому элементу списка.В этом случае я зацикливаюсь на количестве элементов в каждой группе (n()).

my.df %>% 
  group_by(let) %>% 
  mutate(num3 = sapply(1:n(), function(i) sum(exp(-2 * (vect[i] - vect[-i])))))

# A tibble: 9 x 4
# Groups:   let [3]
#   let    num1  vect  num3
#   <fct> <int> <dbl> <dbl>
# 1 A         1   0.5 1.94 
# 2 A         2   0.7 0.972
# 3 A         3   0.1 5.55 
# 4 B         1   0.5 1.94 
# 5 B         2   0.7 0.972
# 6 B         3   0.1 5.55 
# 7 C         1   0.5 1.94 
# 8 C         2   0.7 0.972
# 9 C         3   0.1 5.55 

Это существенно эквивалентно очень неправильно выглядящему циклу for внутри вызова mutate,Однако в этом случае я бы предпочел пользовательскую функцию, предоставленную А. Стамом.

my.df %>%
  group_by(let) %>%
  mutate(num3 = {
    res <- numeric(length = n())
    for (i in 1:n()) {
      res[i] <- sum(exp(-2 * (vect[i] - vect[-i])))
    }
    res
  })

Вы также можете заменить sapply на purrr s map_dbl.

...