I am trying to write a program in R to sum n random number. However, when I try it for some numbers it won't work.
For example,
## rm(list=ls())
random.sum <- function(n) {
x[1:n] <- ceiling(10*runif(n))
cat("x:", x[1:n], "\n")
return(sum(x))
}
x <- rep(100, 10)
show(random.sum(10))
show(random.sum(5))
when I try to sum 10 random numbers it will give me the correct sum which is
show(random.sum(10))
x: 1 3 10 1 3 2 8 6 7 9
[1] 50
However, when I try it for the next one which is 5, it won't work,
show(random.sum(5))
x: 7 5 6 2 9
[1] 529
I am not sure what I am doing wrong
The easiest way would be something like this (updated as per #Axeman's comment):
sum(sample(1:10, 10, replace = TRUE))
where the first "10" is your n and min and max define the value range for runif.
Also keep x local to the function:
random.sum <- function(n) {
x <- sample(1:10, 10, replace = TRUE)
cat("x:", x, "\n")
return(sum(x))
}
The reason for your error is the variable scoping rules of R. Your variable x in global scope is copied upon modification, but maintains the dimension of the global declaration. If you sum over only the first n elements with sum(x[1:n]) you will get the correct answer.
Now, that begs the question, are you trying to modify the global object x inside the function? If that is your intent, the superassignment operator <<- can be used. See the R intro section 10.5 "Assignments within functions" for details.
Related
I am in an intro to R course and the professor has not been much help. One of the questions on the latest homework has me stumped. The question is below, along with my answers so far.
8. [15 points] Given the following code,
#
# x <- rnorm(10)
#
# Do the following.
#
# (1) create a count vector named "count" of four elements and set each to 0 using the rep function.
# (2) using a for loop to process each value in the vector x, count how many times each of the following values occur in the vector x using an if statement.
# a. "value is between -1 and 1 inclusive"
# b. "value is between -2 and 2 inclusive, but not between -1 and 1",
# c. "value is between -3 and 3 inclusive, but not between -2 and -2", or
# d. "value is greater than 3 or less than -3".
# (3) print each of the four counts in the count vector using a while loop.
#
# For example, if the vector x contains the following ten values,
#
# 1.1478911 1.6183994 -2.3790632 -0.2566993 0.8923735
# -0.7523441 -0.7559083 0.9836396 1.0994189 2.5519972
#
# Then, the output should be as below.
#
# count[1] is 5
# count[2] is 3
# count[3] is 2
# count[4] is 0
x <- rnorm(10)
My answers:
(1) count <- c(rep(0,4))
(2)
for (count in x) {
if (x > -1 & x < 1) {
print(count[1])
}
I know there is something wrong with my code for part one but we haven't gone over anything like this in class and I have struggled to find a video for something like this. Please point me in the right direction and let me know what mistakes I have made, thanks so much!
You part one is correct. Maybe you can remove the initial c() from it.
x <- rnorm(10)
#Part 1
count <- rep(0,4)
#Part 2
for(i in x) {
if(i >= -1 && i <= 1)
count[1] <- count[1] + 1
else if(i >= -2 && i <= 2)
count[2] <- count[2] + 1
else if(i >= -3 & i <= 3)
count[3] <- count[3] + 1
else count[4] <- count[4] + 1
}
#Part 3
i <- 0
while (i < length(count)) {
i <- i + 1
print(sprintf('count[%d] is: %d', i, count[i]))
}
Note that there are better/efficient ways to do this but I think for the purpose of this exercise this is what your professor wants.
Each of the 4 slots in count is supposed to keep track of whether a value in x satisfies one of the 4 conditions listed (a. through d.).
If we were to speak it out loud, it'd go something like:
Look at element 1 in x (you can do that with x[1]). It's 1.1478911. This satisfies condition b., so add a 1 to the "b. counter", which is the second slot in count, or count[2].
Now look at element 2 in x (that's x[2])...(and so on, up to the last element in x).
To solve this task, you could just write out 10 statements, looking at each of the 10 elements in x separately, and update count on a case-by-case basis, but that is long and is hard to modify.
A for-loop is kind of like making a template for the spoken-out-loud part above. So instead of saying, "Ok, now we're on Element 3, let's see what the deal is", you can instead say, "Ok, now we're on Element i...", where i is just a temporary variable, a placeholder that only exists for the life of the for-loop. The i placeholder automatically takes on the value of the element in the vector we're iterating over.
If it's for (i in 1:3) then i will be 1, then 2, then 3.
If it's for (letter in c("a", "b", "c")), then letter will be "a", then "b", then "c".
So you can see that when you write for (count in x), that doesn't follow the rules of the for-loop. It's true that we'll want to update count at some point in the loop, but you've got it in the spot where our temporary placeholder is supposed to go. You can call that placeholder whatever you want, but i is common when looping over numbers, by convention.
Here's an example: the following code will start i at 1, and repeat the code inside the loop statement with new integers, until i gets to 10:
for (i in 1:10) {
print(paste("i is", i, "and the i'th value of x is", x[i]))
}
That should be enough to get you over the part you're stuck on.
A couple of additional hints:
f you want to know how many things are in a vector, such as x, you can use length(x) (try it, you will see the output is 10). So instead of doing: for(i in 1:10), you can swap out 10 for length(x).
count[3] <- count[3] + 1 adds 1 to whatever the current total is in the third element of count.
Good luck! Someone may post the answer to the whole problem, but if you want to work through each piece, I hope this is a good jump start for you.
I want to create a function that transforms its object.
I have tried to transform the variable as you would normally, but within the function.
This works:
vec <- c(1, 2, 3, 3)
vec <- (-1*vec)+1+max(vec, na.rm = T)
[1] 3 2 1 1
This doesn't work:
vec <- c(1, 2, 3, 3)
func <- function(x){
x <- (-1*x)+1+max(x, na.rm = T))
}
func(vec)
vec
[1] 1 2 3 3
R is functional so normally one returns the output. If you want to change
the value of the input variable to take on the output value then it is normally done by the caller, not within the function. Using func from the question it would normally be done like this:
vec <- func(vec)
Furthermore, while you can overwrite variables it is, in general, not a good
idea. It makes debugging difficult. Is the current value of vec the
input or output and if it is the output what is the value of the input? We
don't know since we have overwritten it.
func_ovewrite
That said if you really want to do this despite the comments above then:
# works but not recommended
func_overwrite <- function(x) eval.parent(substitute({
x <- (-1*x)+1+max(x, na.rm = TRUE)
}))
# test
v <- c(1, 2, 3, 3)
func_overwrite(v)
v
## [1] 3 2 1 1
Replacement functions
Despite R's functional nature it actually does provide one facility for overwriting although the function in the question is not really a good candidate for it so let us change the example to provide a function incr which increments the input variable by a given value. That is, it does this:
x <- x + b
We can write this in R as:
`incr<-` <- function(x, value) x + value
# test
xx <- 3
incr(xx) <- 10
xx
## [1] 13
T vs. TRUE
One other comment. Do not use T for true. Always write it out. TRUE is a reserved name in R but T is a valid variable name so it can lead to hard to find errors such as when someone uses T for temperature.
I want to make a function k(n) that draws 'n' times from set {0,1,2,3,4,5,6,7,8,9,10} (the same probability for each one) and then it writes to each number if this is numbers is odd or even. For example :
k(3) draws numbers 1,6,8 and then my function print that :
1 is odd
6 is even
8 is even
My work so far :
About checking odd-even numbers function :
k=function(x)
if((x %% 2) == 0) {
print(paste(x,"is Even"))
} else {
print(paste(x,"is Odd"))
}
And I have the following idea for the next problems : I will draw n times from set {0,1,2,3,4,5,6,7,8,9,10} getting a list with a length of n. Then i will use sapply function for that list.
But i have a problem with drawing n times from above set. Can you please help me with that problem and can you mention please if my idea is correct ?
You do not show what the problem is or how you are drawing the random numbers, but I'll guess that you are using sample(). Read the manual page for the function. The default behavior is selection WITHOUT REPLACEMENT, e.g:
sample(1:5, 5)
# [1] 2 5 3 1 4
sample(1:5, 6)
Error in sample.int(length(x), size, replace, prob) :
cannot take a sample larger than the population when 'replace = FALSE'
You need to draw your samples with replacement, e.g.:
set <- 0:10
set.seed(42)
x <- sample(set, 5, replace=TRUE)
OE <- ifelse(x %% 2 == 0, "Even", "Odd")
cat("\n", paste(x, "is", OE, "\n"))
#
# 0 is Even
# 4 is Even
# 0 is Even
# 8 is Even
# 9 is Odd
Note that now 0 appears twice. Since it set the random number seed to 42 you should get the same values shown here.
I am trying to simulate an unlikely situation in a videogame using a Monte Carlo simulation. I'm extremely new at coding and thought this would be a fun situation to simulate.
There are 3 targets and they are being attacked 8 times independently. My problem comes with how to deal with the fact that one of the columns cannot be attacked more than 6 times, when there are 8 attacks.
I would like to take any attack aimed at column 2 select one of the other 2 columns at random to attack instead, but only if column 2 has been attacked 6 times already.
Here is my attempt to simulate with 5000 repeats, for example.
#determine number of repeats
trial <- 5000
#create matrix with a row for each trial
m <- matrix(0, nrow = trial, ncol = 3)
#The first for loop is for each row
#The second for loop runs each attack independently, sampling 1:3 at random, then adding one to that position of the row.
#The function that is called by ifelse() when m[trial, 2] > 6 = TRUE is the issue.
for (trial in 1:trial){
for (attack in 1:8) {
target <- sample(1:3, 1)
m[trial, target] <- m[trial, target] + 1
ifelse(m[trial, 2] > 6, #determines if the value of column 2 is greater than 6 after each attack
function(m){
m[trial, 2] <- m[trial, 2] - 1 #subtract the value from the second column to return it to 6
newtarget <- sample(c(1,3), 1) #select either column 1 or 3 as a new target at random
m[trial, newtarget] <- m[trial, newtarget] + 1 #add 1 to indicate the new target has been selected
m}, #return the matrix after modification
m) #do nothing if the value of the second column is <= 6
}
}
For example, if I have the matrix below:
> matrix(c(2,1,5,7,1,0), nrow = 2, ncol = 3)
[,1] [,2] [,3]
[1,] 2 5 1
[2,] 1 7 0
I would like the function to look at the 2nd line of the matrix, subtract 1 from 7, and then add 1 to either column 1 or 3 to create c(2,6,0) or c(1,6,1). I would like to learn how to do this within the loop, but it could be done afterwards as well.
I think I am making serious, fundamental error with how to use function(x) or ifelse.
Thank you.
Here's an improved version of your code:
set.seed(1)
trial <- 5000
#create matrix with a row for each trial
m <- matrix(0, nrow = trial, ncol = 3)
#The first for loop is for each row
#The second for loop runs each attack independently, sampling 1:3 at random, then adding one to that position of the row.
#The function that is called by ifelse() when m[trial, 2] > 6 = TRUE is the issue.
for (i in 1:trial){
for (attack in 1:8) {
target <- sample(1:3, 1)
m[i, target] <- m[i, target] + 1
#determines if the value of column 2 is greater than 6 after each attack
if(m[i, 2] > 6){
#subtract the value from the second column to return it to 6
m[i, 2] <- m[i, 2] - 1
#select either column 1 or 3 as a new target at random
newtarget <- sample(c(1,3), 1)
#add 1 to indicate the new target has been selected
m[i, newtarget] <- m[i, newtarget] + 1
}
}
}
# Notice the largest value in column 2 is no greater than 6.
apply(m, 2, max)
set.seed is used to make the results reproducible (usually just used for testing). The ifelse function has a different purpose than the normal if-else control flow. Here's an example:
x = runif(100)
ifelse(x < 0.5, 0, x)
You'll notice any element in x that is less than 0.5 is now zero. I changed your code to have an if block. Notice that m[i, 2] > 6 returns a single TRUE or FALSE whereas in the small example above, x < 0.5 a vector of logicals is returned. So ifelse can take a vector of logicals, but the if block requires there be only a single logical.
You were on the right track with using function, but it just isn't necessary in this case. Often, but not always, you'll define a function like this:
f = function(x)
x^2
But just returning the value doesn't mean what you want is changed:
x = 5
f(5) # 25
x # still 5
For more on this, look up function scope in R.
Lastly, I changed the loop to be i in 1:trial instead of trial in 1:trial. You probably wouldn't notice any issues in your case, but it is better practice to use a separate variable than that which makes up the range of the loop.
Hope this helps.
P.S. R isn't really known for it's speed when looping. If you want to make things goes faster, you'll typically need to vectorize your code.
I am filling a 10x10 martix (mat) randomly until sum(mat) == 100
I wrote the following.... (i = 2 for another reason not specified here but i kept it at 2 to be consistent with my actual code)
mat <- matrix(rep(0, 100), nrow = 10)
mat[1,] <- c(0,0,0,0,0,0,0,0,0,1)
mat[2,] <- c(0,0,0,0,0,0,0,0,1,0)
mat[3,] <- c(0,0,0,0,0,0,0,1,0,0)
mat[4,] <- c(0,0,0,0,0,0,1,0,0,0)
mat[5,] <- c(0,0,0,0,0,1,0,0,0,0)
mat[6,] <- c(0,0,0,0,1,0,0,0,0,0)
mat[7,] <- c(0,0,0,1,0,0,0,0,0,0)
mat[8,] <- c(0,0,1,0,0,0,0,0,0,0)
mat[9,] <- c(0,1,0,0,0,0,0,0,0,0)
mat[10,] <- c(1,0,0,0,0,0,0,0,0,0)
i <- 2
set.seed(129)
while( sum(mat) < 100 ) {
# pick random cell
rnum <- sample( which(mat < 1), 1 )
mat[rnum] <- 1
##
print(paste0("i =", i))
print(paste0("rnum =", rnum))
print(sum(mat))
i = i + 1
}
For some reason when sum(mat) == 99 there are several steps extra...I would assume that once i = 91 the while would stop but it continues past this. Can somone explain what I have done wrong...
If I change the while condition to
while( sum(mat) < 100 & length(which(mat < 1)) > 0 )
the issue remains..
Your problem is equivalent to randomly ordering the indices of a matrix that are equal to 0. You can do this in one line with sample(which(mat < 1)). I suppose if you wanted to get exactly the same sort of output, you might try something like:
set.seed(144)
idx <- sample(which(mat < 1))
for (i in seq_along(idx)) {
print(paste0("i =", i))
print(paste0("rnum =", idx[i]))
print(sum(mat)+i)
}
# [1] "i =1"
# [1] "rnum =5"
# [1] 11
# [1] "i =2"
# [1] "rnum =70"
# [1] 12
# ...
See ?sample
Arguments:
x: Either a vector of one or more elements from which to choose,
or a positive integer. See ‘Details.’
...
If ‘x’ has length 1, is numeric (in the sense of ‘is.numeric’) and
‘x >= 1’, sampling _via_ ‘sample’ takes place from ‘1:x’. _Note_
that this convenience feature may lead to undesired behaviour when
‘x’ is of varying length in calls such as ‘sample(x)’. See the
examples.
In other words, if x in sample(x) is of length 1, sample returns a random number from 1:x. This happens towards the end of your loop, where there is just one 0 left in your matrix and one index is returned by which(mat < 1).
The iteration repeats on level 99 because sample() behaves very differently when the first parameter is a vector of length 1 and when it is greater than 1. When it is length 1, it assumes you a random number from 1 to that number. When it has length >1, then you get a random number from that vector.
Compare
sample(c(99,100),1)
and
sample(c(100),1)
Of course, this is an inefficient way of filling your matrix. As #josilber pointed out, a single call to sample could do everything you need.
The issue comes from how sample and which do the sampling when you have only a single '0' value left.
For example, do this:
mat <- matrix(rep(1, 100), nrow = 10)
Now you have a matrix of all 1's. Now lets make two numbers 0:
mat[15]<-0
mat[18]<-0
and then sample
sample(which(mat<1))
[1] 18 15
by adding a size=1 argument you get one or the other
now lets try this:
mat[18]<-1
sample(which(mat<1))
[1] 3 13 8 2 4 14 11 9 10 5 15 7 1 12 6
Oops, you did not get [1] 15 . Instead what happens in only a single integer (15 in this case) is passed tosample. When you do sample(x) and x is an integer, it gives you a sample from 1:x with the integers in random order.