I'm creating a basic function that runs a while loop and an if statement for an R class and am looking for help.
I don't want to overcomplicate things, so I'd prefer to stick to just the basics with this answer.
I created a basic football score function that's intention is to add 7 points to the score if yards >=80, add 3 points to the total score if yards >= 60 (with else if), and add 0 to the total score if anything <= 60.
This is where I have started:
teamA <- function(drives) {
i <- 0
score <- 0
while (i < drives){
yards <- sample(0:100,1)
if (yards >= 80){
score <- score + 7
}
else if (yards >= 60){
score <- score + 3
}
else {
score <- score
}
i <- i + 1
return (score)
}
}
teamA(5)
This is obviously not accurate to real football, but I wanted to simplify it for class.
I wanted to make a function where you could specify an amount of drives a team had and compile a score based on a random amount of yards generated by the sample I wrote in the while loop.
Would anyone be able to help fix this code? I'm not very experienced with R and can't think of the best way to solve my issue.
My biggest issue right now is that it seems like I'm only getting one score returned and not compiling a total score.
The problem in your code is that you've placed return(score) in the while loop. return terminates the function and returns the corresponding value. Therefore, your function always gets terminated after the first iteration of your loop.
Another edit I made to your code is to remove the last condition, because it doesn't change the value of score.
set.seed(4)
teamA <- function(drives) {
i <- 0
score <- 0
while (i < drives) {
yards <- sample(0:100,1)
if (yards >= 80) {
score <- score + 7
}
else if (yards >= 60) {
score <- score + 3
}
i <- i + 1
}
return(score)
}
teamA(5)
[1] 6
An easy way to debug such functions is to place a browser() in the code and see what happens in the function.
Related
I know it's a StackOverflow sin to not create a reproducible example, but I'm not sure it's necessary in this. The following nested loop takes 40 minutes to run. There are ~6,300 rows in ptptdata, which represents an individual. The loop just pulls someone's age and duration until retirement. It runs a loop that adds inflation (taken from infdata) and merit growth (static 2.5%) until duration. Once the loop ends, it saves the final salary and age back to ptptdata. It then moves down a row to the next individual and does it again. I've read that using a vector instead of adding back to ptptdata would be faster, but I'm not sure I'm following how to do that. I was expecting the code to be slow, but I have a lot more to add to this and it won't be feasible if this simple process is taking this long.
ptptdata$FinalSalary <- 0
ptptdata$FinalAge <- 0
trial <- 1
for(row in 1:nrow(ptptdata)){ ### Tells model to complete loop for each individual
i <- 1 ## Starting point for each individual
dur <- as.numeric(ptptdata[row,"DurationRet"])
age <- as.numeric(ptptdata[row, "Age"])
salary <- as.numeric(ptptdata[row,"Current Salary"])
while (i<=dur){
inflation <- as.numeric(infdata[infdata$Item == 'Inflation' & infdata$Scenario == as.factor(trial),i+2])
salboy <- salary
ageboy <- age
salary <- salboy * (1+meritgrowth)* (1+inflation)
age <- age + 1
i <- i + 1
}
ptptdata[row,"FinalSalary"]<-salary
ptptdata[row,"FinalAge"]<-age
}
ptptdata$FinalSalary <- 0
ptptdata$FinalAge <- 0
trial <- 1
durv <- as.numeric(ptptdata$DurationRet)
agev <- as.numeric(ptptdata$Age)
salaryv <- as.numeric(ptptdata[['Current Salary']])
xx <- infdata$Item == 'Inflation' & infdata$Scenario == as.factor(trial)
for(row in 1:nrow(ptptdata)) {
i <- 1
dur <- durv[row]
age <- agev[row]
salary <- salaryv[row]
while (i<=dur){
inflation <- as.numeric(infdata[[i+2]][xx])
salboy <- salary
ageboy <- age
salary <- salboy * (1+meritgrowth)* (1+inflation)
age <- age + 1
i <- i + 1
}
ptptdata[row,"FinalSalary"]<-salary
ptptdata[row,"FinalAge"]<-age
}
maybe this will be faster...
I am currently trying to simulate a (random) market in R using a while-loop that runs while the market is open: while the time is less than 600 minutes.
On this market only one of four events may happen at any time: birth of a supply, birth of a demand, death of a supply or death of a demand.
These are all drawn from exponential distributions using the rexp()-command with their own intensity. Their amounts and respective prices are each drawn from their own normal distribution (only values greater than 0), and the time is then updated depending on which of the events is drawn first.
Then I would like to update these intensities (using cox-regression), and for this to happen I need to store previous information about each of the events, preferably in a list, so that I can for example draw samples from the living supplies and remove them, to imitate a purchase. I basically want to keep track of what is "alive" on the market at a given time. Here is some of my code:
TIME <- 0
count <- 1
...
my.stores <- c()
while(TIME < 600){
time.supply.birth <- rexp(1, intensity1)
time.supply.death <- rexp(1, intensity2)
time.demand.birth <- rexp(1, intensity3)
time.demand.death <- rexp(1, intensity4)
case1 <- time.supply.birth == min(time.supply.birth, time.demand.birth, time.supply.death, time.demand.death)
case2 <- time.supply.death == min(time.supply.birth, time.demand.birth, time.supply.death, time.demand.death)
case3 <- time.demand.birth == min(time.supply.birth, time.demand.birth, time.supply.death, time.demand.death)
case4 <- time.demand.death == min(time.supply.birth, time.demand.birth, time.supply.death, time.demand.death)
TIME <- TIME + time.supply.birth*case1 + time.supply.death*case2 + time.demand.birth*case3 + time.demand.death*case4
if(case1 == T){
amount.supply.birth <- rnorm() # with values
price.supply.birth <- rnorm()
count.supply.birth.event <- count.supply.birth.event + 1
my.stores[[count]]$amount.supply.birth <- c(my.stores[[count-1]]$amount.supply.birth, amount.supply.birth)
my.stores[[count]]$price.supply.birth <- c(my.stores[[count-1]]$price.supply.birth, price.supply.birth)
} else if(case2 ==T) {
# Death supply event: here a sample from the living supplies should be drawn
} else if(case3 == T){
# Similar to case 1
} else if(case4 == T){
# similar to case 2
} else{
}
count <- count + 1
}
My problem is that I cannot even store any information in the list, since the while-loop breaks immediately after one iteration, which results in the length of the list my.stores to be only 1 - I bet it is something about my indexing in the list, but I'm not sure how to get around it. I get the following warning:
Error in my.stores[[count - 1]] :
attempt to select less than one element in get1index <real>
and when I print the list I get the following:
> my.stores[[1]]
$amount.demand.birth
[1] 6.044815
Say I draw a demand.birth with an amount and a price, and then the next iteration I similarly draw a supply.birth, I would have liked something like:
> my.stores[[1]]
$amount.demand.birth
[1] 6.044815
$amount.supply.birth
[1] 0
$price.demand.birth
[1] 50.78
$price.supply.birth
[1] 0
> my.stores[[2]]
$amount.demand.birth
[1] 6.044815
[2] 6.044815
$amount.supply.birth
[1] 0
[2] 7.1312
$price.demand.birth
[1] 50.78
[2] 50.78
$price.supply.birth
[1] 0
[2] 95.00
Anyone who can help me with this or who has other suggestions?
Sorry about the long post.
Cheers!
Since my.stores[[0]] is not valid, did you try :
if (count==1) {
my.stores[[count]]$amount.supply.birth <- amount.supply.birth
my.stores[[count]]$price.supply.birth <- price.supply.birth
}
else {
my.stores[[count]]$amount.supply.birth <- c(my.stores[[count-1]]$amount.supply.birth, amount.supply.birth)
my.stores[[count]]$price.supply.birth <- c(my.stores[[count-1]]$price.supply.birth, price.supply.birth)
}
How can the following be accomplished with R?
Connect a constantly changing data source (e.g. https://goo.gl/XCM6yG) into R,
Measure time once prices start to rise consistently from initial baseline range to peak (represented by the green horizontal line),
Measure time from peak back to baseline range (the teal line)
Note: "Departure from baseline range" (unless there is a better mathematical way) defined as at least the most recent 5 prices all being over 3 standard deviations above the mean of the latest 200 prices
This is a really vague questions with an unknown use case but... here we go.
Monitoring in what way? The length? That's what I did
The vector has over 200 values we can take the mean, so we need a control flow for that part.
I added in some noise which basically says force the behavior you want to calculate ( ifelse(i %in% 996:1000, 100, 0) which means, if the iterator is in 996 to 1000, add 100 to the random normal i generated). We set a counter and check if each value is about 3 sd of the vector values, if so we record the time.
At each input of the data...check if the current value is the max value... now this is more tricky since we would have to look at the trend. This is beyond the scope of my assistance.
Up to you to figure out since I don't really understand
vec <- vecmean <- val5 <- c()
counter <- 0
for(i in 1:1000){
vec[i] <- rnorm(1) + ifelse(i %in% 996:1000, 100, 0)
Sys.sleep(.001) # change to 1 second
#1
cat('The vector has',length(vec),'values within...\n')
#2
if(length(vec)>200){
vecmean <- c(vecmean, mean(vec[(i-200):i]))
cat('The mean of the last 200 observations is ',
format(vecmean[length(vecmean)], digits =2),'\n')
#3
upr <- vecmean[length(vecmean)] + 3*sd(vec)
if(vec[i] > upr){
counter <- counter + 1
} else{
counter <- 0
}
if(counter > 4){
cat('Last 5 values greater than 3sd aboving the rolling mean!\n')
val5 <- Sys.time()
cat("Timestamp:",as.character(val5),'\n')
}
}
# 4
theMax <- max(vec)
if(vec[i] == theMax & !is.null(val5) ){
valMax <- Sys.time()
valDiff <- valMax - val5
cat('The time difference between the first flag and second is', as.character(valDiff),'\n')
}
}
I'm new to R, so most of my code is most likely wrong. However, I was wondering how to use a while() loop within a for() loop. I'm trying to simulate rolling a pair of dice several times if the total 2,3,7,11,or 12 then I stop. If the total 4,5,6,8,9, or 10 then I continue to the roll the dice until the initial total appears or 7. I'm trying to find the average number of rolls it take to end the game
count = 0
x = NULL
for (i in 1:10) {
x[i] = c(sample(1:6,1) +sample(1:6,1))
if(x[i] == c(2||3||7||11||12)) {
if(TRUE) {count = count +1}
} else { while(x[i] == c(4||5||6||8||9||10)) {
x[i +1] = c(sample(1:6,1)+sample(1:6,1))
if(x[i+1] == c(x[i]||7)) {
if(TRUE){count = count + x[i+1]}
}
}
}
}
print(count)
I think there are a few issues with your logic. I'm not quite sure what you're trying to do in your code, but this is my interpretation of your description of your problem ... this only runs a single round of your game -- it should work if you embed it in a for loop though (just don't reset count or reset the random-number seed in side your loop -- then count will give you the total number of rolls, and you can divide by the number of rounds to get the average)
Setup:
count = 0
sscore <- c(2,3,7,11,12)
set.seed(101)
debug = TRUE
Running a single round:
x = sample(1:6,1) +sample(1:6,1) ## initial roll
count = count + 1
if (x %in% sscore) {
## don't need to do anything if we hit,
## as the roll has already been counted
if (debug) cat("hit",x[i],"\n")
} else {
## initialize while loop -- try one more time
y = c(sample(1:6,1)+sample(1:6,1))
count = count + 1
if (debug) cat("initial",x,"next",y,"\n")
while(!(y %in% c(x,7))) {
y = c(sample(1:6,1)+sample(1:6,1))
count = count+1
if (debug) cat("keep trying",y,"\n")
} ## end while
} ## end if-not-hit
print(count)
I tried embedding this in a for loop and got a mean of 3.453 for 1000 rounds, close to #PawelP's answer.
PS I hope this isn't homework, as I prefer not to answer homework questions ...
EDIT: I had a bug - forgot to remove if negation. Now the below seems to be 100% true to your description of the problem.
This is my implementation of the game you've described. It calculates the average number of rolls it took to end the game over a TOTAL_GAMES many games.
TOTAL_GAMES = 1000
counts = rep(0, TOTAL_GAMES)
x = NULL
for (i in 1:TOTAL_GAMES) {
x_start = c(sample(1:6,1) +sample(1:6,1))
counts[i] = counts[i] + 1
x = x_start
if(x %in% c(2, 3, 7, 11, 12)){
next
}
repeat {
x = c(sample(1:6,1)+sample(1:6,1))
counts[i] = counts[i] + 1
if(x %in% c(x_start, 7)){
break
}
}
}
print(mean(counts))
It seems that the average number of rolls is around 3.38
Here's one approach to this question - I made a function that runs a single trial, and another function which conducts a variable number of these trials and returns the cumulative average.
## Single trial
rollDice <- function(){
init <- sample(1:6,1)+sample(1:6,1)
rolls <- 1
if( init %in% c(2,3,7,11,12) ){
return(1)
} else {
Flag <- TRUE
while( Flag ){
roll <- sample(1:6,1)+sample(1:6,1)
rolls <- rolls + 1
if( roll %in% c(init,7) ){
Flag <- FALSE
}
rolls
}
}
return(rolls)
}
## Multiple trials
simAvg <- function(nsim = 100){
x <- replicate(nsim,rollDice())
Reduce("+",x)/nsim
}
##
## Testing
nTrial <- seq(1,1000,25)
Results <- sapply(nTrial, function(X){ simAvg(X) })
##
## Plot over varying number of simulations
plot(x=nTrial,y=Results,pch=20)
As #Ben Bolker pointed out, you had a couple of syntax errors with ||, which is understandable for someone new to R. Also, you'll probably hear it a thousand times, but for and while loops are pretty inefficient in R so you generally want to avoid them if possible. In the case of the while loop in the above rollDice() function, it probably isn't a big deal because the probability of the loop executing a large number of times is very low. I used the functions Reduce and replicate to serve the role of a for loop in the second function. Good question though, it was fun to work on.
I am trying to write a program that sets a state from A to state B and vice versa.
rnumbers <- data.frame(replicate(5,runif(2000, 0, 1)))
I am imagining this data frame of random numbers in a uniform distribution, except it has 10000 rows instead of 20 rows.
Setting the probability of going to state A and state B :
dt <- c(.02)
A <- dt*1
B <- dt*.5
Making a function that goes through data frame rnumbers and putting in a 0 if the number is less than B and a 1 if the number is less than A.
step_generator <- function(x){
step <- ifelse ( x < B, 0, ifelse(x < A, 1, NA))
return(step)
}
state <- apply(rnumbers, 2, step_generator)
This essentially gives me what I want - a data frame with columns that contain 0, 1, or NA depending on the value of the random number in rnumbers. However, I am missing a couple of things--
1) I would like to keep track of how long each state lasts. What I mean by that, is if you imagine each row as a change in time as above (dt <- c(.02)). I want to be able to plot "state vs. time". In order to address this, this is what I tried :
state1 <- transform(state, time = rep(dt))
state2 <- transform(state1, cumtime = cumsum(time))
This gets me close to what I want, cumtime goes from .02 to .4. However, I want the clock to start at 0 in the 1st row and add .02 to every subsequent row.
2) I need to know how long each state lasts for. Essentially, I want to be able to go through each column, and ask for how much time (cumsum) does each state last. This would then give me a distribution of times for state A and state B. I want this stored in another data frame.
I think this makes sense, if anything is unclear please let me know and I will clarify.
Thanks for any and all help!
The range between "number is less than .02*1 and greater than .02*.5" is very narrow so if you are setting this simulation up, most of the first row will most probably be zero. You cannot really hope to get success with ifelse when the conditions have any look-back features. That function doesn't allow "back-indexing".
rstate <- rnumbers # copy the structure
rstate[] <- NA # preserve structure with NA's
# Init:
rstate[1, ] <- rnumbers[1, ] < .02 & rnumbers[1, ] > 0.01
step_generator <- function(col, rnum){
for (i in 2:length(col) ){
if( rnum[i] < B) { col[i] <- 0 }
else { if (rnum[i] < A) {col[i] <- 1 }
else {col[i] <- col[i-1] } }
}
return(col)
}
# Run for each column index:
for(cl in 1:5){ rstate[ , cl] <-
step_generator(rstate[,cl], rnumbers[,cl]) }