Examples
Loading serialised objects
If you want to work with the serialised dataset, models, or simulations in a Python session, you’ll first need to temporarily add the directory to where these classes are defined to your PATH:
import sys
import os
sys.path.append("PATH_TO_\src\analyse")
Now, to unserialise the models and simulations:
import dill
mds = dill.load(open("PATH_TO\models\phase_correction_mds.p", "rb"))
sims = dill.load(open("PATH_TO\models\phase_correction_sims.p", "rb"))
Working with PhaseCorrectionModel
instances
The mds
variable we defined above is an interable containing an instance of the PhaseCorrectionModel
class for each performance in the corpus. We can access the model md
for each individual performance, therefore, with code like:
for md in mds:
do something
PhaseCorrectionModel
attributes
There are a number of helpful attributes within this class that we can access to gain more information about each performance. For example, to print the results of the nearest neighbour matching process, you can access the md.keys_nn
or md.drms_nn
attribute. If you want to access the underlying regression object in StatsModels, you can use the md.keys_md
and md.drms_md
attributes, for example md.keys_md.summary()
or md.drms_md.params
.
Printing a summary dataframe
We can print a dataframe containing summary statistics for all the performances in the corpus (e.g. the individual coupling coefficients) by accessing the md.keys_dic
and md.drms_dic
attributes in a loop, for example:
import pandas as pd
res = []
for md in mds:
res.append(md.keys_dic) # Results dictionary for pianist
res.append(md.drms_dic) # Results dictionary for drummer
df = pd.DataFrame(res)
df
Accessing ensemble-level characteristics
The df
contained above will contain one row for each performer, meaning that the data for a single performance in the corpus occupies two rows (given two performers in each ensemble).
Certain characteristics referred to in the paper are instead measured across an ensemble; for instance, coupling asymmetry is calculated as the absolute difference between coupling coefficients obtained from both musicians. To obtain these characteristics, we will need to group df
, e.g.:
res = []
grp_vars = ['trial', 'block', 'latency', 'jitter']
for idx, grp in df.groupby(grp_vars):
dr = grp[grp['instrument'] != 'Keys']['correction_partner'].iloc[0]
ke = grp[grp['instrument'] == 'Keys']['correction_partner'].iloc[0]
di = {'coupling_strength': dr + ke, 'coupling_asymmetry': abs(dr - ke)}
di.update({k: v for k, v in zip(grp_vars, idx)})
res.append(di)
df_ensemble = pd.DataFrame(res)
The resulting df_ensemble
will have 6 columns, containing the duo and session number, the latency and jitter values of the condition, and the coupling asymmetry and strength values obtained across both members of the duo. Note that this grouping is carried out automatically when creating figures and visualisations.
For more information on the PhaseCorrectionModel
class, consult the documentation and code.
Working with Simulation
instances
The sims
list, likewise, is an iterable of Simulation
class instances, where each instance corresponds to one condition and simulation paradigm. Taking the 45 ms latency, 0.5x jitter experimental condition as an example, we’d have one Simulation instance for all the individual simulations made under using the democracy
paradigm and another instance for the simulations made using the anarchy
paradigm.
Just as before, we can access each individual Simulation sim
instance by iterating like:
for sim in sims:
do something
Simulation
attributes
Inside each sim
instance, we can access every individual keyboard simulation with sim.keys_simulations
and every drum simulation with sim.drms_simulations
. These attributes are lists of dataframes, with each dataframe corresponding to one simulation. So, to match the data for one ensemble simulation together:
matched = [(drms_sim, keys_sim) for drms_sim, keys_sim in zip(sim.keys_simulations, sim.drms_simulations)]
Simulation
helper functions
There are three helper functions provided to obtain averages across all the simulations contained within a single sim
instance, which can be used when comparing across paradigms. These are:
sim.get_average_tempo_slope()
sim.get_average_ioi_variability()
sim.get_average_pairwise_asynchrony()
Each function takes in a keyword argument func
, which can be used to define the method used to obtain the average. Any function should be compatible here, as long as it returns a single value from a Pandas series of dtype float
. Additional keyword arguments passed into these helper functions will be passed as **kwargs
to func
.
Printing a summary dataframe
As with the PhaseCorrectionModel
class, you can print a dataframe containing summary statistics for all the simulations by accessing sim.results_dic
in a loop, for example:
res = pd.DataFrame([sim.results_dic for sim in sims])
Working with the graphs
The code for generating the graphs, contained within the \src\visualise
directory, is generally provided as-is, and not intended to be accessed or imported directly outside this project or used for different datasets. However, some small changes can still be made.
Aesthetic changes to the plots can be made by altering the constant variables defined at the start of visualise_utils.py
contained in src\visualise\
. You can also make aesthetic & statistic changes to the plots by adjusting various keyword arguments used in the individual plotting functions called in src\visualise\run_visualisations.py
.