Assign a value to character named variable with index - r

I know we can use assign to assign values to a character name vector. For example
assign("target",1:5)
However, if we want to change the 1st element of the target(target can be a vector/matrix/list), how should we do that? target here can also be a matrix, so we can change one element, one row or one column.
I want to do something like
target[1] <- 99
if I use
assign("target[1]",99)
it will only generate a new object named target[1] and value is 99. Here is a simple and trial example
# This function is meaningless, just used to show my situation
# variable_name is a character
example_function <- function(variable_name){
assign(variable_name,1:5)
if(rnorm(1)>1){
variable_name[1] <- 99 #This will not work and I just need some function to achive this purpose
}
}
example_function("justAname")

As an alternative approach you could use the [<- function.
f = function(variable_name){
assign(variable_name,1:5)
if(rnorm(1)>1){
`[<-`(eval(as.name(variable_name)),i = 1, value = 99)
}
get(variable_name)
}
This should also work with matrices
f_mat = function(variable_name){
assign(variable_name,matrix(1:25,nrow = 5))
if(rnorm(1)>1){
`[<-`(eval(as.name(variable_name)),i = 1, j = , value = 99) # for 1st row
# `[<-`(eval(as.name(variable_name)),i = , j = 1, value = 99) # for 1st col
#specify i and j for ith row jth column
}
get(variable_name)
}
and lists similarly.

Related

Appending a vector inside a double for loop in R

I am trying to edit the values inside a vector determined before the loop. I want to append every element in the vector sequentially but can't figure out how to do it.
So here I tried
hr <- seq(0,5,1)
hr2 = hr
vec <- vector("list",length = length(hr)^2)
for (x in seq(hr)) {
for (i in seq(hr2)) {
vec[[x]] <- i +1
}
}
But this just edits the first 6 elements of the list. I would like to update element 1, then 2, then 3,.... finally element 36.
EDIT: The expected output would be a vector of length 36 with values in each element

Find rows that have closest columns' values to a specific row in a data.frame

Imagine we have one row in the data below as our reference (row # 116).
How can I find any other rows in this data whose columns' values are the same or the closest (if column value is numerical, lets say up to +/- 3 is an acceptable match) to the columns' values of this reference row?
For example, if the column value for variable prof in the reference row is beginner, we want to find another row whose value for prof is also beginner.
Or if the column value for variable study_length in the reference row is 5, we want to find another row whose value for study_length is also 5 +/- 3 and so on.
Is it possible to set up a function do this in R?
data <- read.csv("https://raw.githubusercontent.com/hkil/m/master/wcf.csv")[-c(2:6,12,17)])
reference <- data[116,]
############################# YOUR POSSIBLE ANSWER:
foo <- function(data = data, reference_row = 116, tolerance_for_numerics = 3) {
# your solution
}
# Example of use:
foo()
Here is a solution.
foo <- function(x = data, reference_row = 116, tolerance_for_numerics = 3) {
# which columns are numeric
i <- sapply(x, is.numeric)
reference <- x[reference_row, ]
# numeric columns are within a range
num <- mapply(\(y, ref, tol) {
y >= ref - tol & y <= ref + tol
}, data[i], reference[i], MoreArgs = list(tol = 3))
# other columns must match exactly (?)
other <- mapply(\(y, ref) {
y == ref
}, data[!i], reference[!i])
which(rowSums(cbind(other, num)) == ncol(data))
}
data <- read.csv("https://raw.githubusercontent.com/hkil/m/master/wcf.csv")[-c(2:6,12,17)]
# Example of use:
foo()
#> [1] 112 114 116
Created on 2022-08-13 by the reprex package (v2.0.1)

How to treat a single row of matrix in R as a matrix object

I have an R script that removes random rows from an nxm (n row, m column) matrix depending on which elements occur in a data set. I have a conditional statement that terminates if there are no rows remaining. This works fine if there are 0 rows, but not if there is one.
For example, if I have
m1 = rbind(c(1,2),c(1,4),c(2,3),c(2,4))
and I delete all rows
m1 = m1[-c(1,2,3,4),]
the conditional statement
if(length(m1[,1]) > 0)
evaluates correctly to FALSE and the program terminates, since the object m1 is a 0x2 matrix. However, if I delete all but one row, e.g.
m1 = m1[-c(1,2,4),]
the same conditional statement does not evaluate because the remaining row is no longer treated as a matrix object of dimension 1xn, but rather as a numeric vector, so dim, length(m[,1]) etc are undefined.
Is there some way to preserve a single row as a 1xn matrix object, other than checking if only a single row remains and applying t(as.matrix(m1)), which would be a very clumsy approach?
I've appended my complete script below, but the details of the script shouldn't be necessary to address this question. The while(temp_mat[,1] > 0) is the step that breaks if I have a single row (but works fine if there are none or any number of rows > 1)
seq_to_mask = function(mat){
temp_mat = mat
to_mask = c()
iter = 0
while(length(temp_mat[,1])>0){
all_instances = c(temp_mat[,1],temp_mat[,2])
#number of times a sample appears
occurrences = sort(table(all_instances))
max_instances = as.numeric(names(occurrences)[length(occurrences)])
posits = which(temp_mat[,1]==max_instances | temp_mat[,2]==max_instances)
to_mask = c(to_mask, max_instances)
temp_mat = temp_mat[-posits,]
iter = iter + 1
}
return(to_mask)
}
The reason seems to be the coercion of matrix to vector when there is a single row/column. We can use drop = FALSE (by default it is drop = TRUE)
m1 <- m1[-c(1, 2, 4), , drop = FALSE]

apply problem giving each element a different name

I need to optimize a small piece of code. The code can be simplified as following.
Let's say I have two data frame, I want to obtain a "result" data frame that is a selection of data2 with some conditions. For each line I need to add an identifier that corresponds to the line of the first data frame. This identifier is added to the resulting data frame as a column called "identity".
data=data.frame(a=sample(1:100, 100, replace=TRUE),b=sample(1:100, 100, replace=TRUE) )
data2=data.frame(a=sample(1:100, 100, replace=TRUE),b=sample(1:100, 100, replace=TRUE) )
result=NULL
for(i in 1:nrow(data)){ # I loop on each row of "data"
# if the difference between the current row and the column "a"
# of "data2" is bigger than zero we store the values of data2
boolvect=data[i,"a"]-data2$a>0
ares=data2[ boolvect,]
if(nrow(ares)>0){
# we add an identifier for such event, the identifier is the
# row number of "data"
ares$identity=i
result=rbind(result,ares)
}
}
I tried to use apply with margin 1. The results are the same but I don't know how to properly deal with the "identity" column.
all_df=apply(data, 1, function(x, data2){
val=as.numeric(x["a"])
boolvect=val-data2$a>0
return(data2[boolvect,])
}, data2=data2)
result2=do.call(rbind, all_df)
Any help please?
To get the identity column we need to iterate over the index of data.
You can do this using lapply or Map.
result1 <- do.call(rbind, lapply(seq_along(data$a), function(i) {
boolvect= data$a[i] - data2$a > 0
if(any(boolvect)) transform(data2[boolvect, ], identity = i)
}))
With Map :
result2 <- do.call(rbind, Map(function(x, y) {
boolvect = x - data2$a > 0
if(any(boolvect)) transform(data2[boolvect, ], identity = y)
}, data$a, 1:nrow(data)))
I would use lapply instead of apply and feed in the index of each row for the lapply to iterate over. It's the only way for an apply function to "know what row it's on".
all_df=lapply(1:nrow(data), function(x, data, data2){
boolvect=data[x,"a"]-data2$a>0
ares=data2[ boolvect,]
if(nrow(ares)>0){
ares$identity=x
}
return(ares)
}, data =data,data2=data2)
result2=dplyr::bind_rows(all_df)

Replacing NAs in a Matrix in R

I have created a container of NAs and am trying to replace the NAs with a specified value that is an argument in one of the functions.
num.cars.beg<-20
num.cars.end<-70
num.cars.incr<-5
num.cols<-(abs(num.cars.end-num.cars.beg)/num.cars.incr)+1
num.its<-10
car.var.mat<-matrix(NA,num.its,num.cols) #creates empty container to hold
results
car.intervals<-c(seq(num.cars.beg,num.cars.end, num.cars.incr))
colnames(car.var.mat)<-paste(car.intervals,"Cars",sep = " ")
rownames(car.var.mat)<-paste("Iter.",c(seq(1,num.its,1)),sep = "")
This has created a matrix where the rows are driven by "num.its" and the columns are "num.cars" from 20-70 in intervals of 5. For each iteration, I would like to run each column through my formula "run.sim" and replace the NAs with the value of run.sim. So for example:
num.cars = 20 num.cars = 25
num.its = 1 run.sim1 output run.sim2 output
num.its = 2 run.sim3 output run.sim4 output
where,
run.sim(num.cars = each value in car.intervals, num.its = 2)
The key to filling in the container is 1) to make sure the for loop structured as "1:numcols" or "1:numrows" and 2) that the argument in run.sim for num.cars is defined through [] of the matrix car.intervals, so that you clarify that the function loops through each row i, its respective location in each column j.
num.its <- 1
num.itr <- 10
for (i in 1:num.itr){
for (j in 1:num.cols){
car.var.mat[i,j]<-mean(run.sim(
num.cars = car.intervals[j],
num.its = num.its))
}
}

Resources