代码之家  ›  专栏  ›  技术社区  ›  Andrew

加快data.frame操作而不是循环

  •  0
  • Andrew  · 技术社区  · 8 年前

    我有以下数据集 R

    dat <- data.frame(t = rep(seq(1, 5, 1),4), id = rep(c(rep("A",5), rep("B",5), rep("C",5), rep("D",5)), 1),
                      x = 1:20, y = 51:70, h = c(rep(1,10), rep(0,10) ) ) 
    require(dplyr)
    dat <- arrange(dat, t)
    

    数据集是一个面板,其中 t 作为时间变量和 id 作为主题ID,我需要附加一行,在这里我计算 x y 对于当时剩下的受试者 T 并除以 X 其他受试者的变量 T .这一新行的主题应该显示为零 h == 0 .

    例如,主题 A 当时 t == 1 ,操作是: (6 * 56 + 11 * 61 + 16 * 66) / sd(c(6, 11, 16)) .对受试者的类似操作 B 当时 t==1 (1 * 51 + 11 * 61 + 16 * 66) / sd(c(1, 11, 16)) .然而,对于受试者来说 C D ,新行将仅具有0。

    在没有循环的情况下,最快的方法是什么?我相信 dplyr 包裹是最快的,但我对它还很陌生,我不确定如何处理它。在我的尝试中,我首先按时间分组,然后收集变量,但是我收到一个警告,并且删除了几个变量。我不确定如何为每个组选择变量。

    dat %>%
      group_by(t) %>%
      gather(key, value, -t)
    # Warning message:
    # attributes are not identical across measure variables;
    # they will be dropped
    

    条件作用

    如何在上一个操作中包含这样的条件:在下表中,只有在 cond == id .例如,对于第一行,我们应该有:0,因为主题 B ,请 C D 所有人的价值观都不同于他们的 身份证件 ( cond A )。对于第6行,操作是 (2*52 + 12*62 + 17*67) / sd(c(2,12,17)) .

    dat <- data.frame(t = rep(seq(1, 5, 1),4), id = rep(c(rep("A",5), rep("B",5), rep("C",5), rep("D",5)), 1),
                      x = 1:20, y = 51:70, h = c(rep(1,10), rep(0,10) ) )
    dat <- arrange(dat, t)
    dat <- data.frame(dat, cond = c("B", "A", "A", "A", "A", "B", "C", "D", "A", "B", "D", "C", "A", "D", "C", "A", "A", "C", "C", "B") )
    dat
    
    #    t  id x y  h   cond
    # 1  1  A  1 51 1    B
    # 2  1  B  6 56 1    A
    # 3  1  C 11 61 0    A
    # 4  1  D 16 66 0    A
    # 5  2  A  2 52 1    A
    # 6  2  B  7 57 1    B
    # 7  2  C 12 62 0    C
    # 8  2  D 17 67 0    D
    # 9  3  A  3 53 1    A
    # 10 3  B  8 58 1    B
    # 11 3  C 13 63 0    D
    # 12 3  D 18 68 0    C
    # 13 4  A  4 54 1    A
    # 14 4  B  9 59 1    D
    # 15 4  C 14 64 0    C
    # 16 4  D 19 69 0    A
    # 17 5  A  5 55 1    A
    # 18 5  B 10 60 1    C
    # 19 5  C 15 65 0    C
    # 20 5  D 20 70 0    B
    

    建议的解决方案

    dat %>% 
     filter(id == cond) %>% 
     group_by(t) %>% 
     mutate(new = h * ((sum(x *y) - (x * y))/map_dbl(row_number(), ~ sd(x[-.x])))) %>% 
     bind_rows(dat %>% filter(id != cond))
    

    工作得很好,但部分原因是 NaN 从乘法 0 * Inf .相反,我想要 0 当条件不适用或分母处的标准偏差为 0 .非常感谢!

    1 回复  |  直到 8 年前
        1
  •  2
  •   akrun    8 年前

    按“t”分组后,通过使用 sum “x”和“y”的乘积与“x”和“y”的乘积(不包括当前行的乘积)之和除以 sd “x”的元素,通过循环遍历行索引( row_number() )用于排除当前行并乘以“h”,这样我们得到0,其中“h”为0。

    library(tidyverse)
    out <- dat %>% 
             group_by(t) %>% 
             mutate(new =  h * ((sum(x *y) - (x * y))/map_dbl(row_number(),
                                                         ~ sd(x[-.x]))))
    head(out, 4)
    # A tibble: 4 x 6
    # Groups:   t [1]
    #      t id        x     y     h   new
    #  <dbl> <fct> <int> <int> <dbl> <dbl>
    #1     1 A         1    51     1  413.
    #2     1 B         6    56     1  233.
    #3     1 C        11    61     0    0 
    #4     1 D        16    66     0    0 
    
    推荐文章