Smon
Smon

Reputation: 55

R apply function pass multiple elements from row as arguments

I finally want to master the apply functions in R. Unfortunately it seems I'm too dumb to understand the common examples, which are often not complex enough to represent my use case. This is my example:

I want to replace the car name in mtcars with an attribute, e.g. "speedy car" or "scrap truck", based on a complex function:

mtcars$name <- rownames(mtcars)
    
hotornot <- function(horsepower, cylinders, name)
{
  if(horsepower < 200 && cylinders < 5 && name != "Merc 240D")       
        return("scrap truck")
  
  return("speedy car")
}

Now how do I manage to pass the corresponding values from mtcars$cyl, mtcars$hp and mtcars$name to my function hotornot() and write the return value in the mtcars$name column?

Upvotes: 0

Views: 55

Answers (3)

G. Grothendieck
G. Grothendieck

Reputation: 269624

Use mapply as follows:

transform(mtcars, name = mapply(hotornot, hp, cyl, name))

giving:

                     mpg cyl  disp  hp drat    wt  qsec vs am gear carb        name
Mazda RX4           21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4  speedy car
Mazda RX4 Wag       21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4  speedy car
Datsun 710          22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1 scrap truck
Hornet 4 Drive      21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1  speedy car
Hornet Sportabout   18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2  speedy car
Valiant             18.1   6 225.0 105 2.76 3.460 20.22  1  0    3    1  speedy car
Duster 360          14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4  speedy car
Merc 240D           24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2  speedy car
Merc 230            22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2 scrap truck
Merc 280            19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4  speedy car
Merc 280C           17.8   6 167.6 123 3.92 3.440 18.90  1  0    4    4  speedy car
Merc 450SE          16.4   8 275.8 180 3.07 4.070 17.40  0  0    3    3  speedy car
Merc 450SL          17.3   8 275.8 180 3.07 3.730 17.60  0  0    3    3  speedy car
Merc 450SLC         15.2   8 275.8 180 3.07 3.780 18.00  0  0    3    3  speedy car
Cadillac Fleetwood  10.4   8 472.0 205 2.93 5.250 17.98  0  0    3    4  speedy car
Lincoln Continental 10.4   8 460.0 215 3.00 5.424 17.82  0  0    3    4  speedy car
Chrysler Imperial   14.7   8 440.0 230 3.23 5.345 17.42  0  0    3    4  speedy car
Fiat 128            32.4   4  78.7  66 4.08 2.200 19.47  1  1    4    1 scrap truck
Honda Civic         30.4   4  75.7  52 4.93 1.615 18.52  1  1    4    2 scrap truck
Toyota Corolla      33.9   4  71.1  65 4.22 1.835 19.90  1  1    4    1 scrap truck
Toyota Corona       21.5   4 120.1  97 3.70 2.465 20.01  1  0    3    1 scrap truck
Dodge Challenger    15.5   8 318.0 150 2.76 3.520 16.87  0  0    3    2  speedy car
AMC Javelin         15.2   8 304.0 150 3.15 3.435 17.30  0  0    3    2  speedy car
Camaro Z28          13.3   8 350.0 245 3.73 3.840 15.41  0  0    3    4  speedy car
Pontiac Firebird    19.2   8 400.0 175 3.08 3.845 17.05  0  0    3    2  speedy car
Fiat X1-9           27.3   4  79.0  66 4.08 1.935 18.90  1  1    4    1 scrap truck
Porsche 914-2       26.0   4 120.3  91 4.43 2.140 16.70  0  1    5    2 scrap truck
Lotus Europa        30.4   4  95.1 113 3.77 1.513 16.90  1  1    5    2 scrap truck
Ford Pantera L      15.8   8 351.0 264 4.22 3.170 14.50  0  1    5    4  speedy car
Ferrari Dino        19.7   6 145.0 175 3.62 2.770 15.50  0  1    5    6  speedy car
Maserati Bora       15.0   8 301.0 335 3.54 3.570 14.60  0  1    5    8  speedy car
Volvo 142E          21.4   4 121.0 109 4.11 2.780 18.60  1  1    4    2 scrap truck

Single Exit

Note that while hotornot in the question works having a single exit at the bottom is preferred so you might want to write it like this which returns the value of the if/else statement.

hotornot1 <- function(horsepower, cylinders, name) {
  if (horsepower < 200 && cylinders < 5 && name != "Merc 240D") {
    "scrap truck"
  } else "speedy car"
}

mtcars$name <- rownames(mtcars)
transform(mtcars, name = mapply(hotornot1, hp, cyl, name))

Vectorized Code

Also it would be possible to write it as a vectorized function eliminating the need for explicitly iterating over the rows. To do that use & in place of && and ifelse in place of if ... else ... . Unlike && the & operator does not short circuit but in this case it does not matter.

hotornot_vec <- function(horsepower, cylinders, name) {
  ifelse(horsepower < 200 & cylinders < 5 & name != "Merc 240D",
    "scrap truck", "speedy car")
}

mtcars$name <- rownames(mtcars)
transform(mtcars, name = hotornot_vec(hp, cyl, name))

Upvotes: 4

Qbik
Qbik

Reputation: 6147

You can do this also with dplyr package dplyr::mutate(mtcars, name = mapply(hotornot, hp, cyl, name))if you prefer to work with packages rrom tidyverse

Upvotes: 0

ThomasIsCoding
ThomasIsCoding

Reputation: 101343

You can run via with (or within) + Vectorize

within(mtcars, name <- Vectorize(hotornot)(hp, cyl, name))

or

mtcars$name <- with(mtcars, Vectorize(hotornot)(hp, cyl, name))

where

> with(mtcars, Vectorize(hotornot)(hp, cyl, name))
 [1] "speedy car"  "speedy car"  "scrap truck" "speedy car"  "speedy car" 
 [6] "speedy car"  "speedy car"  "speedy car"  "scrap truck" "speedy car"
[11] "speedy car"  "speedy car"  "speedy car"  "speedy car"  "speedy car"
[16] "speedy car"  "speedy car"  "scrap truck" "scrap truck" "scrap truck"
[21] "scrap truck" "speedy car"  "speedy car"  "speedy car"  "speedy car"
[26] "scrap truck" "scrap truck" "scrap truck" "speedy car"  "speedy car" 
[31] "speedy car"  "scrap truck"

Upvotes: 4

Related Questions