How to manually write a function which duplicates values in r? - r

I am learning how to do loops now, and trying to figure out how to write a function which duplicates arguments manually.
Essentially, I want to take something like this:
duplicate_easy <- function(x){
rep(c(x), c(x))
}
x1 <- c(3,1,9)
duplicate_easy(x1)
result: 3 3 3, 1, 9 9 9 9 9 9 9 9 9
And replace it with a for loop along the lines of,
duplicate <- function(x)
{
result <- NULL
for (i in rep(x) )
{
result <- c(result, rep(x))
}
return(result)
}
x1 <- c(3, 1, 9)
duplicate(x1)
Which is also intended to result in the same thing, but the above does not work.

Maybe this:
duplicate <- function(x)
{
result <- NULL
for (i in 1:length(x))
{
result <- c(result, rep(x[i], x[i]))
}
return(result)
}

Related

Repeat and while problems in loop with some condition

In my artificial problem, I need to remove empty values create during a sample process and make a new sampling process until I have just one value (nrow(s.df)>0). But, if the first condition is satisfied I keep the results (res[[i]] <- s.df) but if not, I need to make a new sample again and for this, I try to use repeat and while functions combining with else without success.
My example:
#Artificial data set
v0<-rnorm(20)
vNA<-rep(NA, 80)
v<-c(v0,vNA)
id<-1:100
df<-data.frame(id,v)
s_size<-c(1,2,3,4,5)
# Sampling using repeat
res<-list()
for(i in 1:length(s_size)){ # Loop for different sample size
s.df<-df[sample(nrow(df), 3), ] #sampling in data set
s.df<-s.df[complete.cases(s.df), ] #remove NAs
s.df
if (nrow(s.df)>0){
res[[i]] <- s.df# add it to the list
}
}
else{
repeat{
s.df<-df[sample(nrow(df), 3), ] #sampling in data set
s.df<-s.df[complete.cases(s.df), ] #remove NAs
if (nrow(s.df)>0){
res[[i]] <- s.df# add it to the list
}
if (nrow(res.circle)>0){break}
}
}
}
big_sample = do.call(rbind, res)
or
# Sampling using while
res<-list()
for(i in 1:length(s_size)){ # Loop
s.df<-df[sample(nrow(df), 3), ] #sampling in data set
s.df<-s.df[complete.cases(s.df), ] #remove NAs
if (nrow(s.df)>0){
res[[i]] <- s.df# add it to the list
}
}
else{
while(nrow(res.circle)>0) {
s.df<-df[sample(nrow(df), 3), ] #sampling in data set
s.df<-s.df[complete.cases(s.df), ] #remove NAs
if (nrow(s.df)>0){
res[[i]] <- s.df# add it to the list
}
}
}
big_sample = do.call(rbind, res)
This approach obviously doesn't work but if I don't use the else{}, I will overwrite the results that already satisfied the first condiction. Any ideas, please?
You could put the while loop inside the for loop and make it depend on the outcome of the if-condition then you don't need the else:
set.seed(42)
v0 <- rnorm(20)
vNA <- rep(NA, 80)
v <- c(v0, vNA)
id <- 1:100
df <- data.frame(id, v)
s_size <- c(1, 2, 3, 4, 5)
res <- list()
for (i in 1:length(s_size)) {
condition <- FALSE
while (condition == FALSE) {
s.df <- df[sample(nrow(df), 3),]
s.df <- s.df[complete.cases(s.df),]
if (nrow(s.df) > 0) {
res[[i]] <- s.df
condition <- TRUE
}
}
}
big_sample <- do.call(rbind, res)
big_sample
#> id v
#> 15 15 -0.13332134
#> 8 8 -0.09465904
#> 18 18 -2.65645542
#> 4 4 0.63286260
#> 6 6 -0.10612452
#> 2 2 -0.56469817
Created on 2020-06-11 by the reprex package (v0.3.0)

Replace Numeric values with NA

I am writing a function that takes in some vector and checks if it is numeric. If it is false, it will replace the numeric values with NA in the else statement. I have tried using the is.numeric() function in many ways, without much luck. Any help would be appreciated!
test <- function(x){
if(is.numeric(x) == TRUE){
mean.x <- mean(x)
vectorlist <- list(mean.x)
}
else
return(vectorlist)
}
x <- c("a", 1, 2)
test(x)
It sounds like you're looking for a function roughly like this:
test <- function(x){
if(is.numeric(x)){
return(mean(x))
}
else{
x[!is.na(as.numeric(x))] <- NA
return(x)
}
}
x <- c("a", 1, 2)
test(x)
Note that if (is.numeric(x)) is sufficient, you don't need the == TRUE stuff in the if clause.

Using If trying to get a vector a certain length

I'm trying to use if function to create a vector X that follows the pattern of the if statement, that is length 5. However, when I print X, I get 5 vectors with length 1. How do I fix this
for (i in 1:5) {
if (i <2){
a<-i
}
else {
a<-(i-1)
}
X<-a
print(X)
}
R overwrites the contents of your variables a and X with each loop. To avoid this, you can make X a list, and put your value in a different position with each loop.
X <- list()
a <- list()
for(i in 1:5) {
if(i<2){
a <- i
} else {
a <- i-1
}
X[i] <- a
}
Since your final plan is to create a vector, you may initialize a vector ("X") first, and then add a value ("a") in each 'for' loop.
X = vector("numeric",0)
for (i in 1:5){
if (i<2){
a <- i
}
else{
a <- (i-1)
}
X = c(X, a)
print(X)
}
X
# [1] 1 1 2 3 4

while loop construction for use with `apply`

I have a data.frame df like this:
df <- data.frame (x=1:5,y=1:5)
I want to use an apply function row-wise to this data frame, where I check a condition that is dependent on both the x and y and then change the x and y elements until they meet my condition. In this example if x and y don't add up to 8 I keep picking new random numbers for them and try again.
I figured an apply function using a while loop would be best. So I tried the following:
checkchange <- function(x) while(x[1] + x[2] < 8)
{
x[1] <- sample(5,1)
x[2] <- sample(5,1)
return(cbind(x[1],x[2]))
}
I would then plan on doing this:
newdf <- apply(df,1,checkchange)
This doesn't work. Should I be using repeat and a break or do I need to specify a return value more clearly. while loop grammar help greatly appreciated.
You are missing the curly braces for your anonymous function
This works for me:
checkchange <- function(x)
{
while((x[1] + x[2]) < 8)
{
x[1] <- sample(5,1)
x[2] <- sample(5,1)
}
return(cbind(x[1],x[2]))
}
As #Nico pointed out, the function will work with additional braces.
checkchange <- function(x) {
while (sum(x) < 8) {
# no need to sample from 1:5 if the sum has to be at least 8
x <- sample(3:5, 2, TRUE)
}
return(x)
}
The output of the apply needs to transposed to match the original arrangement of the data.
t(apply(df, 1, checkchange))
By the way, you don't need a loop for the function:
checkchange <- function(x) {
if (sum(x) < 8) {
x[1] <- sample(3:5, 1)
x[2] <- ifelse(x[1] == 3, 5, sample(seq(8 - x[1], 5), 1))
}
return(x)
}

R: creating a matrix with unknown number of rows

I have written the code below to generate a matrix containing what is, to me, a fairly complex pattern. In this case I determined that there are 136 rows in the finished matrix by trial and error.
I could write a function to calculate the number of matrix rows in advance, but the function would be a little complex. In this example the number of rows in the matrix = ((4 * 3 + 1) + (3 * 3 + 1) + (2 * 3 + 1) + (1 * 3 + 1)) * 4.
Is there an easy and efficient way to create matrices in R without hard-wiring the number of rows in the matrix statement? In other words, is there an easy way to let R simply add a row to a matrix as needed when using for-loops?
I have presented one solution that employs rbind at each pass through the loops, but that seems a little convoluted and I was wondering if there might be a much easier solution.
Sorry if this question is redundant with an earlier question. I could not locate a similar question using the search feature on this site or using an internet search engine today, although I think I have found a similar question somewhere in the past.
Below are 2 sets of example code, one using rbind and the other where I used trial and error to set nrow=136 in advance.
Thanks for any suggestions.
v1 <- 5
v2 <- 2
v3 <- 2
v4 <- (v1-1)
my.matrix <- matrix(0, nrow=136, ncol=(v1+4) )
i = 1
for(a in 1:v2) {
for(b in 1:v3) {
for(c in 1:v4) {
for(d in (c+1):v1) {
if(d == (c+1)) l.s = 4
else l.s = 3
for(e in 1:l.s) {
my.matrix[i,c] = 1
if(d == (c+1)) my.matrix[i,d] = (e-1)
else my.matrix[i,d] = e
my.matrix[i,(v1+1)] = a
my.matrix[i,(v1+2)] = b
my.matrix[i,(v1+3)] = c
my.matrix[i,(v1+4)] = d
i <- i + 1
}
}
}
}
}
my.matrix2 <- matrix(0, nrow=1, ncol=(v1+4) )
my.matrix3 <- matrix(0, nrow=1, ncol=(v1+4) )
i = 1
for(a in 1:v2) {
for(b in 1:v3) {
for(c in 1:v4) {
for(d in (c+1):v1) {
if(d == (c+1)) l.s = 4
else l.s = 3
for(e in 1:l.s) {
my.matrix2[1,c] = 1
if(d == (c+1)) my.matrix2[1,d] = (e-1)
else my.matrix2[1,d] = e
my.matrix2[1,(v1+1)] = a
my.matrix2[1,(v1+2)] = b
my.matrix2[1,(v1+3)] = c
my.matrix2[1,(v1+4)] = d
i <- i+1
if(i == 2) my.matrix3 <- my.matrix2
else my.matrix3 <- rbind(my.matrix3, my.matrix2)
my.matrix2 <- matrix(0, nrow=1, ncol=(v1+4) )
}
}
}
}
}
all.equal(my.matrix, my.matrix3)
If you have some upper bound on the size of the matrix,
you can create a matrix
large enough to hold all the data
my.matrix <- matrix(0, nrow=v1*v2*v3*v4*4, ncol=(v1+4) )
and truncate it at the end.
my.matrix <- my.matrix[1:(i-1),]
This is the generic form to do it. You can adapt it to your problem
matrix <- NULL
for(...){
...
matrix <- rbind(matriz,vector)
}
where vector contains the row elements
I stumbled upon this solution today: convert the matrix to a data.frame. As new rows are needed by the for-loop those rows are automatically added to the data.frame. Then you can convert the data.frame back to a matrix at the end if you want. I am not sure whether this constitutes something similar to iterative use of rbind. Perhaps it becomes very slow with large data.frames. I do not know.
my.data <- matrix(0, ncol = 3, nrow = 2)
my.data <- as.data.frame(my.data)
j <- 1
for(i1 in 0:2) {
for(i2 in 0:2) {
for(i3 in 0:2) {
my.data[j,1] <- i1
my.data[j,2] <- i2
my.data[j,3] <- i3
j <- j + 1
}
}
}
my.data
my.data <- as.matrix(my.data)
dim(my.data)
class(my.data)
EDIT: July 27, 2015
You can also delete the first matrix statement, create an empty data.frame then convert the data.frame to a matrix at the end:
my.data <- data.frame(NULL,NULL,NULL)
j <- 1
for(i1 in 0:2) {
for(i2 in 0:2) {
for(i3 in 0:2) {
my.data[j,1] <- i1
my.data[j,2] <- i2
my.data[j,3] <- i3
j <- j + 1
}
}
}
my.data
my.data <- as.matrix(my.data)
dim(my.data)
class(my.data)

Resources