Loop over vector containing NULL - r

I want to loop over a vector and send the values as parameters to a function. One of the values I want to send is NULL. This is what I've been trying
things <- c('M','F',NULL)
for (thing in things){
doSomething(thing)
}
But the loop ignores the NULL value. Any suggestions?

The loop doesn't ignore it. Look at things and you'll see that the NULL isn't there.
You can't mix types in a vector, so you can't have both "character" and "NULL" types in the same vector. Use a list instead.
things <- list('M','F',NULL)
for (thing in things) {
print(thing)
}
[1] "M"
[1] "F"
NULL

When you construct a vector with c(), a value of NULL is ignored:
things <- c('M','F',NULL)
things
[1] "M" "F"
However, if it important to pass the NULL downstream, you can use a list instead:
things <- list('M','F',NULL)
for (thing in things){
print(thing)
}
[1] "M"
[1] "F"
NULL

Related

Why subtracting an empty vector in R deletes everything?

Could someone please enlighten me why subtracting an empty vector in R results in the whole content of a data frame being deleted? Just to give an example
WhichInstances2 <- which(JointProcedures3$JointID %in% KneeIDcount$Var1[KneeIDcount$Freq >1])
JointProcedures3 <-JointProcedures3[-WhichInstances2,]
Will give me all blanks in JointProcedures3 if WhichInstances2 has all its value as FALSE, but it should simply give me what JointProcedures3 was before those lines of code.
This is not the first time it has happened to me and I have asked my supervisor and it has happened to him as well and he just thinks t is a quirk of R.
Rewriting the code as
WhichInstances2 <- which(JointProcedures3$JointID %in% KneeIDcount$Var1[KneeIDcount$Freq >1])
if(length(WhichInstances2)>0)
{
JointProcedures3 <-JointProcedures3[-WhichInstances2,]
}
fixes the issue. But it should not have in principle made a scooby of a difference if that conditional was there or not, since if length(WhichInstances2) was equal to 0, I would simply be subtract nothing from the original JointProcedures3...
Thanks all for your input.
Let's try a simpler example to see what's happening.
x <- 1:5
y <- LETTERS[1:5]
which(x>4)
## [1] 5
y[which(x>4)]
## [1] "E"
So far so good ...
which(x>5)
## integer(0)
> y[which(x>5)]
## character(0)
This is also fine. Now what if we negate? The problem is that integer(0) is a zero-length vector, so -integer(0) is also a zero-length vector, so y[-which(x>5] is also a zero-length vector ..
What can you do about it? Don't use which(); instead use logical indexing directly, and use ! to negate the condition:
y[!(x>5)]
## [1] "A" "B" "C" "D" "E"
In your case:
JointID_OK <- (JointProcedures3$JointID %in% KneeIDcount$Var1[KneeIDcount$Freq >1])
JointProcedures3 <-JointProcedures3[!JointID_OK,]
For what it's worth, this is section 8.1.13 in the R Inferno, "negative nothing is something"
It seems you are checking for ids in a vector and you intend to remove them from another; probably setdiff is what you are looking for.
Consider if we have a vector of the lowercase letters of the alphabet (its an r builtin) and we want to remove any entry that matches something that is not in there ("ab") , as programmers we would wish for nothing to be removed and keep our 26 letters
# wont work
letters[ - which(letters=="ab")]
#works
setdiff(letters , which(letters=="ab"))
[1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u"
[22] "v" "w" "x" "y" "z"

Change the null values ​of multiple lists that only differ by a number

I want to change the null values ​​of multiple lists that only differ by a number. In this example I have 3 lists: "a1", "a2" and "a3", and I want to change their null values for "THERE'S NO VALUE". I've tried with a for loop using "paste" function, but it doesn't run. This is a simplied version of my code:
a1<-list(NULL, "a","b")
a2<-list("d", NULL,"m")
a3<-list("k", NULL,"l")
for (i in 1:3){
var<-paste("a", i, sep = "")
var[var=='NULL']<-"THERE'S NO VALUE"
}
Also I've tried with assign function, but It changes all variables, and I only want to change the null element of each one (I suspect why, but I don't know how to change the function to work):
for (i in 1:3){
var<-paste("a", i, sep = "")
assign(var,var[var=='NULL']<-"THERE'S NO VALUE")
}
Thanks in advance.
We use mget to get the objects in a list, then loop over the list with lapply, replace the elements that are NULL with the new value and then if needed, use list2env to change the object values in the global env
list2env(lapply(mget(paste0("a", 1:3)), function(x) {
x[sapply(x, is.null)] <- "THERE'S NO VALUE"
x}),
.GlobalEnv)
-Now check the objects
a1
[[1]]
[1] "THERE'S NO VALUE"
[[2]]
[1] "a"
[[3]]
[1] "b"
a2
[[1]]
[1] "d"
[[2]]
[1] "THERE'S NO VALUE"
[[3]]
[1] "m"
a3
[[1]]
[1] "k"
[[2]]
[1] "THERE'S NO VALUE"
[[3]]
[1] "l"

R: Check if strings in a vector are present in other vectors, and return name of the match

I need a tool more selective than %in% or match(). I need a code that matches a vector of string with another vector, and that returns the names of the matches.
Currently I have the following,
test <- c("country_A", "country_B", "country_C", "country_D", "country_E", "country_F") rating_3 <- c("country_B", "country_D", "country_G", "country_K")
rating_3 <- c("country_B", "country_D", "country_G", "country_K")
rating_4 <- c("country_C", "country_E", "country_M", "country_F)
i <- 1
while (i <= 33) {
print(i)
print(test[[i]])
if (grepl(test[[i]], rating_3) == TRUE) {
print(grepl(test[[i]], rating_3)) }
i <- i+1
},
This should check each element of test present in rating_3, but for some reason, it returns only the position, the name of the string, and a warning;
[1]
[country_A]
There were 6 warnings (use warnings() to see them)
I need to know what this piece of code fails, but I'd like to eventually have it return the name only when it's inside another vector, and if possible, testing it against several vectors at once, having it print the name of the vector in which it fits, something like
[1]
[String]
[rating_3]
How could I get something like that?
Without a reproducible example, it is hard to determine what exactly you need, but I think this could be done using %in%:
# create reprex
test <- sample(letters,10)
rating_3 <- sample(letters, 20)
print(rating_3[rating_3 %in% test])
[1] "r" "z" "l" "e" "m" "c" "p" "t" "f" "x" "n" "h" "b" "o" "s" "v" "k" "w" "a"
[20] "i"

How to create multiple columns filled with 0 in an xts object in R?

I would like to create multiple columns filled with zeroes in one xts object. Manually I can use this code :
> class(data)
[1] "xts" "zoo"
> colnames(data)
[1] "A" "B"
> data$C <- 0
> colnames(data)
[1] "A" "B" "C"
But unfortunately when in a for loop the i is interpreted as an object name instead of a variable.
> symbols
[1] "D" "E" "F"
for (i in symbols) {
data$i <- 0
}
> colnames(data)
> [1] "A" "B" "C" "i"
When I use [[, the programmatic equivalent of $ then colnames(data) returns NULL.
Finally I try with the apply family of functions like below but it doesn't work as expected.
> sapply(symbols, function(i) {data$i <- 0})
D E F
0 0 0
What could be the best solution to do this ?
Thanks in advance
I recommend creating a new xts object with the desired column names and values, then merge that with the original data.
require(xts)
data <- xts(cbind(A=1:5,B=5:1), Sys.Date()-5:1)
symbols <- LETTERS[3:6]
zeros <- xts(matrix(0,nrow(data),length(symbols)),
index(data), dimnames=list(NULL,symbols))
data <- merge(data, zeros)
That makes what is being done explicit, and therefore less confusing for future you. :)
You can try:
do.call(cbind,setNames(c(list(data),rep(list(0),length(symbols))),
c("",symbols)‌​))

Can't access vector elements in a for loop

Let's say I have two vectors:
a1=c("a","b")
a2=c("x","y")
Now in a 'for' loop, I want to access the first element of each vector:
for(i in c(a1,a2)) {
print(i[1])
}
If I run the above code, I get:
[1] "a"
[1] "b"
[1] "x"
[1] "y"
But I just want:
[1] "a"
[1] "x"
More surprisingly, if I want to access the second element:
for(i in c(a1,a2)) {
print(i[2])
}
I get:
[1] "NA"
[1] "NA"
[1] "NA"
[1] "NA"
Any help will be highly appreciated.
Because c(a1, a2) = c("a","b","x","y") -- passing multiple atomic vectors to c causes them to get collapsed. Use list(a1, a2) in the loop instead.

Resources