Vectorized if-else for multiple block commands

I am looking for a way to perform multiple variable assignments based on a single conditional statement. The ifelse function does what I want for one variable at a time, but I would like to be able to execute a block of statements based on one condition.

Here is a little simplified code example:

within(mydata, { if (gender == "f") { test1 <- 1 test2 <- 2 } else { test1 <- 0 test2 <- 0 } test3 <- gender test4 <- ifelse(gender == "f", 1, 0) test5 <- ifelse(gender == "f", 2, 0) }) 

Which gives the following conclusion:

  workshop gender q1 q2 q3 q4 test5 test4 test3 test2 test1 1 1 f 1 1 5 1 2 1 f 2 1 2 2 f 2 1 4 1 2 1 f 2 1 3 1 f 2 2 4 3 2 1 f 2 1 4 2 f 3 1 NA 3 2 1 f 2 1 5 1 m 4 5 2 4 0 0 m 2 1 6 2 m 5 4 5 5 0 0 m 2 1 7 1 m 5 3 4 4 0 0 m 2 1 8 2 m 4 5 5 5 0 0 m 2 1 Warning message: In if (gender == "f") { : the condition has length > 1 and only the first element will be used 

When I run this code, test4 and test5 are correctly assigned, but test1 and test2 are incorrectly assigned, because the if statement only returns the value for the first line. Is there a way to do what I'm trying to do with test1 and test2 - run multiple statements for each row of the data frame based on one condition?

I know that I can accomplish the same result with ifelse, but I would like to be able to group statements together for clarity when reading my code.

For example, I would like to be able to group the savings calculations that I do at least as follows:

 a.lighting.all.3 <- within(a.lighting.all.3, { if (measure.subcategory %in% c('HID to Linear Fluorescent Retrofit', 'Hardwired CFL', 'Induction Lighting', 'Screw-In CFL', 'Specialty Screw-In CFL', 'T12 to Premium T8/T5', 'T12 to Standard T8/T5', 'T8 to Premium T8', 'T12/T8 Delamping')) { kw.nc.v <- (base.watts - ee.watts) / 1000 * (1 + dif) * df * quantity kwh.v <- (base.watts - ee.watts) / 1000 * (1 + eif) * op.hrs * quantity } else if (measure.subcategory == 'Traffic Signals') { kw.nc.v <- (base.watts - ee.watts) / 1000 * quantity kwh.v <- (base.watts - ee.watts) / 1000 * op.hrs * quantity } else if (measure.subcategory == 'Exit Sign Retrofit') { } else if (measure.subcategory %in% c('LED Channel Lights', 'Cold Cathode FL')) { } else if (measure.subcategory %in% c('Daylighting Controls', 'Occupancy Sensors')) { } else if (measure.subcategory == 'Lighting Power Density') { } else if (measure.subcategory == 'LED Lighting') { } }) 

Or assign parameter sets by measure, for example:

 a.lighting.all.3 <- within(a.lighting.all.3, { switch(as.character(measure.subcategory), "T8 to Premium T8" = { op.hrs <- 4481 cf <- 0.93 }, "Cold Cathode FL" = { op.hrs <- 6400 cf <- 1 }, "Exit Sign Retrofit" = { op.hrs <- 8760 cf <- 1 }, "LED Channel Lights" = { op.hrs <- 5110 cf <- 0.134 }, "Traffic Signals" = { op.hrs <- ifelse(grepl("Green", measure), 3679, 4818) df <- ifelse(grepl("Green", measure), 0.42, 0.55) cf <- 1 }, "Daylighting Controls" = { dsf <- esf <- 0.54 # daylight savings fraction }, "Occupancy Sensors" = { dsf <- 0.16 # demand savings fraction esf <- 0.39 # energy savings fraction }, "LED Lighting" = { if (measure %in% c("Pedestrian NO countdown", "Pedestrian W/ countdown")) { cf <- 1 op.hrs <- ifelse(measure == "Pedestrian W/ countdown", 6483, 5432) op.hrs.base <- 5432 df <- ifelse(measure == "Pedestrian W/ countdown", 0.74, 0.62) df.base <- 0.62 } else if (measure %in% c("Refrigerated Case LED Lamps NO motion Sensors", "Refrigerated Case LED Lamps W/ motion Sensors")) { cf <- 1 dif <- 0.25 eif <- 0.25 op.hrs.base <- 8634 op.hrs <- ifelse(measure == "Refrigerated Case LED Lamps W/ motion Sensors", 6043, 8634) } } ) }) 

Any ideas?

+4
source share
2 answers
 d <- data.frame(workshop=rep(1:2,4), gender=rep(c("f","m"),each=4)) 

I don’t know if this answer pleases you or not, but: if you use plyr to work with blocks of code, you can do what you want with the if .

 library(plyr) ddply(d,"gender", function(x) { within(x, { test3 <- gender ## test FIRST value only, since by construction they ## are all the same within a piece if (gender[1]=="f") { test1 <- 1 test2 <- 2 test4 <- 1 test5 <- 1 } else { test1 <- test2 <- test4 <- test5 <- 0 }}) }) 

Please note that this will cause your data to change into blocks by gender (in this example, it does not change anything), which may be undesirable ...

I did not include additional variables in my example, but they will be correctly ported.

+2
source

Data.table solution for time and memory efficiency and coding elegance

 library(data.table) DT <- as.data.table(d) DT[, `:=`(paste0('test',1:5), list((1:0)[gender], (c(2,0))[gender], gender, (1:0)[gender], (1:0)[gender])), with = F] 

:= will be assigned by reference and can work for several columns if the LHS argument is a character vector of names (which should be created), and RHS is a list with the values ​​to be used.

This solution also exploits the fact that gender is a factor variable, and we can use basic integer values ​​to refer to the transcoding.

you can also do something like

 setkey(|Dt, gender) DT['f', test1 := 1] DT['m', test1 := 0] DT['f', test2 := 2] DT['m', test2 := 0] DT[,test3 := gender] # etc 

This will give warnings if gender is not a character factor, but will work anyway.

+2
source

Source: https://habr.com/ru/post/1436232/


All Articles