purrr将t.test映射到拆分df

时间:2016-02-22 16:27:23

标签: r purrr

我是purrr的新手,Hadley有前途的函数式编程R library。我正在尝试采用分组和拆分数据框并对变量运行t检验。使用样本数据集的示例可能如下所示。

mtcars %>% 
  dplyr::select(cyl, mpg) %>% 
  group_by(as.character(cyl)) %>% 
  split(.$cyl) %>% 
  map(~ t.test(.$`4`$mpg, .$`6`$mpg))

这会导致以下错误:

Error in var(x) : 'x' is NULL
In addition: Warning messages:
1: In is.na(x) : is.na() applied to non-(list or vector) of type 'NULL'
2: In mean.default(x) : argument is not numeric or logical: returning NA

我只是误解了map的工作原理吗?或者有更好的方式来考虑这个问题吗?

3 个答案:

答案 0 :(得分:10)

我不完全理解预期的结果,但这可能是一个答案的起点。来自map()的{​​{1}}在公式参数中使用purrr

这是完成我认为你只想.x尝试做的事情的一种方法。

purrr

但是,mtcars %>% split(as.character(.$cyl)) %>% map(~t.test(.x$mpg)) purrr::by_slice()很好地配对。

dplyr::group_by()

或者,您可以完全使用library(purrr) library(dplyr) mtcars %>% dplyr::select(cyl, mpg) %>% group_by(as.character(cyl)) %>% by_slice(~ t.test(.x$mpg)) 跳过purrr

dplyr:::summarise()

如果嵌套library(purrr) library(dplyr) mtcars %>% dplyr::select(cyl, mpg) %>% group_by(as.character(cyl)) %>% summarise(t_test = data_frame(t.test(.$mpg))) 令人困惑,data.frame可以帮助我们轻松获得结果的broom摘要。

data.frame + purrr + broom

tidyr

library(broom) library(tidyr) mtcars %>% group_by(as.character(cyl)) %>% by_slice(~tidy(t.test(.x$mpg))) %>% unnest() + dplyr

broom

已修改为包含对评论的回复

通过管道,我们可以很快得到带走。我认为沃尔特的答案很好,但我想确保我提供library(broom) mtcars %>% dplyr::select(cyl, mpg) %>% group_by(as.character(cyl)) %>% do(tidy(t.test(.$mpg))) - ty答案。我希望purrr的使用不会过于混乱。

pipeR

答案 1 :(得分:6)

特别是在处理需要多个输入的管道时(我们这里没有Haskell的箭头),我发现首先通过类型/签名更容易推理,然后将逻辑封装在函数中(你可以单元测试),然后写一个简洁的链。

在这种情况下,你想要比较所有可能的向量对,所以我会设定一个目标,即编写一个带有一对(即2个列表)向量的函数,并返回它们的双向t.test

一旦你完成了这个,你只需要一些胶水。所以计划是:

  1. 写入带有向量列表并执行双向t检验的函数。
  2. 编写一个从mtcars(easy)获取向量的函数/管道。
  3. 将上面的内容映射到成对列表上。
  4. 在编写任何代码之前制定此计划非常重要。由于R不是强类型的事实,事情在某种程度上被混淆了,但这种方式你推理"类型"首先,实施第二。

    第1步

    t.test需要点数,因此我们使用purrr:lift来获取列表。由于我们不想匹配列表元素的名称,因此我们使用.unnamed = TRUE。此外,我们更清楚地说明我们使用t.test函数,其中arity为2(尽管代码不需要执行此额外步骤)。

    t.test2 <- function(x, y) t.test(x, y)
    liftedTT <- lift(t.test2, .unnamed = TRUE)
    

    第2步

    将我们在第1步中获得的功能包装到一个简单配对的功能链中(这里我使用索引,应该很容易使用cyl factor级别,但我没有时间去弄清楚)

    doTT <- function(pair) {
      mtcars %>%
        split(as.character(.$cyl)) %>%
        map(~ select(., mpg)) %>% 
        extract(pair) %>% 
        liftedTT %>% 
        broom::tidy
    }
    

    第3步

    既然我们准备好了所有的乐高乐曲,那么乐曲很简单。

    1:length(unique(mtcars$cyl)) %>% 
      combn(2) %>% 
      as.data.frame %>% 
      as.list %>% 
      map(~ doTT(.))
    
    $V1
      estimate estimate1 estimate2 statistic      p.value parameter conf.low conf.high
    1 6.920779  26.66364  19.74286  4.719059 0.0004048495  12.95598 3.751376  10.09018
    
    $V2
      estimate estimate1 estimate2 statistic      p.value parameter conf.low conf.high
    1 11.56364  26.66364      15.1  7.596664 1.641348e-06  14.96675 8.318518  14.80876
    
    $V3
      estimate estimate1 estimate2 statistic      p.value parameter conf.low conf.high
    1 4.642857  19.74286      15.1  5.291135 4.540355e-05  18.50248 2.802925  6.482789
    

    这里有相当多的清理工作,主要是使用因子级别并在输出中保留它们(而不是在第二个函数中使用全局变量),但我认为你想要的核心就在这里。根据我的经验,不要迷失的诀窍就是从内到外工作。

答案 2 :(得分:2)

要执行两个样本t检验,您必须创建气瓶数的组合。我没有看到您可以使用purrr函数创建组合。但是,仅使用purrr和基本R函数的方法是

library(purrr)
t_test2 <- mtcars %>% split(.$cyl) %>%
          transpose() %>%
          .[["mpg"]] %>%
          (function(x) combn(names(x), m=2, function(y) t.test(flatten_dbl(x[y[1]]), flatten_dbl(x[y[2]])) , simplify=FALSE))

虽然这似乎有点做作。

仅使用基本R函数和链接的类似方法是

t_test <- mtcars %>% split(.$cyl) %>%
                          (function(x) combn(names(x), m=2, function(y) x[y], simplify=FALSE)) %>%
                           lapply( function(x) t.test(x[[1]]$mpg, x[[2]]$mpg))