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
Replace Na with Groups Mean in a Non Specified Number of Columns
Given a Set of Random Numbers Drawn from a Continuous Univariate Distribution, Find the Distribution
Can't Load X11 in R After Os X Yosemite Upgrade
R: Losing Column Names When Adding Rows to an Empty Data Frame
Does Roxygen2 Automatically Write Namespace Directives for "Imports:" Packages
Random Forest with Classes That Are Very Unbalanced
Unnest a List Column Directly into Several Columns
Extract Knots, Basis, Coefficients and Predictions for P-Splines in Adaptive Smooth
How to Change Font Family in a Legend in an R-Plot
Replace All Na with False in Selected Columns in R
Get All the Rows with Rownames Starting with Abc111
Can't Change Fonts in Ggplot/Geom_Text
Remove Empty Elements from List with Character(0)
Rescaling the Y Axis in Bar Plot Causes Bars to Disappear:R Ggplot2
R List Files with Multiple Conditions
Using Lapply to Change Column Names of a List of Data Frames
How to Display Verbatim Inline R Code with Backticks Using Rmarkdown