How to use NA in if..else if statement in R - r

I created this small example. I want to print some values, for example, B for NA values using the if else statement.
x = c(1,7,NA, 3, NA, NA)
for(i in 1: length(x)){
y = x[i]
if(y == 1){
print("A")
}
else if(y == 'NA'){
print("B")
}
else{
print("C")
}
}
I am getting an error message Error in if (y == 1) { : missing value where TRUE/FALSE needed Why can't I print B for NA values? How to use NA within the if else statement?

The issue is also that == with NA returns NA and not FALSE. Make sure to add a condition to check NA as well. Also, y == 'NA' should be replaced with is.na(y)
for(i in 1:length(x)){
y = x[i]
if(y == 1 & !is.na(y)){
print("A")
}
else if(is.na(y)){
print("B")
}
else{
print("C")
}
}
-output
[1] "A"
[1] "C"
[1] "B"
[1] "C"
[1] "B"
[1] "B"
Or this can be done in a vectorized way
c("C", "B", "A")[1 + is.na(x) + 2 *(x %in% 1)]
#[1] "A" "C" "B" "C" "B" "B"

To avoid repetition, ensure that the first block checks for NA:
x = c(1,7,NA, 3, NA, NA)
for(i in 1: length(x)){
y = x[i]
if(is.na(y)){
print("B")
}
else if(y == 1){
print("A")
}
else{
print("C")
}
}
[1] "A"
[1] "C"
[1] "B"
[1] "C"
[1] "B"
[1] "B"

You can use vectorized way using case_when or nested ifelse -
dplyr::case_when(is.na(x) ~ 'B',
x == 1 ~ 'A',
TRUE ~ 'C')
#[1] "A" "C" "B" "C" "B" "B"

Related

R - subscript out of bounds on if statement

I have a ncol=10, nrow=343 matrix "E" containing letters "a", "b",..,or "f" standing for technologies, where "a" stands for gas. Whenever [row, col]="a" and [row+1,col] !="a" I add resale values for "a" in [row,col] from s_house_gas dim(ncol=10,nrow=10) where the rows are the years like in E. Additionally, resale values for "a" shall be added in E[5,] to account for a resale in the last year.
The code below works, however, I want to add the condition that resale values shall not be added when [row+1] =="d" or "e" but I cannot seem to make it work.
Maybe I have looked at it for too long but I would really appreciate your support here, I feel like I am very close.
HV <- rep.int(0, ncol(E))
R<- rbind(E,HV)
i <- 0
for(col in 1:ncol(R)){
for(row in 1:nrow(R)){
if(R[row,col] == "a" && row <= 10){
i = i+1
R[row,col] = i
} else if (R[row,col] != "a" || row == 11){
i = 0
}
}
}
R <- R[-11,]
Y <- R
for(row in 1:nrow(Y)){
for(col in 1:ncol(Y)){
if(Y[row,col] == "a" || Y[row,col] == "b"|| Y[row,col] == "c" || Y[row,col] == "d"|| Y[row,col] == "e"|| Y[row,col] == "f"){
Y[row,col] = 0
}
}
}
Y <- apply(Y, 2,as.numeric)
L <- rbind(HV,Y,HV)
M <- matrix(0L, nrow = dim(L)[1], ncol = dim(L)[2])
for(col in 1:ncol(L)){
for (row in 2:nrow(L)) {
if(L[row,col] == 0 && L[row-1,col] != 0) {
M[row,col] = row-1-L[row-1,col]
}
}
}
I <- M
N <- matrix(0, nrow = dim(I)[1], ncol = dim(I)[2])
i <- 0
j <- 0
for(row in 2:nrow(I)){
for(col in 1:ncol(I)){
if(I[row,col] != 0){
i = L[row-1,col]
j = M[row,col]
N[row-1,col] = s_house_gas[i, j]
}
}
}
resale_gas <- N[c(-1,-12),]
resale_gas <- matrix(resale_gas, ncol=343, nrow=10)
EDIT
library(xts)
library(stringi)
library(gtools)
t <- c("a","b","c","d","e","f")
E <- t(permutations(6,5, v=t,repeats.allowed=T))
s_house_gas <- 15:11
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
[1,] "a" "a" "a" "a" "a" "a" "a" "a"
[2,] "a" "a" "a" "a" "a" "a" "a" "a"
[3,] "a" "a" "a" "a" "a" "a" "a" "a"
[4,] "a" "a" "a" "a" "a" "a" "b" "b"
[5,] "a" "b" "c" "d" "e" "f" "a" "b"
This is what I tried:
HV <- rep.int(0, ncol(E))
R<- rbind(E,HV)
i <- 0
for(col in 1:ncol(R)){
for(row in 1:(nrow(R)-1)){
if(R[row,col] == "a" && R[row+1,col] != "d" && row <= 10 || R[row,col] == "a" && R[row+1,col] != "e" && row <= 10){
i = i+1
R[row,col] = i
} else if (R[row,col] != "a" || row == 11){
i = 0
}
}
}
R <- R[-11,]
Y <- R
for(row in 1:nrow(Y)){
for(col in 1:ncol(Y)){
if(Y[row,col] == "a" || Y[row,col] == "b"|| Y[row,col] == "c" || Y[row,col] == "d"|| Y[row,col] == "e"|| Y[row,col] == "f"){
Y[row,col] = 0
}
}
}
Y <- apply(Y, 2,as.numeric)
L <- rbind(HV,Y,HV)
M <- matrix(0L, nrow = dim(L)[1], ncol = dim(L)[2])
for(col in 1:ncol(L)){
for (row in 2:nrow(L)) {
if(L[row,col] == 0 && L[row-1,col] != 0) {
M[row,col] = row-1-L[row-1,col]
}
}
}
I <- M
N <- matrix(0, nrow = dim(I)[1], ncol = dim(I)[2])
i <- 0
j <- 0
for(row in 2:nrow(I)){
for(col in 1:ncol(I)){
if(I[row,col] != 0){
i = L[row-1,col]
j = M[row,col]
N[row-1,col] = s_house_gas[i, j]
}
}
}
resale_gas <- N[c(-1,-12),]
resale_gas <- matrix(resale_gas, ncol=343, nrow=10)

While look results not getting printed

could anyone help me while results are not getting displayed here below
col_name <- c("A", "B", "C", "D")
i <- 1
while (i < length(col_name)) {
col_name[i]
i = i+1
}
Expected output
"A"
"B"
"C"
It needs a print
while (i < length(col_name)) {
print(col_name[i])
i = i+1
}
#[1] "A"
#[1] "B"
#[1] "C"
If we need to store the output, initialize an object and update
out <- c()
i <- 1
while (i < length(col_name)) { out <- c(out, col_name[i]); i = i+1}
out
#[1] "A" "B" "C"

How to print outputs from a loop on one line?

I have to make a grade calculator in r which can convert numerical grades into letter grades. Here is the code I came up with:
numGrades<-(c(66,02,99,59,82))
for(i in 1:length(numGrades)) {
if (numGrades[i]>=90){
print("A")
} else if (numGrades[i]>=80){
print("B")
} else if (numGrades[i]>=70){
print("C")
} else if (numGrades[i]>=60){
print("D")
} else {
print("F")}
}
I can't find a way to integrate the cat or print(c()) functions so that it prints on one line rather than getting:
[1] "D"`
[1] "F"`
[1] "A"
[1] "F"
[1] "B"
If anyone has any ideas it would be greatly appreciated!
I would simply use paste to join all elements of a 'graded' list. Hope this helps.
numGrades = graded = (c(66,02,99,59,82))
for(i in 1:length(numGrades)) {
if (numGrades[i]>=90){
graded[i] = "A"
} else if (numGrades[i]>=80){
graded[i] = "B"
} else if (numGrades[i]>=70){
graded[i] = "C"
} else if (numGrades[i]>=60){
graded[i] = "E"
} else {
graded[i] = "F"}
}
print(paste(graded))
This gives:
> print(paste(graded))
[1] "E" "F" "A" "F" "B"
why the cat is not working?
numGrades<-(c(66,02,99,59,82))
for(i in 1:length(numGrades)) {
if (numGrades[i]>=90){
cat("A ")
} else if (numGrades[i]>=80){
cat("B ")
} else if (numGrades[i]>=70){
cat("C ")
} else if (numGrades[i]>=60){
cat("D ")
} else {
cat("F ")}
}
With many tasks in R it’s better to do this using vectorised functions rather than loops. Here’s two ways of doing what you want, one using base R and the other dplyr::case_when. Note that cut returns a factor but you can always use as.character.
numGrades <- c(66,02,99,59,82)
letGrades <- cut(
numGrades,
breaks = c(-Inf, 6:9, Inf) * 10,
labels = LETTERS[c(6, 4:1)],
right = FALSE
)
letGrades
library(dplyr)
letGrades <- case_when(
numGrades >= 90 ~ "A",
numGrades >= 80 ~ "B",
numGrades >= 70 ~ "C",
numGrades >= 60 ~ "D",
TRUE ~ "F"
)
letGrades
Just for the record, there's no need to use a for loop, you can use a nested ifelse
> graded2 <- ifelse(numGrades>=90, "A",
ifelse(numGrades >= 80 & numGrades < 90, "B",
ifelse(numGrades >= 70 & numGrades < 80, "C",
ifelse(numGrades >= 60 & numGrades < 70, "E", "F"))))
> graded2
[1] "E" "F" "A" "F" "B"

return value of ifelse from another vector via index

I can't get my head around this problem regarding ifelse:
Say I have two vectors:
x <- c(0, 1:4, 1:4)
y <- letters[1:3]
When I do
ifelse(x==2, y[x], x)
I get
"0" "1" "c" "3" "4" "1" "c" "3" "4"
However, it should return "b" at position 2 of vector y.
Why is ifelse doing that?
To explain this strange behaviour the source code of ifelse is helpful (see below).
As soon as you call ifelse the expressions passed as the arguments test, yes and no are evaluated resulting in:
Browse[2]> test
[1] FALSE FALSE TRUE FALSE FALSE FALSE TRUE FALSE FALSE
Browse[2]> yes
[1] "a" "b" "c" NA "a" "b" "c" NA
Browse[2]> no
[1] 0 1 2 3 4 1 2 3 4
Observe that y[x] uses the values of x to pick values from y
and the value 0 is empty (= ignored) , values above 3 are NA,
that is why the `yes´ argument becomes
[1] "a" "b" "c" NA "a" "b" "c" NA
The code line
ans[test & ok] <- rep(yes, length.out = length(ans))[test & ok]
is then applied at the end and effectivly does update all TRUE-elements using the test logical vector:
yes[test]
which results in:
[1] "c" "c"
being stored in the result indices 3 and 7
ans[test & ok]
So the problem is using y[x] as second argument to ifelse + the non-intuitive ifelse behaviour to use a logical index to pick the "TRUE"-results from y[x]...
Lesson learned: Avoid complicated ifelse logic, it has lot of side effects (eg. you may loose the correct data type or attributes).
# ifelse function
function (test, yes, no)
{
if (is.atomic(test)) {
if (typeof(test) != "logical")
storage.mode(test) <- "logical"
if (length(test) == 1 && is.null(attributes(test))) {
if (is.na(test))
return(NA)
else if (test) {
if (length(yes) == 1) {
yat <- attributes(yes)
if (is.null(yat) || (is.function(yes) && identical(names(yat),
"srcref")))
return(yes)
}
}
else if (length(no) == 1) {
nat <- attributes(no)
if (is.null(nat) || (is.function(no) && identical(names(nat),
"srcref")))
return(no)
}
}
}
else test <- if (isS4(test))
methods::as(test, "logical")
else as.logical(test)
ans <- test
ok <- !(nas <- is.na(test))
if (any(test[ok]))
ans[test & ok] <- rep(yes, length.out = length(ans))[test &
ok]
if (any(!test[ok]))
ans[!test & ok] <- rep(no, length.out = length(ans))[!test &
ok]
ans[nas] <- NA
ans
}
You are using 0 as an index in the first element so that is why the alignment is messed up.
y[x]
[1] "a" "b" "c" NA "a" "b" "c" NA
So
> y[0]
character(0)
> y[1]
[1] "a"
> y[2]
[1] "b"
> y[3]
[1] "c"
So the length of y[x] is different than the length of x.
What you want is
> ifelse(x==2, y[x+1], x)
[1] "0" "1" "c" "3" "4" "1" "c" "3" "4"
but only if the first element is always 0.
Old answer
Because
x <- c(0, 1:4, 1:4)
returns
[1] 0 1 2 3 4 1 2 3 4
so x==2
returns
1] FALSE FALSE TRUE FALSE FALSE FALSE TRUE FALSE FALSE
so for y = letters[1:3]
ifelse(x==2, y[x], x)
You are going to get the letters in the third and seventh positions.
The documentation for ifelse says that if one vector is too short it will be recycled which you would expect to be
c("a","b","c","a","b","c","a").
However when I try
ifelse(x==3, y[x], x)
I get
[1] "0" "1" "2" NA "4" "1" "2" NA "4"
Which tells me that the recycling is not working the way I would expect.
So that's the nominal reason you are getting the result. The reason it works like that is something I don't know now, but if I figure it out I will add to this answer. I suspect it has to do with the conversion to a string.
Just looking at y[x] I get
[1] "a" "b" "c" NA "a" "b" "c" NA
Which, by the way is only length 8 even though x is length 9.
So this really doesn't have to do with ifelse() at all, it is really about a different issue with recycling.
From Comment: It returns c because: which(x==2) returns 3 and 7. I don't know why it doesn't recycle 7 but chooses only 3. Perhaps because y is less than length 7
Try:
ind<-which(x==2)
ind1<-ind[1]-1
ifelse(x==2,y[ind1],x)
[1] "0" "1" "b" "3" "4" "1" "b" "3" "4"
Here's an attempt to make a function:
dynamic_index<-function(ind,x,y){
x<-x
y<-y
ind1<-which(x==ind)
ind2<-ind1[1]-1
ifelse(x==ind,y[ind2],x)
}
dynamic_index(2,x,y)
The result occurs lat way because the == function returns a vector of logicals:
x <- c(0, 1:4, 1:4)
y <- letters[1:3]
ifelse(x==2, y[x], x)
#look at x==2
x==2
[1] FALSE FALSE TRUE FALSE FALSE FALSE TRUE FALSE FALSE
It's a logical vector that has true in the third position, not the second so it is the third value of y that is selected. This also shows why the answer that references the behavior of which is incorrect.
x <- c(0, 1:4, 1:4)
y <- letters[1:3]
ifelse(x==2, y[x], x)
in ifelse it will check each position in x .if it is true then it will print y[x] position it means the position which was checked in x and that position of value in Y will be printed .it will check all the values in X

Assign vector in indexing another vector

Here's a little code to illustrate my problem:
x <- 1:10
# > x
# [1] 1 2 3 4 5 6 7 8 9 10
y <- rep(letters[1:2], 5)
# > y
# [1] "a" "b" "a" "b" "a" "b" "a" "b" "a" "b"
z <- rep(c(5,4), 5)
# > z
# [1] 5 4 5 4 5 4 5 4 5 4
Now, depending in which order I issue the next two commands I get different subassignments:
x first, y second:
x[(x == 2) & (y != "a") & (z == 4)] <- "a"
# > x
# [1] "1" "a" "3" "4" "5" "6" "7" "8" "9" "10"
y[(x == 2) & (y != "a") & (z == 4)] <- "a"
# > y
# [1] "a" "b" "a" "b" "a" "b" "a" "b" "a" "b"
y first, x second:
y[(x == 2) & (y != "a") & (z == 4)] <- "a"
# > y
# [1] "a" "a" "a" "b" "a" "b" "a" "b" "a" "b"
x[(x == 2) & (y != "a") & (z == 4)] <- "a"
# > x
# [1] "1" "2" "3" "4" "5" "6" "7" "8" "9" "10"
The assignment of the second vector depends on the assignment done in the previous vector. Hence, in the second assignment I need to make sure that I have the relevant indices still available for the second assigment. My first idea is:
x[ind <- ((x == 2) & (y != "a") & (z == 4))] <- "a"
y[ind] <- "a"
rm(ind)
I want to avoid a separate call to do the assignment of the ind vector given that I might be doing a lot of this. Would that still be considered good coding in R or can it lead to any devious behaviour I haven't thought of?
Your solution seems fine. However, I would still regard your code as somewhat bad practice. Consider your first bullet:
x[(x == 2) & (y != "a") & (z == 4)] <- "a"
y[(x == 2) & (y != "a") & (z == 4)] <- "a"
At line 1, your numeric variable x is converted to a character since you assign "a" to the TRUE indices or maybe not if no indices are TRUE. Hence your output type is not really clear. That's somewhat bad practice and can lead to all sorts or problems downstream. You should stay within on type.
This also means that the x == 2 in your second line in the above is somewhat unclear though R correctly interprets the comparison. Again however, it could cause problems in a more elaborate example. But maybe you don't have these type issues in your application.

Resources