用零替换负值

Posted

技术标签:

【中文标题】用零替换负值【英文标题】:Replace negative values by zero 【发布时间】:2012-07-01 18:37:42 【问题描述】:

我们希望将数组中的所有值设置为零。

我尝试了很多东西,但还没有找到可行的解决方案。 我想过一个带条件的 for 循环,但这似乎不起作用。

#pred_precipitation is our array
pred_precipitation <-rnorm(25,2,4)     

for (i in nrow(pred_precipitation))

  if (pred_precipitation[i]<0) pred_precipitation[i] = 0
  elsepred_precipitation[i] = pred_precipitation[i]

【问题讨论】:

【参考方案1】:

感谢您提供可重现的示例。这是非常基本的 R 东西。您可以分配给向量的选定元素(注意数组有维度,您给出的是向量而不是数组):

> pred_precipitation[pred_precipitation<0] <- 0
> pred_precipitation
 [1] 1.2091281 0.0000000 7.7665555 0.0000000 0.0000000 0.0000000 0.5151504 0.0000000 1.8281251
[10] 0.5098688 2.8370263 0.4895606 1.5152191 4.1740177 7.1527742 2.8992215 4.5322934 6.7180530
[19] 0.0000000 1.1914052 3.6152333 0.0000000 0.3778717 0.0000000 1.4940469

基准战!

@James 找到了一种更快的方法并将其留在评论中。我支持他,只是因为我知道他的胜利是短暂的。

首先,我尝试编译,但这似乎对任何人都没有帮助:

p <- rnorm(10000)
gsk3 <- function(x)  x[x<0] <- 0; x 
jmsigner <- function(x) ifelse(x<0, 0, x)
joshua <- function(x) pmin(x,0)
james <- function(x) (abs(x)+x)/2
library(compiler)
gsk3.c <- cmpfun(gsk3)
jmsigner.c <- cmpfun(jmsigner)
joshua.c <- cmpfun(joshua)
james.c <- cmpfun(james)

microbenchmark(joshua(p),joshua.c(p),gsk3(p),gsk3.c(p),jmsigner(p),james(p),jmsigner.c(p),james.c(p))
           expr      min        lq    median        uq      max
1     gsk3.c(p)  251.782  255.0515  266.8685  269.5205  457.998
2       gsk3(p)  256.262  261.6105  270.7340  281.3560 2940.486
3    james.c(p)   38.418   41.3770   43.3020   45.6160  132.342
4      james(p)   38.934   42.1965   43.5700   47.2085 4524.303
5 jmsigner.c(p) 2047.739 2145.9915 2198.6170 2291.8475 4879.418
6   jmsigner(p) 2047.502 2169.9555 2258.6225 2405.0730 5064.334
7   joshua.c(p)  237.008  244.3570  251.7375  265.2545  376.684
8     joshua(p)  237.545  244.8635  255.1690  271.9910  430.566

但是等等! Dirk 写了这个 Rcpp 东西。一个完全没有 C++ 能力的人可以阅读他的 JSS 论文,修改他的示例,并编写所有这些中最快的函数吗?亲爱的听众,敬请期待。

library(inline)
cpp_if_src <- '
  Rcpp::NumericVector xa(a);
  int n_xa = xa.size();
  for(int i=0; i < n_xa; i++) 
    if(xa[i]<0) xa[i] = 0;
  
  return xa;
'
cpp_if <- cxxfunction(signature(a="numeric"), cpp_if_src, plugin="Rcpp")
microbenchmark(joshua(p),joshua.c(p),gsk3(p),gsk3.c(p),jmsigner(p),james(p),jmsigner.c(p),james.c(p), cpp_if(p))
         expr      min        lq    median        uq       max
1   cpp_if(p)    8.233   10.4865   11.6000   12.4090    69.512
2     gsk3(p)  170.572  172.7975  175.0515  182.4035  2515.870
3    james(p)   37.074   39.6955   40.5720   42.1965  2396.758
4 jmsigner(p) 1110.313 1118.9445 1133.4725 1164.2305 65942.680
5   joshua(p)  237.135  240.1655  243.3990  250.3660  2597.429

这是肯定的,船长。

这会修改输入p,即使您没有分配给它。如果你想避免这种行为,你必须克隆:

cpp_ifclone_src <- '
  Rcpp::NumericVector xa(Rcpp::clone(a));
  int n_xa = xa.size();
  for(int i=0; i < n_xa; i++) 
    if(xa[i]<0) xa[i] = 0;
  
  return xa;
'
cpp_ifclone <- cxxfunction(signature(a="numeric"), cpp_ifclone_src, plugin="Rcpp")

不幸的是,这扼杀了速度优势。

【讨论】:

Ari 和@DirkEddelbuettel:它真的修改p 而不分配吗?当我尝试它时似乎没有。 @Aaron 在这里查看 Dirk 的解释:***.com/questions/11300048/…【参考方案2】:

我会使用pmax,因为ifelse 有时会有点慢,并且子集替换会创建一个额外的向量(这可能是大型数据集的问题)。

set.seed(21)
pred_precipitation <- rnorm(25,2,4)
p <- pmax(pred_precipitation,0)

子集替换是迄今为止最快的:

library(rbenchmark)
gsk3 <- function(x)  x[x<0] <- 0; x 
jmsigner <- function(x) ifelse(x<0, 0, x)
joshua <- function(x) pmin(x,0)
benchmark(joshua(p), gsk3(p), jmsigner(p), replications=10000, order="relative")
         test replications elapsed relative user.self sys.self
2     gsk3(p)        10000   0.215 1.000000     0.216    0.000
1   joshua(p)        10000   0.444 2.065116     0.416    0.016
3 jmsigner(p)        10000   0.656 3.051163     0.652    0.000

【讨论】:

+1 用于基准测试。添加了时序图(在taRifx 包中使用autoplot.microbenchmark @gsk3:哇,你做了什么让我的解决方案变得更糟? :P (abs(p)+p)/2 似乎更快 pmax 也“已知”很慢 相比 rbenchmark,我更相信微基准测试的结果 - 它使用 个更高精度的计时器,并以 rbenchmark 无法做到的方式随机化复制的顺序。跨度> 【参考方案3】:

或者你也可以使用ifelse:

ifelse(pred_precipitation < 0, 0, pred_precipitation)

【讨论】:

【参考方案4】:

如果您的主要对象是 tibble 或数据框,您也可以使用 tidy 包。与 Ari B. Friedman 提出的替换相比,替换可以“即时”编写并与其他突变结合。

使用 dplyr 和 %&gt;% 管道的示例如下所示:

df %>% mutate(varA = if_else(varA < 0, 0, varA))

您可以在mutate() 语句中添加更多突变(即新变量)。我在这种类型的编码中看到的一个优点是您不会冒跳过或重新执行单个转换步骤的风险,因为它们都分组在一个语句中。 例如,通过在 RStudio 中添加%&gt;% View(),您已经可以预览结果。但是,结果尚未存储在任何地方(“即时”)。这样,您在更改代码时可以保持命名空间/环境的清洁。

【讨论】:

以上是关于用零替换负值的主要内容,如果未能解决你的问题,请参考以下文章

如何优化 spark 函数以用零替换空值?

sql 用零替换零的整数,并将其作为整数。

用负值替换负数的会计符号

我怎么能将此列中的负值替换为0

以独立于类型的变量属性为条件替换所有负值

Pandas 替换 NaN 值