将功能映射到数据集的选定元素上

时间:2018-08-16 18:25:39

标签: r function nested tidyr purrr

我一直在尝试将bctau函数(及其包含的theil)映射到数据集的选定元素上。此功能使用两步过程来计算AB单箱设计的效果大小。它需要两个参数(a =基线阶段的所有值; b =干预阶段的所有值)。在下面,您可以找到Tarlow (2017)开发的两个功能。

library(Kendall)
library(dplyr)
library(purrr)    
library(tidyr)

bctau <- function(a,b) {

# The bctau() function accepts two arguments, a and b, which
# are vectors for each phase in an AB single-case design


    n <- length(a) + length(b)

    ta <- 1:(length(a))
    tb <- (length(a) + 1):(length(a) + length(b))

    # if baseline trend is not statistically significant, 
    # return tau result (no trend correction)

        if (Kendall(a,ta)$sl > .05) {
            results <- Kendall(c(a,b), c(rep(0,length(a)), rep(1,length(b))))
            tau <- as.numeric(results$tau)
            p <- as.numeric(results$sl)
            se <- sqrt((2/n) * (1 - (tau^2)))
            return(list(tau = tau, p = p, se = se, corrected = FALSE))
        }

    # if baseline trend is statistically significant,
    # get Theil-Sen residuals

        theilsen <- theil(ta, a)
        slope <- theilsen$slope
        intercept <- theilsen$int

        correcteda <- as.numeric()
        correctedb <- as.numeric()

        for (i in 1:length(a)) {
            correcteda[i] <- a[i] - (slope*i + intercept)
        }
        for (i in 1:length(b)) {
            correctedb[i] <- b[i] - (slope*(i + length(a)) + intercept)
        }

        results <- Kendall(c(correcteda,correctedb),c(rep(0,length(a)),rep(1,length(b))))
        tau <- as.numeric(results$tau)
        p <- as.numeric(results$sl)
        se <- sqrt((2/n) * (1 - (tau^2)))
        return(list(tau = tau, p = p, se = se, corrected = TRUE, int = intercept, slope = slope, correcteda = correcteda, correctedb = correctedb))

}

theil <- function(x,y) {

# returns theil-sen slope and intercept estimates;
# x and y are two equal length vectors (x & y coords)

    n <- length(x)
    slopes <- as.numeric()
    ints <- as.numeric()
    for (i in 1:(n - 1)) {
        for (j in (i + 1):n) {
            slopes <- c(slopes, ((y[j] - y[i]) / (x[j] - x[i])))
        }
    }
    b <- median(slopes)
    for (i in 1:n) {
        ints <- c(ints, (y[i] - (b*x[i])))
    }
    results <- list(slope = b, int = median(ints))
    return(results)
}

我的数据集由五列组成: 1. Scalex:为参与者评分的行为量表; 2. IDx:参与者ID(请注意,每个参与者都完成了两个量表); 3. Timex:会话号(每次阶段更改时重新启动); 4.阶段:基线(A)或干预阶段(B); 5. Ratex:评分量表分数(从1到20)。

Scalex <- c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2)
IDx <- c("C1", "C1", "C1", "C1", "C1", "C1", "C1", "C1", "C1", "C1", "C1", "C2", "C2", "C2", "C2", "C2", "C2", "C2", "C2", "C1", "C1", "C1", "C1", "C1", "C1", "C1", "C1", "C2", "C2", "C2", "C2", "C2", "C2", "C2", "C2", "C2")
Timex <- c(1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5)
Phasex <- c("A", "A", "A", "A", "A", "B", "B", "B", "B", "B", "B", "A", "A", "A", "A", "B", "B", "B", "B", "A", "A", "A", "A", "B", "B", "B", "B", "A", "A", "A", "A", "B", "B", "B", "B", "B")
Ratex <- c(4, 8, 10, 12, 15, 7, 7, 9, 14, 15, 16, 4, 3, 2, 2, 7, 7, 9, 14, 2, 3, 6, 6, 7, 5, 9, 11, 5, 6, 3, 4, 8, 7, 9, 3, 3)


db <- data.frame(Scalex, IDx, Timex, Phasex, Ratex)

我想做的是按比例将数据分组,然后将bctau()函数应用于每个参与者。我试图将参与者嵌套到秤中,这就是结果。

d <- db %>% 
  group_by(Scalex) %>% 
  nest()

# A tibble: 2 x 2
#  Scalex data             
#   <dbl> <list>           
#1      1 <tibble [19 x 4]>
#2      2 <tibble [17 x 4]>

这是其中一个嵌套的小对象的样子。 Phasex指示评估是在基准阶段(A)还是干预阶段(B)进行的,Ratex指示学生的行为评分。

d$data[[1]]
#[[1]]
#    # A tibble: 19 x 4
#   IDx   Timex Phasex Ratex
#   <fct> <dbl> <fct>  <dbl>
# 1 C1        1 A          4
# 2 C1        2 A          8
# 3 C1        3 A         10
# 4 C1        4 A         12
# 5 C1        5 A         15
# 6 C1        1 B          7
# 7 C1        2 B          7
# 8 C1        3 B          9
# 9 C1        4 B         14
#10 C1        5 B         15
#11 C1        6 B         16
#12 C2        1 A          4
#13 C2        2 A          3
#14 C2        3 A          2
#15 C2        4 A          2
#16 C2        1 B          7
#17 C2        2 B          7
#18 C2        3 B          9
#19 C2        4 B         14

我试图编写这段代码。我使用了map2_df,因为我正在使用两个变量。我的代码每次都占用两列,并将第一列用作基线,第二列用作干预。然后计算bctau值和与其相关的其他统计参数(标准误差,p值等)

f <- db %>% 
  filter(Scalex == 1) %>%
  unite(ID2x, IDx, Phasex) %>%
  spread(ID2x, Ratex) %>% 
  dplyr::select(-Scalex, -Timex) %>% 
  data.frame()

#> f
#  C1_A C1_B C2_A C2_B
#1    4    7    4    7
#2    8    7    3    7
#3   10    9    2    9
#4   12   14    2   14
#5   15   15   NA   NA
#6   NA   16   NA   NA

f1 <- f %>% select(C2_A, C2_B)
> g <- map2_df(.x = f1[seq(1, ncol(f1), 2)], .y = f1[seq(2, ncol(f1), 2)], ~ bctau(.x, .y))
> g
# A tibble: 1 x 4
#    tau      p    se corrected
#  <dbl>  <dbl> <dbl> <lgl>    
#1 0.784 0.0284 0.253 FALSE 

仅当函数bctau不需要调用函数theil时,代码才似乎起作用(这意味着基线趋势不需要使用非参数Theil-Sen估计器对A和B阶段进行校正)。调用theil函数时,还将调用软件包Kendall,并且NA值似乎会产生一些问题。但是,我无法摆脱它们,因为基线和干预阶段的长度并不总是相同。

g <- map2_df(.x = f[seq(1, ncol(f), 2)], .y = f[seq(2, ncol(f), 2)], ~ bctau(.x, .y)) 

#WARNING: Error exit, tauk2. IFAULT =  10
#Error in bind_rows_(x, .id) : Argument 7 must be length 1, not 6

即使很好,我也不必使用purrr。

更新

我能够摆脱部分问题。如果我删除了bctau函数中**之间的代码部分(我实际上并不需要),则第二条错误行将不再显示。

return(list(tau = tau, p = p, se = se, corrected = TRUE)) 
code removed from the bctau function: **int = intercept, slope = slope, correcteda = correcteda, correctedb = correctedb** 

很遗憾,#WARNING: Error exit, tauk2. IFAULT = 10仍在那儿,它不允许在结果中报告校正后的估计值。

g <- map2_df(.x = f[seq(1, ncol(f), 2)], .y = f[seq(2, ncol(f), 2)], ~ bctau(.x, .y))

#WARNING: Error exit, tauk2. IFAULT =  10
g
# A tibble: 2 x 4
#    tau      p    se corrected
#  <dbl>  <dbl> <dbl> <lgl>    
#1 1     1      0     TRUE     
#2 0.784 0.0284 0.253 FALSE   

更新2

当我从列中手动删除NA值时,出现解决方案。因此,我的猜测是,当需要进行基线校正时,如果涉及NA值,则theil函数将无法计算新的估算值。有没有办法告诉函数不要考虑NA值?

f2$C1_A
#[1]  4  8 10 12 15 NA

f2$C1_B
#[1]  7  7  9 14 15 16

#bl <- c(4, 8, 10, 12, 15, NA)
#i <- c(7, 7, 9, 14, 15, 16)
#bctau(bl, i)
#WARNING: Error exit, tauk2. IFAULT =  10

bl <- c(4, 8, 10, 12, 15) #remove NA manually
bl
#[1]  4  8 10 12 15
i
#[1]  7  7  9 14 15 16

bctau(bl, i) #calculate bctau
#$`tau`
#[1] -0.7385489
#$p
#[1] 0.008113123
#$se
#[1] 0.2874798
#$corrected
#[1] TRUE

1 个答案:

答案 0 :(得分:1)

我认为您需要按Scalex和IDx嵌套data.frame,然后在嵌套的data.frame上使用匿名函数。我认为这段代码可以满足您的需求:

db %>% 
  spread(Phasex, Ratex) %>% 
  group_by(Scalex, IDx) %>% 
  nest() %>% 
  mutate(m = map(data, function(d) bctau(a = d$A, b = d$B))) %>% 
  unnest(m)