Is there a simpler way to create a factor variable with more than 2 levels in a data.frame?

Viewed 42

I'm quite new to R, and because I'm unfamiliar with all of its features I'm still coding certain things in quite a naive and round-about way. Here, I have a dataset of recent films, their Rotten Tomatoes critic scores, Audience scores, and budget in millions:

> str(film_ratings_data)
'data.frame':   562 obs. of  7 variables:
 $ Film                 : chr  "(500) Days of Summer " "10,000 B.C." "12 Rounds " "127 Hours" ...
 $ Genre                : Factor w/ 7 levels "Action","Adventure",..: 3 2 1 2 3 1 3 5 3 3 ...
 $ Rotten_Tomatoes_Score: int  87 9 30 93 55 39 40 50 43 93 ...
 $ Audience_Score       : int  81 44 52 84 70 63 71 57 48 93 ...
 $ Budget_Millions      : int  8 105 20 18 20 200 30 32 28 8 ...
 $ Release_Year         : int  2009 2008 2009 2010 2009 2009 2008 2007 2011 2011 ...

The budgets range from almost 0 to ~300-million; however, most of them are somewhere in the middle, making plotting them with a continous colour scale difficult to read. I wanted to make a new factor variable in the film_ratings_data DF with levels "Low", "Medium", and "High". I've done this (with the code below) but this seems incredibly verbose (and like I said naive) so I'd like to know if there is a better way:

Cost_Range_Low  <- which(film_ratings_data$Budget_Millions >= 0 & film_ratings_data$Budget_Millions <= 99)
Cost_Range_Med  <- which(film_ratings_data$Budget_Millions > 99 & film_ratings_data$Budget_Millions <= 199)
Cost_Range_High <- which(film_ratings_data$Budget_Millions > 199)
Cost_Range      <- rep("-", times = nrow(film_ratings_data))
Cost_Range[Cost_Range_Low] <- "Low"; Cost_Range[Cost_Range_Med] <- "Medium"; Cost_Range[Cost_Range_High] <- "High"
film_ratings_data <- cbind(film_ratings_data, Cost_Range = as.factor(Cost_Range))

I had found a few threads using the ifelse() function, but my variable has 3 levels, not two (maybe I could have chained ifelse()'s together? There's an idea). The book R for Data Science by Hadley Wickham also has a few examples with the mutate() function, which I initially wanted to use but couldn't find a neat way of doing the conditional checking on one line.

If anyone knows a better way, then I'm sure you'll save me a lot of frustration in the future. Cheers!

2 Answers

You can replace your whole code with

film_ratings_data$Cost_Range <- cut(film_ratings_data$Budget_Millions, 
                                    breaks = c(0, 99, 199, Inf), 
                                    labels = c("Low", "Medium", "High"))

An option with findInterval

film_ratings_data$Cost_Range <- c("Low", "Medium", "High")[findInterval(film_ratings_data$Budget_Millions, 
                                c(0, 99, 199))]
Related