r语言 - 在突变和附加结果中为 for 循环



我有一个简单的for-loop,它可以在向量上工作,我想在dataframe中按另一列分组的dataframe列上使用我的for-loop,例如:

# 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逻辑,试图在mutate内部有一个for-loop,任何建议都非常感谢。

简单的解决方案是创建自定义函数并将其传递给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是否可以提供替代方案。

我们可以使用purrr中的map_dbl并应用公式进行计算。

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 

您可以将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 

这基本上等同于mutate调用中看起来非常错误的for循环。但是,在这种情况下,我更喜欢 A. Stam 提供的自定义功能。

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替换为purrrmap_dbl

或者使用 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

最新更新