【问题标题】:Pairwise combine the rows of a grouped R dataframe and apply functions to each pair成对组合分组的 R 数据帧的行并将函数应用于每一对
【发布时间】:2020-06-12 07:58:38
【问题描述】:

我使用一组自动、时间同步和固定的探测器麦克风对蝙蝠的超声波呼叫活动进行了采样。在生成的数据帧中,每一行都是一个录制的通话,以及录制时间、通话的长度(活动的代理)以及设备的 x 和 y 坐标。

我想将探测器距离与 delta(每两个探测器之间的呼叫长度差异)关联起来,以评估空间距离对探测器性能的影响。为此,我需要将每个数据帧行与所有其他数据帧行成对组合,并且每个时间戳(= 组),最后应用两个函数计算(i)增量和(ii)每对检测器之间的空间距离。这是一个示例数据帧,由三个设备和三个时间戳组成。

bats <- data.frame(time=as.POSIXct(rep(c("2019-05-18 00:00:00","2019-05-18 00:05:00","2019-05-18 00:10:00"),each=3)), device=rep(c("a","b","c"),3), pos.X=rep(c(0,2,4),3), pos.Y=rep(c(5,7,9),3), call.length=rlnorm(9,meanlog=0.5,sd=1))

对于一组,我成功地使用了 combn() 和 raster::pointDistance():

delta <- abs(combn(x=bats$call.length, m=2, FUN=function(x) x[1]-x[2])) # calculate delta

coordinates <- bats[ ,3:4] # make dataframe of coordinates

row.pairs <- combn(x=nrow(bats), m=2) # get indices for row pairs

df_row.pairs <- cbind(x=coordinates[row.pairs[1,],], y=coordinates[row.pairs[2,],]) # make dataframe from the row pairs
distance <- raster::pointDistance(p1=df_row.pairs[2:1], df_row.pairs[4:3], lonlat=FALSE) # calculate distances among coordinates
df <- data.frame(delta,distance=distance) # bind distances and deltas to a dataframe

但是尝试使用 dplyr::group_by(time) %>% group_map() 扩展所有时间戳的代码,我惨遭失败。

特别欢迎 Tidyverse 解决方案,但我非常感谢每一个建议。

【问题讨论】:

    标签: r dplyr tidyverse


    【解决方案1】:

    如果我正确理解了您的问题,您可以将数据框与其自身(每个时间戳)连接起来,然后计算增量和距离:

    library(dplyr)
    bats %>%
      left_join(bats, by = "time", suffix = c("", "_2")) %>%
      mutate(distance = sqrt((pos.X - pos.X_2) ^ 2 + (pos.Y - pos.Y_2) ^ 2),
             delta = abs(call.length - call.length_2)) 
    

    结果:

    # A tibble: 27 x 11
       time                device pos.X pos.Y call.length device_2 pos.X_2 pos.Y_2 call.length_2 distance delta
       <dttm>              <fct>  <dbl> <dbl>       <dbl> <fct>      <dbl>   <dbl>         <dbl>    <dbl> <dbl>
     1 2019-05-18 00:00:00 a          0     5       2.17  a              0       5         2.17      0     0   
     2 2019-05-18 00:00:00 a          0     5       2.17  b              2       7         0.361     2.83  1.81
     3 2019-05-18 00:00:00 a          0     5       2.17  c              4       9         3.49      5.66  1.32
     4 2019-05-18 00:00:00 b          2     7       0.361 a              0       5         2.17      2.83  1.81
     5 2019-05-18 00:00:00 b          2     7       0.361 b              2       7         0.361     0     0   
     6 2019-05-18 00:00:00 b          2     7       0.361 c              4       9         3.49      2.83  3.13
     7 2019-05-18 00:00:00 c          4     9       3.49  a              0       5         2.17      5.66  1.32
     8 2019-05-18 00:00:00 c          4     9       3.49  b              2       7         0.361     2.83  3.13
     9 2019-05-18 00:00:00 c          4     9       3.49  c              4       9         3.49      0     0   
    10 2019-05-18 00:05:00 a          0     5       3.79  a              0       5         3.79      0     0   
    # ... with 17 more rows
    

    【讨论】:

    • 谢谢@Aron,简洁优雅!我非常专注于 group_by() 我没有考虑到这一点。伟大的。但仍然存在两个问题。 (1) 设备与它们自身进行比较,对于增量和距离都必然产生零。轻松解决,包括管道中的 %>% filter(device != device_2) 。 (2) 等效组合每组出现两次,即 a/b 和 b/a、a/c 和 c/a 等。我可以在最终数据框中添加另一列,对坐标值求和并使用unique() 排除双打。可能,但有点尴尬 - 有什么更优雅的解决方案的想法吗?
    • @AleBru 很高兴它成功了!将您的过滤器设置为 filter(as.character(device)
    • 再次感谢@Aron,非常感谢!与此同时,我找到了解决这两个问题的另一种方法。利用时间组中的常规行序列,我通过将 %>% group_by(time) %>% slice(c(2,3,6)) 添加到管道来选择行。但是您的解决方案更通用,因此是首选。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多