Victor
Victor

Reputation: 1205

For loop over a List of Data frames

I'm using the mtcars dataset in R. I have a list of data frames (mtcars dataset split into number of cylinders). I need to:

  1. Identify the car with the min value for miles per gallon (mpg) within each cylinder type (i.e. 4,6,8).
  2. Create a vector that stores the values of horsepower (hp) for each of the cars found in step 1 (the length of the vector will be 3).

Steps I have performed so far, as follows:

# load the data
data(mtcars)

# split cars data.frame into a list of data frames by cylinder
cars <- split(mtcars, mtcars$cyl)    

# find the position within each data frame for the min values of mpg (i.e. first 
# column)
positions <- sapply(cars,function(x) which.min(x[,1]))  

As I see it, the next step would be to make a loop over each data frame to find the horsepower value for each position. I have tried to make a For loop for this, but I haven't been able to make it work. Maybe there's even a better solution for this problem.

Upvotes: 1

Views: 360

Answers (3)

David Arenburg
David Arenburg

Reputation: 92300

You don't need to split the data and then use sapply. There are many ways to reach that output using much more efficient ways. Here's possible data.table solution

mtcars$Cars <- rownames(mtcars)
library(data.table)
data.table(mtcars)[, list(Car = Cars[which.min(mpg)],
                           HP = hp[which.min(mpg)]),
                   by = cyl]

#    cyl                Car  HP
# 1:   6          Merc 280C 123
# 2:   4         Volvo 142E 109
# 3:   8 Cadillac Fleetwood 205

Or maybe using dplyr

library(dplyr)
mtcars %>% 
  mutate(Cars = rownames(mtcars)) %>%
  group_by(cyl) %>% 
  summarize(Car = Cars[which.min(mpg)], HP = hp[which.min(mpg)]) 

# Source: local data frame [3 x 3]
# 
#   cyl                Car  HP
# 1   4         Volvo 142E 109
# 2   6          Merc 280C 123
# 3   8 Cadillac Fleetwood 205

Upvotes: 4

bstockton
bstockton

Reputation: 577

This is really easy if you use the plyr library. Here ya go:

library(plyr)
data(mtcars)

mpMins <- ddply(mtcars, .(cyl),summarize, min = min(mpg), .drop = FALSE)
mpMins

    cyl  min
1   4 21.4
2   6 17.8
3   8 10.4

This only gives you the minimum value of the mpg though, you want the horsepower too

hpMins <- (merge(mpMins, mtcars, by.x = c("min","cyl"), by.y = c("mpg","cyl" )))$hp
hpMins
[1] 205 215 123 109

Strange, there are four values. You said you wanted three. If you go back and check the data though, there are two minimum values of 10.4 for the 8 cylinder category. Remember to be careful when going to summary values (like minimums) to individual observations.

Upvotes: 1

Rich Scriven
Rich Scriven

Reputation: 99391

From the pre-split cars set, you can do it this way with Map and Reduce.

> Reduce(rbind,  
         Map(function(x) x[which.min(x$mpg), "hp", drop = FALSE],   
             cars, USE.NAMES = FALSE)  
         )
                     hp
# Volvo 142E         109
# Merc 280C          123
# Cadillac Fleetwood 205

If you wanted a vector, you can assign the above code to a variable, say rr, and do

> setNames(rr[,1], rownames(rr))
#        Volvo 142E          Merc 280C Cadillac Fleetwood 
#               109                123                205 

Upvotes: 1

Related Questions