3
votes
$\begingroup$

How to add noise to an object, regardless of the amount of dimensions?

I would expect to try to use some sort of apply to add noise of the type:

runif(1, -0.00001, 0.0001) for each cell.

What I tried before was to have a matrix of r * c, and then creating a matrix with noise, and add them together. It works, but I believe there is a more general solution (and I am interesting in how apply could solve this)

As a side question, should we add uniform errors or normally distributed errors?

EDIT: The reason is that I am trying to do bootstraps, and my distributions are heavily skewed. An overrepresentation of 1's. The problem is that, when I am trying to do cross-validations, sometimes I end up doing an

lm(Y ~ X1, X2)

In which there is no variance (all 1's), and therefore it can't compute and gives an NA. However, this is not good for bootstrapping anymore, because these errors are messing up what is "really" going on. I figured adding noise allows the linear model to run, since there will not be 0 variance any more?

$\endgroup$
10
  • $\begingroup$ why do you want to use apply? $\endgroup$
    – user603
    Dec 20, 2012 at 14:39
  • $\begingroup$ @user603 I have the idea it might be a quick way to "apply" one different sample of the uniform distribution to all cells of an object with any dimensions/length $\endgroup$ Dec 20, 2012 at 14:41
  • 2
    $\begingroup$ The side question makes this thread on topic here. (Otherwise, it would be purely about R programming, which would be migrated to SO.) However, it's not possible to answer the side question without more information: why are you adding noise to your matrix? What is it intended to represent or model? $\endgroup$
    – whuber
    Dec 20, 2012 at 14:47
  • 3
    $\begingroup$ Although adding the noise will make lm run, your attempted "bootstrap" may then give answers that depend arbitrarily on the noise. The NA values are a clear sign that the particular bootstrap you are attempting may be invalid or give misleading results. Please consider rethinking your analytical strategy rather than papering over its deficiencies with this artificial fix-up. Perhaps you could instead ask a question in which you describe your data and what you are trying to learn about them, point out the problem with the bootstrap, and ask about alternative approaches. $\endgroup$
    – whuber
    Dec 20, 2012 at 15:08
  • 1
    $\begingroup$ I posted a $1-$line code doing exactly what you were asking for (A function that takes as an argument either a scalar, a vector or a matrix and adds random noise using a previously chosen distribution). Since the question seems to be evolving every time an answer (excepts yours) appears, I am deleting mine. $\endgroup$
    – user10525
    Dec 21, 2012 at 10:41

4 Answers 4

4
votes
$\begingroup$

This is my answer so far, though I do not think it's the best code ever written.

Noisify <- function(data) {

  if (is.vector(data)) {
    noise <- runif(length(data), -0.00001, 0.00001)
    noisified <- data + noise
  } else {
    length <- dim(data)[1] * dim(data)[2]
    noise <- matrix(runif(length, -0.0001, 0.00001), dim(data)[1])
    noisified <- data + noise
  }
  return(noisified)
}
$\endgroup$
4
  • $\begingroup$ Looks legit. :) You might reconsider using return(). Removing that (and just leaving the object, like I did in my answer) may give you some performance boost, especially if you have a lot of return calls. $\endgroup$ Dec 20, 2012 at 15:53
  • $\begingroup$ I don't know why, but it did not return the last created object? $\endgroup$ Dec 20, 2012 at 16:10
  • 1
    $\begingroup$ You could avoid defining all those variables in the function by writing the function as Noisify <- function(data) { if (is.vector(data)) return( data + runif(length(data), -0.00001, 0.00001)) else return(data + matrix(runif(dim(data)[1] * dim(data)[2], -0.0001, 0.00001), dim(data)[1])) }. $\endgroup$
    – user10525
    Dec 20, 2012 at 22:23
  • $\begingroup$ agreed, i'll update it soon $\endgroup$ Dec 24, 2012 at 16:03
3
votes
$\begingroup$

@whuber makes excellent point regarding the goal of this endeavour, but here's a idea of how to proceed. The idea is to add each cell a corresponding amount of generated noise.

> my.data <- matrix(1:9, nrow = 3)
> my.data
     [,1] [,2] [,3]
[1,]    1    4    7
[2,]    2    5    8
[3,]    3    6    9
> random.stuff <- matrix(runif(prod(dim(my.data)), min = -0.00001, max = 0.0001), nrow = 3)
> random.stuff 
             [,1]          [,2]         [,3]
[1,] 8.488258e-05 -4.608706e-06 1.869516e-05
[2,] 2.100283e-05  8.500601e-05 7.376338e-05
[3,] 7.625872e-05  6.188059e-05 4.424394e-05
> random.stuff + my.data
         [,1]     [,2]     [,3]
[1,] 1.000085 3.999995 7.000019
[2,] 2.000021 5.000085 8.000074
[3,] 3.000076 6.000062 9.000044

This function takes in a matrix and adds some random noise.

addNoise <- function(mtx) {
  if (!is.matrix(mtx)) mtx <- matrix(mtx, byrow = TRUE, nrow = 1)
  random.stuff <- matrix(runif(prod(dim(mtx)), min = -0.00001, max = 0.0001), nrow = dim(mtx)[1])
  random.stuff + mtx
}

> new.data <- matrix(1:100, nrow = 10)
> addNoise(mtx = new.data)
           [,1]     [,2]     [,3]     [,4]     [,5]     [,6]     [,7]     [,8]     [,9]    [,10]
 [1,]  1.000002 11.00003 21.00001 31.00001 41.00004 51.00009 61.00004 71.00004 81.00002 90.99999
 [2,]  2.000002 11.99999 22.00004 32.00000 42.00006 52.00009 62.00000 72.00008 82.00001 92.00001
 [3,]  3.000069 13.00006 23.00003 33.00010 43.00004 53.00005 63.00009 73.00005 83.00008 93.00000
 [4,]  4.000088 14.00003 24.00006 34.00009 44.00002 54.00002 64.00007 74.00003 84.00007 94.00010
 [5,]  5.000073 15.00007 25.00002 35.00009 45.00001 55.00004 65.00002 75.00010 85.00006 95.00000
 [6,]  6.000062 16.00001 26.00002 36.00002 46.00008 56.00007 66.00002 76.00000 86.00006 96.00009
 [7,]  7.000007 17.00003 27.00005 37.00007 47.00002 57.00000 67.00006 77.00000 87.00007 97.00000
 [8,]  8.000019 18.00009 27.99999 37.99999 48.00003 58.00009 68.00001 77.99999 88.00003 98.00001
 [9,]  8.999995 19.00010 29.00007 39.00004 49.00008 59.00004 69.00008 79.00005 89.00001 99.00002
[10,] 10.000015 20.00007 30.00003 40.00009 50.00004 60.00009 70.00002 80.00002 90.00003 99.99999

Here's an example of working on vectors. Output will be a 1 by x matrix.

> new.data <- matrix(1:10, nrow = 1)
> addNoise(mtx = new.data)
         [,1]     [,2]     [,3]     [,4]     [,5]     [,6]     [,7]     [,8]     [,9]    [,10]
[1,] 1.000091 2.000014 2.999991 4.000047 5.000056 6.000042 6.999998 8.000097 9.000066 10.00008

Added some code to show how return() seems to slow down code.

library(rbenchmark)
addNoise <- function(mtx) {
  if (!is.matrix(mtx)) mtx <- matrix(mtx, byrow = TRUE, nrow = 1)
  random.stuff <- matrix(runif(prod(dim(mtx)), min = -0.00001, max = 0.0001), nrow = dim(mtx)[1])
  out <- random.stuff + mtx
  out
}

addNoiseReturn <- function(mtx) {
  if (!is.matrix(mtx)) mtx <- matrix(mtx, byrow = TRUE, nrow = 1)
  random.stuff <- matrix(runif(prod(dim(mtx)), min = -0.00001, max = 0.0001), nrow = dim(mtx)[1])
  out <- random.stuff + mtx
  return(out)
}

new.data <- matrix(1:100, nrow = 10)
benchmark(replications = rep(10000, 1),
          noReturn = addNoise(new.data),
          withReturn = addNoiseReturn(new.data))
        test replications elapsed relative user.self sys.self user.child sys.child
1   noReturn        10000    0.19    1.000      0.19        0         NA        NA
2 withReturn        10000    0.22    1.158      0.22        0         NA        NA
$\endgroup$
7
  • $\begingroup$ You describe the method that I already used, but what if we want a general function to do this, for any dimension sizes? When you don't want to manually have to fill in the amount of rows etc. $\endgroup$ Dec 20, 2012 at 14:59
  • $\begingroup$ I've added a function, is this what you're after? $\endgroup$ Dec 20, 2012 at 15:19
  • $\begingroup$ No, because you can't use this function for any other data, say a 10 by 10 matrix. $\endgroup$ Dec 20, 2012 at 15:20
  • $\begingroup$ I think it's OK now. I've added an example. $\endgroup$ Dec 20, 2012 at 15:22
  • 2
    $\begingroup$ What do you mean by "add it to a vector"? Using apply in this context would be like reaching for your left pocket with your right hand. $\endgroup$ Dec 20, 2012 at 15:28
3
votes
$\begingroup$

I am not sure I understand the question. The '+' function in R does this already.

> a<-matrix(1:12,nr=3)
> b<-rnorm(12)  # b is a vector
> a+b           # R adds b to a as a matrix
         [,1]     [,2]     [,3]     [,4]
[1,] 1.144552 4.946283 8.026290 11.40905
[2,] 1.038299 5.752317 7.544441 10.78278
[3,] 3.173348 5.574810 9.805634 13.65378

What do you actually need a function for, that this doesn't do?

$\endgroup$
4
  • $\begingroup$ I don't understand actually why people have a trouble understanding that with this you have to set the length of the rnorm. I'd like to just pass data of any kind to a function and that's it. $\endgroup$ Dec 21, 2012 at 5:02
  • $\begingroup$ b<-rnorm(prod(dim(a))). Was that really the problem? $\endgroup$
    – Glen_b
    Dec 21, 2012 at 7:24
  • 1
    $\begingroup$ If you have a vector, dim won't work. $\endgroup$ Dec 21, 2012 at 15:26
  • 3
    $\begingroup$ b<-rnorm(prod(dim(as.array(a))))? $\endgroup$
    – Glen_b
    Dec 21, 2012 at 21:27
0
votes
$\begingroup$

Another option would be to use jitter from base package, and which adds a small amount of noise to a numeric vector.

$\endgroup$

Not the answer you're looking for? Browse other questions tagged or ask your own question.