Mario Cappellano
Mario Cappellano

Reputation: 71

Simple R function issue

I've written a very long winded R function that I'm sure can be recreated in 2 lines or less, but I just can't get a handle on how as yet.

I pass my function a dataframe of greyhound result data and an integer representing the distance run. It then returns the winning percentage for each box number (8 boxes in total) over that distance.

Currently:

boxPercents <- function(dist, data) {

## get each box num of wins
x1 <- data$position == 1 & data$distance == dist & data$box == 1
x2 <- data$position == 1 & data$distance == dist & data$box == 2
x3 <- data$position == 1 & data$distance == dist & data$box == 3
...
x8 <- data$position == 1 & data$distance == dist & data$box == 8

## count the total num of races at that 
numRaces <- data$position == 1 & data$distance == dist

## print out the winning percent for each box
print(sum(x1) / sum(numRaces))
print(sum(x2) / sum(numRaces))
print(sum(x3) / sum(numRaces))
...
print(sum(x8) / sum(numRaces))

}

My output is like the below which I then convert to a vector:

[1] 0.2452107
[1] 0.1340996
[1] 0.09961686
[1] 0.1034483
[1] 0.08045977
[1] 0.1034483
[1] 0.09961686
[1] 0.1340996

I'm pretty certain one of the apply functions is what I'm supposed to be using but all efforts have been fruitless.

Edit: Here's the header of the data:

       track      date race position box             name   sp  fave distance
 Warrnambool 02 Jan 14    1        1   1       TOP SECRET  1.7  true      450
 Warrnambool 02 Jan 14    1        2   4     FLASH WILSON  4.7 false      450
 Warrnambool 02 Jan 14    1        3   8 HEAPS OF ABILITY 11.8 false      450
 Warrnambool 02 Jan 14    1        4   7   OCCUPATION LAD 24.1 false      450
 Warrnambool 02 Jan 14    1        5   2   HE'S A VILLIAN 19.3 false      450
 Warrnambool 02 Jan 14    1        6   5 ZAC'S A SIXPENCE  9.7 false      450

Upvotes: 2

Views: 174

Answers (2)

akrun
akrun

Reputation: 887821

You can make it a function and use the appropriate dist

dist <- 450
vapply(1:8, function(i) sum(with(data, 
           position==1 & distance==dist & box==i))/sum(with(data, 
               position==1 & distance==dist)), numeric(1L))

or

sapply(1:8, function(i) sum(with(data,
     position==1 & distance==dist & box==i))/sum(with(data,
                                 position==1 & distance==dist)))

Because the position and distance are the same in both numerator and denominator, I would do

sapply(1:8, function(i) {indx <- with(data, position==1 & distance==dist)
                      sum(indx & data$box==i)/sum(indx)} )

Update

A faster option for big datasets will be using data.table

library(data.table)
 setDT(data)[position==1 & distance==dist, c(.SD,numRaces= .N)][,
                list(percentage=unique(.N/numRaces)), by=box]

Or the above could be shortened (as commented by @Arun)

 setDT(data)[position==1 & distance==dist, .N, by=box][, N := N/sum(N)]

Or an option using prop.table

 as.data.frame(prop.table(table(subset(data,
        position==1 & distance==dist, select=c(position, box)))))

Upvotes: 4

talat
talat

Reputation: 70336

Another option using dplyr, might be faster than sapply approaches for large data sets:

Update:

library(dplyr)
boxPercents <- function(dist, data) {
  data <- data %>% filter(position == 1 & distance == dist) %>% select(box)
  data %>% count(box) %>% transmute(percentage = n / sum(n))
}

Original:

boxPercents <- function(dist, data) {
  data <- data %>% filter(position == 1 & distance == dist) %>% select(box)
  numRaces <- nrow(data)
  data %>% 
    group_by(box) %>%
    summarise(percentage = n() / numRaces) 
}

Using the function (note that I modified the input data - see dput below):

boxPercents(450, data)
#Source: local data frame [2 x 2]
#
#  box percentage
#1   1  0.6666667
#2   5  0.3333333

data

data <- structure(list(track = structure(c(1L, 1L, 1L, 1L, 1L, 1L), .Label = "Warrnambool", class = "factor"), 
    date = structure(c(1L, 1L, 1L, 1L, 1L, 1L), .Label = "02 Jan 14", class = "factor"), 
    race = c(1L, 1L, 1L, 1L, 1L, 1L), position = c(1, 2, 3, 4, 
    1, 1), box = c(1, 4, 8, 7, 1, 5), name = structure(c(5L, 
    1L, 2L, 4L, 3L, 6L), .Label = c("FLASH WILSON", "HEAPS OF ABILITY", 
    "HES A VILLIAN", "OCCUPATION LAD", "TOP SECRET", "ZACS A SIXPENCE"
    ), class = "factor"), sp = c(1.7, 4.7, 11.8, 24.1, 19.3, 
    9.7), fave = structure(c(2L, 1L, 1L, 1L, 1L, 1L), .Label = c("false", 
    "true"), class = "factor"), distance = c(450L, 450L, 450L, 
    450L, 450L, 450L)), .Names = c("track", "date", "race", "position", 
"box", "name", "sp", "fave", "distance"), row.names = c(NA, -6L
), class = "data.frame")

Upvotes: 2

Related Questions