amads.time.variability#

Time variability.

This module provides various functions for calculating rhythmic variability in music, including the normalized pairwise variability index (nPVI) and variations thereof.

References:
Author:

Huw Cheston (2025)

amads.time.variability.isochrony_proportion(durations) float[source]#

Calculates the isochrony proportion (IsoP) for a list of durations, as defined in Condit-Schultz (2019): “by iterating over every pair of successive IOIs in a rhythm, counting the pairs that are identical, and dividing this count by the total number of pairs (one less than the total number of IOIs).”

Condit-Schultz 2019 demonstrates that the IsoP accounts for a large degree of the variance in the nPVI.

Parameters:
  • (Iterable[float]) (durations)

  • durations (Iterable[float])

Returns:

float

Return type:

the extracted IsoP value

Examples

>>> isochrony_proportion([1, 1, 2, 2, 1, 0.5])
0.4
Parameters:

durations (Iterable[float])

Return type:

float

amads.time.variability.normalized_pairwise_calculation(durations) list[float][source]#

Calculates the normalized pairwise calculation (nPC) for a list of durations, as defined by Condit-Schultz (2019).

The nPVI is equivalent to the arithmetic mean of a set of nPC values. The equation for nPC can be written as:

\[\text{nPC} = 200 * \biggl{|} \frac{\text{antecedent IOI} - \text{consequent IOI}} {\text{antecedent IOI} + \text{consequent IOI}} \biggr{|}\]
Parameters:
  • (Iterable[float]) (durations)

  • durations (Iterable[float])

Returns:

Iterable[float]

Return type:

the extracted nPC value(s) for every pair of antecedent-consequent durations

Examples

From Condit-Schultz (2019) figure 3.

>>> normalized_pairwise_calculation([1, 15])
[175.]
>>> normalized_pairwise_calculation([1, 7])
[150.]
>>> normalized_pairwise_calculation([1, 5])
[133.33]
>>> normalized_pairwise_calculation([1, 4])
[120.]
>>> normalized_pairwise_calculation([1, 3])
[100.]
>>> normalized_pairwise_calculation([1, 2])
[66.67]
>>> normalized_pairwise_calculation([2, 3])
[40.]
>>> normalized_pairwise_calculation([1, 1])
[0.]
Parameters:

durations (Iterable[float])

Return type:

list[float]

amads.time.variability.normalized_pairwise_variability_index(durations) float[source]#

Calculates the normalised pairwise variability index (nPVI).

The nPVI is a measure of variability between successive elements in a sequence.

The equation is:

\[\]

text{nPVI} = frac{100}{m-1} times sumlimits_{k=1}^{m-1} left| frac{d_k - d_{k+1}}{frac{d_k + d_{k+1}}{2}} right|

where \(m\) is the number of intervals, and \(d_k\) is the duration of the \(k^{th}\) interval.

A completely regular stream of equal durations returns 0 variability. High difference between successive items returns a high nPVI value irrespective of other (e.g., metrical) considerations.

Parameters:
  • (Iterable[float]) (durations)

  • durations (Iterable[float])

Returns:

float

Return type:

the extracted nPVI value.

Examples

A completely regular stream of equal durations returns 0 variability.

>>> normalized_pairwise_variability_index([1., 1., 1., 1.])
0.

This example is from Daniele & Patel (2013, Appendix).

>>> durs = [1., 1/2, 1/2, 1., 1/2, 1/2, 1/3, 1/3, 1/3, 2., 1/3, 1/3, 1/3, 1/3, 1/3, 1/3, 3/2, 1., 1/2]
>>> x = normalized_pairwise_variability_index(durations=durs)
>>> round(x, 1)
42.2

These next examples are from Condit-Schultz’s 2019 critique (Figure 2).

>>> normalized_pairwise_variability_index([0.25, 0.25, 0.25, 0.25, 1., 0.25, 0.25, 0.25, 0.25, 1.])
40.
>>> normalized_pairwise_variability_index([0.25, 0.25, 0.5, 1., 0.25, 0.25, 0.5, 1.])
55.2
>>> normalized_pairwise_variability_index([0.5, 0.25, 0.25, 1., 0.5, 0.25, 0.25, 1.])
62.8
>>> normalized_pairwise_variability_index([2, 1, 2, 1])
66.66
>>> normalized_pairwise_variability_index([0.5, 1, 2, 1, 0.5])
66.66
>>> normalized_pairwise_variability_index([2, 1, 0.5, 1, 0.5, 0.25])
66.66
Parameters:

durations (Iterable[float])

Return type:

float

amads.time.variability.pairwise_anisochronous_contrast_index(durations) float[source]#

Calculates the pairwise anisochronous contrast index (pACI) for a list of durations.

Defined in Condit-Schultz (2019), the pACI is equivalent to the nPVI, except that it “factors out” isochronous pairs of IOIs. This means that it is sensitive to changes in the frequencies of IOIs pairs such as 2:1, 3:1, without being “overwhelmed by isochronous pairs” (1:1).

Parameters:
  • (Iterable[float]) (durations)

  • durations (Iterable[float])

Returns:

float

Return type:

the extracted pACI value

amads.time.variability.phrase_normalized_pairwise_variability_index(durations, phrase_boundaries) float[source]#

Calculates the phrase-normalized pairwise variability index (pnPVI), as defined by VanHandel & Song (2010).

The pnNPVI calculates the nPVI, ignoring cases where pairs of IOIs straddle a phrase boundary. To do so, we also need a list of times for where those phrase boundaries fall.

Parameters:
  • (Iterable[float]) (durations) – phrase_boundaries (Iterable[float]): the phrase boundaries to analyse

  • durations (Iterable[float])

  • phrase_boundaries (Iterable[float])

Returns:

float

Return type:

the extracted pnNPVI value