使用 dplyr 在 data.frame 中查找逐行最小正非零数

Posted

技术标签:

【中文标题】使用 dplyr 在 data.frame 中查找逐行最小正非零数【英文标题】:Find row-wise minimum positive non-zero number in data.frame using dplyr 【发布时间】:2019-02-27 09:42:51 【问题描述】:

给定一个数字数据框

A <- c(1.1, 3.0, 2.0, 4.0, 0.0, 1.3)
B <- c(0.2, 1.0, 2.4, 1.1, 1.3, 0.0)
C <- c(5.2, 1.3, 3.7, 1.7, 1.3, 1.0)

data <- data.frame(A, B, C) %>% as_tibble()

如何创建包含按行最小正非零数的另一列(如果可能,使用 dplyr)以获得以下数据帧?

## A tibble: 6 x 4
#      A     B     C posmin
#  <dbl> <dbl> <dbl>  <dbl>
#1   1.1   0.2   5.2    0.2
#2   3     1     1.3    1  
#3   2     0     3.7    2  
#4   4     1.1   1.7    1.1
#5   0     1.3   1.3    1.3  
#6   1.3   0     1      1  

什么是简洁,几乎可以完成工作的是

data %>% mutate(posmin = pmin(A, B, C))

但是有两个问题:

我的真实数据框有更多列(A 到 Z),我无法调用pmin(A:Z) pmin 计算行最小值

是否有类似pminpos 的东西,如果没有,我该如何创建它以便可以像上面代码中的pmin 一样调用它?以及如何在不传递以逗号分隔的名称列表的情况下指定许多连续的列?

非常感谢。

编辑:我显然没有强调这一点,我正在寻找非零正数,即严格大于 > 0 的数字。因此寻求-after 第 5 行和第 6 行的值不为零。

【问题讨论】:

第 5 行和第 6 行的最小值应该为 0,不是吗? @JilberUrbina 我正在寻找严格大于零的数字。很抱歉含糊不清 - 我调整了问题的措辞。 【参考方案1】:

一种选择是将列名转换为符号,然后计算 (!!!)

library(dplyr)
data %>% 
   mutate_all(funs(replace(., .==0, NA))) %>% 
   transmute(posmin = pmin(!!! rlang::syms(names(.)), na.rm = TRUE)) %>%
   bind_cols(data, .)

# A tibble: 6 x 4
#      A     B     C posmin
#  <dbl> <dbl> <dbl>  <dbl>
#1   1.1   0.2   5.2    0.2
#2   3     1     1.3    1  
#3   2     2.4   3.7    2  
#4   4     1.1   1.7    1.1
#5   0     1.3   1.3    1.3  
#6   1.3   0     1      1 

或使用map/reduce

map(data, na_if, 0) %>% 
    reduce(pmin, na.rm = TRUE) %>% 
    bind_cols(data, posmin = .)

或者不使用任何外部包,我们可以在do.call内单行调用pmin

data$posmin <- do.call(pmin, c(NA^ (data == 0) * data, na.rm = TRUE))
data$posmin
#[1] 0.2 1.0 2.0 1.1 1.3 1.0

或者基于@Moody_Mudskipper的cmets,而不是将0分配给NA,而是将其更改为更大的值(Inf),然后使用pmin

data$posmin <- do.call(pmin, '[<-'(data, data <=0, value=Inf))

【讨论】:

我认为 OP 只寻找 > 0 的最小值,即类似于data$posmin &lt;- do.call(pmin, c(replace(data, data == 0, NA), na.rm = TRUE)) @markus 没错。我正在寻找严格大于零的值 另一种类似的方式是data$posmin &lt;- do.call(pmin, '[&lt;-'(data, data &lt;=0, value=Inf)) @Moody_Mudskipper 谢谢,这是一个紧凑的选择【参考方案2】:

apply 适合这里:

> data$posmin <- apply(data, 1, function(x) min(x[x>0]))
> data
# A tibble: 6 x 4
      A     B     C posmin
  <dbl> <dbl> <dbl>  <dbl>
1   1.1   0.2   5.2    0.2
2   3     1     1.3    1  
3   2     2.4   3.7    2  
4   4     1.1   1.7    1.1
5   0     1.3   1.3    1.3
6   1.3   0     1      1  

【讨论】:

【参考方案3】:

您可以使用invokena_if,正如@markus 所评论的那样

   data %>% 
       mutate(posmin = invoke(pmin, na_if(., 0), na.rm = TRUE))

# A tibble: 6 x 4
      A     B     C posmin
  <dbl> <dbl> <dbl>  <dbl>
1   1.1   0.2   5.2    0.2
2   3     1     1.3    1  
3   2     2.4   3.7    2  
4   4     1.1   1.7    1.1
5   0     1.3   1.3    1.3
6   1.3   0     1      1  

【讨论】:

或者data %&gt;% mutate(posmin = invoke(pmin, na_if(., 0), na.rm = TRUE))【参考方案4】:

这是一个类似于apply() 的tidyverse 解决方案,使用purrr 包:

data %>% mutate(posmin = pmap(data, min))

【讨论】:

以上是关于使用 dplyr 在 data.frame 中查找逐行最小正非零数的主要内容,如果未能解决你的问题,请参考以下文章

覆盖使用 Dplyr - R 过滤的 data.frame 上的值

查找重复行(使用dplyr)

当我不知道 data.frame 中的列名时,当我使用 dplyr mutate 函数时

R dplyr:来自外部查找表的 summarise_each?

dplyr、lapply 或 Map 以识别来自一个 data.frame 的信息并将其放入另一个 [重复]

基于存储为R w / dplyr mutate()中的向量的列索引连接data.frame字符列?