Calculate a relative measure comparing three preceding and three following rows in R

Viewed 32

I'm trying to calculate a column (prominence) based on a set of calculations from quite a variety of other columns, and I'm stuck. Any tips? Here's a working example dataset with a set of sounds, their acoustic energy and start and end_times.

df <- data.frame("sound" = c("a", "b", "c", "d", "e", "f", "g"), 
"energy" = c(4, 5, 2, 26, 2, 1.5, 4), 
"start" = c(10, 11, 13, 15, 17, 20, 22), 
"end" = c(10.5, 12, 14, 16, 18.5, 21, 23))

What I'm looking to do is calculate the sounds relative prominence compared to the 3 preceding and 3 following sounds with some time penalty (so if they're too far away from each other in time, they don't enter the calculation). For this, I need to calculate a couple extra things described below.

I illustrate the new variables from the perspective of the sound "d", but the aim is to do these calculations for every sound in the data-frame.

The new variables required:

  • time_dist(d) = start(d) - end(a, b, c) for the 3 preceding sounds but start(e, f, g) - end(d) for the 3 following sounds
  • time_dist_energy = time_dist * energy(a, b, c, e, f, g)

These are spelled out in the following data-frame

sound  energy  start  end    time_dist    time_dist_energy      prominence 
a      4       10     10.5   15-10.5=4.5  (15-10.5)*4=18 
b      5       11     12     15-12=3      (15-12)*5=15 
c      2       13     14     15-14=1      (15-14)*2=2 
d      26      15     16     NA            NA                   26 > {18,15,2,2,6,24} = 1
e      2       17     18.5   17-16=1       (17-16)*2=2 
f      1.5     20     21     20-16=4       (20-16)*1.5=6 
g      4       22     23     22-16=6       (22-16)*4=24

Using these calculations, I want to define a new column, prominence, which compares the energy of each segment ("d" in this case) to the time_dist_energy of the 3 preceding and 3 following sounds. If it's greater, assign 1, if not, assign 0.

  • prominence = if energy(d) > {18, 15, 2, 2, 6, 24} == 1, else 0

There's so many steps to calculating prominence for each sound in the data-frame, that I'm really stuck. Any and all help is greatly appreciated!!!

1 Answers

Using DF defined reproducibly in the Note at the end, use rollapply to create a window over 7 consecutive row numbers and pass that to prominence to perform the indicated calculation.

library(zoo)

prominence <- function(ix, DF) with(DF[ix, ], {
  time_dist <- c(start[4] - end[1:3], NA, start[5:7] - end[4])
  time_dist_energy <- time_dist * energy
  +(energy[4] > max(time_dist_energy[-4]))
})

nr <- nrow(DF)
transform(DF, prominence = rollapply(1:nr, 7, prominence, DF = DF, fill = NA))

giving:

  sound energy start  end prominence
1     a    4.0    10 10.5         NA
2     b    5.0    11 12.0         NA
3     c    2.0    13 14.0         NA
4     d   26.0    15 16.0          1
5     e    2.0    17 18.5         NA
6     f    1.5    20 21.0         NA
7     g    4.0    22 23.0         NA

Note

Lines <- "sound  energy  start  end
a      4       10     10.5   
b      5       11     12     
c      2       13     14     
d      26      15     16     
e      2       17     18.5   
f      1.5     20     21     
g      4       22     23"

DF <- read.table(text = Lines, header = TRUE)
Related