Conditional Assignment of One Variable to the Value of One of Two Other Variables

Conditional assignment of one variable to the value of one of two other variables

Your original method of assignment is failing for at least two reasons.

1) A problem with the subscripted assignment df$major[df$degree1 == "BA"] <-. Using == can produce NA, which is what prompted the error. From ?"[<-": "When replacing (that is using indexing on the lhs of an assignment) NA does not select any element to be replaced. As there is ambiguity as to whether an element of the rhs should be used or not, this is only allowed if the rhs value is of length one (so the two interpretations would have the same outcome)." There are many ways to get around this, but I prefer using which:

df$major[which(df$degree1 == "BA")] <-

The difference is that == returns TRUE, FALSE and NA, while which returns the indices of an object that are TRUE

> df$degree1 == "BA"
[1] FALSE NA TRUE TRUE TRUE FALSE FALSE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE

> which(df$degree1 == "BA")
[1] 3 4 5 8 9 10 11 12 13 14 15 16 17 18 19 20

2) When you perform a subscripted assignment, the right hand side needs to fit into the left hand side sensibly (this is the way I think of it). This can mean left and right hand sides of equal length, which is what your example seems to imply. Therefore, you would need to subset the right hand side of the assignment as well:

df$major[which(df$degree1 == "BA")] <- df$subj1[which(df$degree1 == "BA")]

I hope that clarifies why your original attempt produced an error.

Using ifelse, as suggested by @DavidRobinson, is a good way of doing this type of assignment. My take on it:

df$major2 <- ifelse(df$degree1 == "BA", df$subj1, ifelse(df$degree2 == "BA",
df$subj2,NA))

This is equivalent to

df$major[which(df$degree1 == "BA")] <- df$subj1[which(df$degree1 == "BA")]
df$major[which(df$degree1 != "BA" & df$degree2 == "BA")] <-
df$subj2[which(df$degree1 != "BA" & df$degree2 == "BA")]

Depending on the depth of the nested ifelse statements, another approach might be better for your real data.


EDIT:

I was going to write a third reason for the original code failing (namely that df$major wasn't yet assigned), but it works for me without having to do that. This was a problem I remember having in the past, though. What version of R are you running? (2.15.0 for me.) This step is not necessary if you use the ifelse() approach. Your solution is fine when using [, although I would have chosen

df$major <- NA

To get the character values of the subjects, instead of the factor level index, use as.character() (which for factors is equivalent to and calls levels(x)[x]):

df$major[which(df$degree1 == "BA")] <- as.character(df$subj1)[which(df$degree1 == "BA")]
df$major[which(df$degree1 != "BA" & df$degree2 == "BA")] <-
as.character(df$subj2)[which(df$degree1 != "BA" & df$degree2 == "BA")]

Same for the ifelse() way:

df$major2 <- ifelse(df$degree1 == "BA", as.character(df$subj1),
ifelse(df$degree2 == "BA", as.character(df$subj2), NA))

Conditional operator to initalize one of two variables

Just assign the initial/other value based on the condition:

int x = myCond ? 13 : 0;
int y = !myCond ? 13 : 0;

You have to definitely assign the variables in order to use them at all (if they are local variables).


I suppose you could do it like:

int unused = myCond ? (x = 13) : (y = 13);

but that's an abuse of syntax, because you have to introduce that unused variable in order to make a statement out of the conditional expression.

One-line multiple variable value assignment with an if condition

You can acheive this by putting (a, b) in parentheses.

a, b = (1, 1) if c == 1 else (5, 10)

The current code is equivalent to

a, b = 1, (1 if c == 1 else 5), 10

Which gives a value error as you are trying to unpack a 3-tuple into two variables.

Assigning values inside conditional operator

No, using an assignment as an assignment is rarely a good idea - code is much easier to read and understand when conditions only test conditions, rather than when those conditions also have side-effects. In this case, you can fix it putting 2 and 0 as the expressions on the right:

const b = a === 1 ? 2 : 0;

The only time I think an assignment inside a conditional might possibly look cleaner than the alternative is when iterating over a global regular expression manually to extract matched groups (this is not using the conditional operator, but the principle is similar):

const regex = /\w(?=(\w))/g;const str = 'foo';
let match;while (match = regex.exec(str)) { console.log(match[1]);}

Setting a value in a variable to NA, conditional on another variable

Here's an attempt that is hopefully not too complex. If you set up the vars you want to loop over, and the corresponding values you want to be selected for indexing, you can do:

vars   <- c("mpg", "disp", "cyl", "hp")
values <- c(0, 0, 1, 1)

ex[vars] <- Map(function(x,y) replace(x, ex$vs == y, NA), ex[vars], vals)

# mpg cyl disp hp drat wt qsec vs am gear carb
#Mazda RX4 NA 6 NA 110 3.90 2.620 16.46 0 1 4 4
#Mazda RX4 Wag NA 6 NA 110 3.90 2.875 17.02 0 1 4 4
#Datsun 710 22.8 NA 108.0 NA 3.85 2.320 18.61 1 1 4 1
#Hornet 4 Drive 21.4 NA 258.0 NA 3.08 3.215 19.44 1 0 3 1
#Hornet Sportabout NA 8 NA 175 3.15 3.440 17.02 0 0 3 2
#Valiant 18.1 NA 225.0 NA 2.76 3.460 20.22 1 0 3 1
# ...

If you've only got two groups, you could do this simpler via a couple of assignments as @HubertL and @Phil mentioned in the comments, but using Map allows you consider many variables with many possible index values, without ever extending past 3 lines of code.



Related Topics



Leave a reply



Submit