indexers Package¶
indexers
Package¶
active_voices
Module¶
-
class
vis.analyzers.indexers.active_voices.
ActiveVoicesIndexer
(score, settings=None)[source]¶ Bases:
vis.analyzers.indexer.Indexer
Indexer that counts the number of voices active at each offset. It can either find all voices sounding, or only the voices that are attacking, depending on the settings passed.
Call this indexer via the
get_data()
method of either anindexed_piece
object or anaggregated_pieces
object (see example below). If nothing is passed in the ‘data’ argument of the call toget_data()
, then the default is to process theNoteRestIndexer
results of theindexed_piece
in question. You can pass some other DataFrame in the ‘data’ argument, but it is discouraged.Parameters: - 'attacked' (boolean) – When true, only counts the voices that are attacking at each offset. Defaults to false.
- 'show_all' (boolean) – When true, shows the results at all offsets, even if there is not change. Defaults to false.
Examples:
Prepare an indexed piece:
>>> from vis.models.indexed_piece import Importer >>> ip = Importer('path_to_piece.xml')
Get the
ActiveVoicesIndexer
results with the default settings:>>> ip.get_data('active_voices')
Get the
ActiveVoicesIndexer
results with specified settings:>>> av_setts = { 'attacked': True, 'show_all': True } >>> ip.get_data('active_voices', settings=av_setts)
-
default_settings
= {'show_all': False, 'attacked': False}¶
-
possible_settings
= ['attacked', 'show_all']¶
-
required_score_type
= 'pandas.DataFrame'¶
-
run
()[source]¶ Returns: new index of the active voices in the piece. Return type: pandas.DataFrame
-
vis.analyzers.indexers.active_voices.
indexer1
(x)[source]¶ Used internally by the
ActiveVoicesIndexer
to count individualevents.
approach
Module¶
-
class
vis.analyzers.indexers.approach.
ApproachIndexer
(score, settings=None)[source]¶ Bases:
vis.analyzers.indexer.Indexer
Using
OverBassIndexer
andFermataIndexer
results, finds cadences as lists of events in the approach to a fermata.Call this indexer via the
get_data()
method of either anindexed_piece
object or anaggregated_pieces
object (see example below).Parameters: Example:
Prepare an indexed piece and import pandas:
>>> from vis.models.indexed_piece import Importer >>> import pandas >>> ip = Importer('path_to_piece.xml')
Prepare
OverBassIndexer
andFermataIndexer
results. For more specific advice on how to do this, please see the documentation of those two indexers. These two DataFrames should be passed as a list. For simplicity, including theFermataIndexer
results is optional, and this example shows how to use theApproachIndexer
without explicitly providing theFermataIndexer
results, so the ‘data’ argument is a singleton list.>>> overbass_input_dfs = [ip.get_data('noterest'), ip.get_data('vertical_interval')] >>> ob_setts = { 'type': 'notes' } >>> overbass = ip.get_data('over_bass', data=overbass_input_dfs, settings=ob_setts)
Get the
ApproachIndexer
results with specified settings:>>> approach_setts = {'length': 3} >>> ip.get_data('approach', data=[overbass], settings=approach_setts)
-
possible_settings
= ['length', 'voice']¶
-
required_score_type
= 'pandas.DataFrame'¶
-
run
()[source]¶ Makes a new index of the approaches to fermatas in the piece.
Returns: A DataFrame
of the approaches.Return type: pandas.DataFrame
-
contour
Module¶
-
vis.analyzers.indexers.contour.
COM_matrix
(contour)[source]¶ Creates a matrix representing the contour given.
-
class
vis.analyzers.indexers.contour.
ContourIndexer
(score, settings=None)[source]¶ Bases:
vis.analyzers.indexer.Indexer
Indexes the contours of a given length in a piece, where contour is a way of numbering the relative heights of pitches, beginning at 0 for the lowest pitch.
Call this indexer via the
get_data()
method of either anindexed_piece
object or anaggregated_pieces
object (see example below). If nothing is passed in the ‘data’ argument of the call toget_data()
, then the default is to process theNoteRestIndexer
results of theindexed_piece
in question. You can pass some other DataFrame to the ‘data’ argument, but it is discouraged.Parameters: 'length' (int) – This is the length of the contour you want to look at. Example:
Prepare an indexed piece:
>>> from vis.models.indexed_piece import Importer >>> ip = Importer('path_to_piece.xml')
Get the
ContourIndexer
results with specified settings and processing the notes and rests:>>> notes = ip.get_data('noterest') >>> contour_setts = {'length': 3} >>> ip.get_data('contour', data=notes, settings=contour_setts)
-
possible_settings
= ['length']¶
-
required_score_type
= 'pandas.DataFrame'¶
-
run
()[source]¶ Makes a new index of the contours in the piece.
Returns: A DataFrame
of the contours.Return type: pandas.DataFrame
-
dissonance
Module¶
-
class
vis.analyzers.indexers.dissonance.
DissonanceIndexer
(score, settings=None)[source]¶ Bases:
vis.analyzers.indexer.Indexer
Indexer that locates vertical dissonances between pairs of voices in a piece. It then categorizes intervals as consonant or dissonant and in the case of fourths (perfect or augmented) and diminished fifths it examines the other parts sounding with that fourth or fifth (if there are any) to see if the interval can be considered consonant. This dissonance analysis allows for the assignment of a dissonance type name or a consonance label for each voice at each offset. This last step is the DataFrame that gets returned. The score type must be a list of dataframes of the results of the following indexers (order matters): beatstrength, duration, horizontal, vertical. To simplify things, however, it is better to use the
get_data()
on anindexed_piece
object to get results from the dissonance indexer as per the example below. The results of the disonance indexer are in the form of one cell in the resultant dataframe in each part at every offset where there is a new note, rest, or chord onset in any part. These cells contain one-character strings which are an abbreviation of what the dissonance type for that part at that moment is. The abbreviations correspoond to the following dissonance types:- ‘Q’: Dissonant third quarter (special type of accented passing
- tone)
- ‘D’: Descending passing tone
- ‘R’: Ascending (“rising”) passing tone
- ‘L’: Lower neighbour
- ‘U’: Upper neighbour
- ‘S’: Suspension
- ‘F’: Fake suspension
- ‘f’: Diminished fake suspension
- ‘A’: Anticipation
- ‘C’: Nota cambiata
- ‘H’: Chanson idiom
- ‘E’: Echappée (escape tone)
- ‘-‘: Either no dissonance, or the part in question is not
- considered to be the dissonant note of the dissonance it’s in
Example: >>> from vis.models.indexed_piece import Importer >>> ip = Importer(‘symbolic_notation_file_location.xml’) >>> ip.get_data(‘dissonance’)
-
check_4s_5s
(pair_name, iloc_indx, suspect_diss, simuls)[source]¶ This function evaluates whether P4’s, A4’s, and d5’s should be considered consonant based whether or not the lower voice of the suspect_diss forms an interval that causes us to deem the fourth or fifth consonant, as determined by the cons_makers list below. The function should be called once for each potentially consonant fourth or fifth. :param pair_name: Name of pair that has the potentially
consonant fourth or fifth.Parameters: - iloc_indx (Integer.) – Pandas iloc number of interval’s row in dataframe.
- suspect_diss (String.) – Interval name with quality and direction (i.e. nothing or ‘-‘) that corresponds to the fourth or fifth to be examined.
Returns: string representing analysis of fourth or fifth in question where a ‘C’ or ‘D’ has been prepended to the interval name with quality to show that it was considered consonant or dissonant respectively.
Return type:
-
classify
(indx, pair, event, prev_event)[source]¶ Checks the dissonance definitions to find a suitable label for the dissonance passed. If no identifiable dissonance type matches, returns an unknown dissonance label. Omits checking the pair if either voice was previously given a known dissonance label still in vigour at the given offset. It only takes its four arguments to pass them on to the dissonance-type functions it calls. :returns: A 5-tuple that can be unpacked to assign separate
labels for each voice in the pair. If none of the known dissonance types were detected the 5-tuple will have the labels to mark one or both of the voices as dissonant.Return type: tuple
-
required_score_type
= 'pandas.DataFrame'¶
-
run
()[source]¶ Make a new index of the piece which consists of a DataFrame with as many columns as there are voices in the piece. The index is the offset of the dissonance analyses. Another DataFrame (
diss_ints
) is calculated but not returned. It is in the same format as theIntervalIndexer
(i.e. a DataFrame of Series where each series corresponds to the intervals in a given voice pair). The difference between this and the interval indexer is that this one figures out whether fourths or diminished fifths should be considered consonant for the purposes of dissonance classification. diss_ints is not calculated separately because it essentially consists of the same loop needed for dissonance classification. :returns: ADataFrame
of the new indices. The columnshave aMultiIndex
.Return type: pandas.DataFrame
fermata
Module¶
Index fermatas.
-
class
vis.analyzers.indexers.fermata.
FermataIndexer
(score, settings=None)[source]¶ Bases:
vis.analyzers.indexer.Indexer
Index
Fermata
.Finds :class:`~music21.expressions.Fermata`s.
Example:
>>> from vis.models.indexed_piece import Importer >>> ip = Importer('pathnameToScore.xml') >>> ip.get_data('fermata')
-
required_score_type
= 'stream.Part'¶
-
-
vis.analyzers.indexers.fermata.
indexer_func
(event)[source]¶ Used internally by
FermataIndexer
. InspectsParameters: event (iterable of music21.note.Note
ormusic21.note.Rest
) – An iterable (nominally aSeries
) with an object to convert. Only the first object in the iterable is processed.Returns: If the first object in the list is a contains a Fermata
, stringu'Fermata'
is returned. Else, NaN.Return type: str
interval
Module¶
Index intervals. Use the IntervalIndexer
to find vertical
(harmonic) intervals between two parts. Use the
HorizontalIntervalIndexer
to find horizontal (melodic)
intervals in the same part.
-
class
vis.analyzers.indexers.interval.
HorizontalIntervalIndexer
(score, settings=None)[source]¶ Bases:
vis.analyzers.indexers.interval.IntervalIndexer
Use
music21.interval.Interval
to create an index of the horizontal (melodic) intervals in a single part. You should provide the result ofNoteRestIndexer
. Alternatively you could provide the results of the :class:’~vis.analyzers.offset.FilterByOffsetIndexer’ if you want to check for horizontal intervals at regular durational intervals. These settings apply to theHorizontalIntervalIndexer
in addition to the settings available from theIntervalIndexer
.Parameters: - 'simple or compound' (str) – Whether intervals should be
represented in their single-octave form (either
'simple'
or'compound'
). - or string 'quality' (bool) – Whether to display diatonic intervals without quality (either False or “diatonic no quality”), diatonic intervals with quality (either True or ‘diatonic with quality’), chromatic intervals (use ‘chromatic’), or interval class intervals (use ‘interval class’).
- 'directed' (boolean) – Whether we distinguish between which note is higher than the other. If True (default), prepends a ‘-‘ before everything else if the first note passed is higher than the second.
- 'horiz_attach_later' (boolean) – If
True
, the offset for a horizontal interval is the offset of the later note in the interval. The default isFalse
, which gives horizontal intervals the offset of the first note in the interval. - 'mp' (boolean) –
- Multiprocesses when True (default) or
- processes serially when False.
Example:
>>> from vis.models.indexed_piece import Importer >>> settings = { 'quality': 'interval class', 'simple or compound': 'simple', 'directed': False } >>> ip = Importer('pathnameToScore.xml') >>> ip.get_data('horizontal_interval', settings)
-
default_settings
= {'directed': True, 'simple or compound': 'compound', 'horiz_attach_later': False, 'quality': False, 'mp': True}¶
-
run
()[source]¶ Make a new index of the piece.
Returns: The new indices. Refer to the example below. Return type: pandas.DataFrame
- 'simple or compound' (str) – Whether intervals should be
represented in their single-octave form (either
-
class
vis.analyzers.indexers.interval.
IntervalIndexer
(score, settings=None)[source]¶ Bases:
vis.analyzers.indexer.Indexer
Use
music21.interval.Interval
to create an index of the vertical (harmonic) intervals between two-part combinations.You should provide the result of the
NoteRestIndexer
. However, to increase your flexibility, the constructor requires only a list ofSeries
. You may also provide aDataFrame
exactly as outputted by theNoteRestIndexer
. The settings for theIntervalIndexer
are as follows:Parameters: - 'simple or compound' (str) – Whether intervals should be
represented in their single-octave form (either
'simple'
or'compound'
). - or string 'quality' (bool) – Whether to display diatonic intervals without quality (either False or “diatonic no quality”), diatonic intervals with quality (either True or ‘diatonic with quality’), chromatic intervals (use ‘chromatic’), or interval class intervals (use ‘interval class’).
- 'directed' (boolean) – Whether we distinguish between which note is higher than the other. If True (default), prepends a ‘-‘ before everything else if the first note passed is higher than the second.
- 'mp' (boolean) – Multiprocesses when True (default) or processes serially when False.
Example:
>>> from vis.models.indexed_piece import Importer >>> settings = { 'quality': 'chromatic', 'simple or compound': 'simple', 'directed': True } >>> ip = Importer('pathnameToScore.xml') >>> ip.get_data('vertical_interval', settings)
-
default_settings
= {'directed': True, 'simple or compound': 'compound', 'quality': False, 'mp': True}¶
-
required_score_type
= 'pandas.DataFrame'¶
-
run
()[source]¶ Make a new index of the piece.
Returns: A DataFrame
of the new indices. The columns have aMultiIndex
; refer to the example below for more details.Return type: pandas.DataFrame
- 'simple or compound' (str) – Whether intervals should be
represented in their single-octave form (either
-
class
vis.analyzers.indexers.interval.
IntervalReindexer
(score, settings=None)[source]¶ Bases:
vis.analyzers.indexers.interval.HorizontalIntervalIndexer
This indexer is only meant to ever be called indirectly to simplify caching of the results of the IntervalIndexer and the
HorizontalIntervalIndexer
. It takes the most information-rich type of interval analysis we offer (i.e. compound, directed intervals with diatonic quality) and re-indexes them to match whatever settings the user has requested. This is much faster, because it takes an entire interval as its input, rather than two notes, and therefore there are considerably fewer memos in its memoization scheme.
meter
Module¶
Indexers for metric concerns.
-
class
vis.analyzers.indexers.meter.
DurationIndexer
(score, part_streams)[source]¶ Bases:
vis.analyzers.indexer.Indexer
Make an index of the durations of all
Note
,Rest
, andChord
objects. These are calculated based on the difference in index positions of consecutive events.Note
Unlike nearly all other indexers, this indexer returns a
Series
offloat
objects rather thanunicode
objects. Also unlike most other indexers, this indexer does not have an indexer func.Example:
>>> from vis.models.indexed_piece import Importer >>> ip = Importer('pathnameToScore.xml') >>> ip.get_data('duration')
-
required_score_type
= 'pandas.DataFrame'¶
-
run
()[source]¶ Make a new index of the piece.
Returns: The new indices of the durations of each note or rest event in a score. Note that each item is a float, rather than the usual basestring. Return type: pandas.DataFrame
-
-
class
vis.analyzers.indexers.meter.
MeasureIndexer
(score)[source]¶ Bases:
vis.analyzers.indexer.Indexer
Make an index of the measures in a piece. Time signatures changes do not cause a problem. Note that unlike most other indexers this one returns integer values >= 0. Using music21’s part.measureTemplate() function is an alternative but it turned out to be much less efficient to looping over the piece and doing it this way makes this indexer just like all the other stream indexers in VIS.
Example:
>>> from vis.models.indexed_piece import Importer >>> ip = Importer('pathnameToScore.xml') >>> ip.get_data('measure')
-
required_score_type
= 'pandas.DataFrame'¶
-
-
class
vis.analyzers.indexers.meter.
NoteBeatStrengthIndexer
(score)[source]¶ Bases:
vis.analyzers.indexer.Indexer
Make an index of the
beatStrength
for allNote
,Rest
, andChord
objects.Note
Unlike nearly all other indexers, this indexer returns a
Series
offloat
objects rather thanunicode
objects.Example:
>>> from vis.models.indexed_piece import Importer >>> ip = Importer('pathnameToScore.xml') >>> ip.get_data('beat_strength')
-
required_score_type
= 'pandas.DataFrame'¶
-
-
vis.analyzers.indexers.meter.
beatstrength_ind_func
(event)[source]¶ Used internally by
NoteBeatStrengthIndexer
. ConvertNote
andRest
objects into a string.Parameters: event – A music21 note, rest, or chord object which get queried for its beat strength. :type event: A music21 note, rest, or chord object or NaN. Returns: The beatStrength
of the event which is dependent on the prevailing time signature.Return type: float
-
vis.analyzers.indexers.meter.
measure_ind_func
(event)[source]¶ The function that indexes the measure numbers of each part in a piece. Unlike most other indexers, this one returns int values. Measure numbering starts from 1 unless there is a pick-up measure which gets the number 0. This can handle changes in time signature without problems.
Parameters: event (A music21 measure object or NaN.) – A music21 measure object which get queried for its “number” attribute. Returns: The number attribute of the passed music21 measure. Return type: int
ngram
Module¶
Indexer to find k-part any-object n-grams. This file is a re-implimentation of the previous ngram_indexer.py file.
-
class
vis.analyzers.indexers.ngram.
NGramIndexer
(score, settings=None)[source]¶ Bases:
vis.analyzers.indexer.Indexer
Indexer that finds k-part n-grams from other indices.
The indexer requires at least one “vertical” index, and supports “horizontal” indices that seem to “connect” instances in the vertical indices. Although we use “vertical” and “horizontal” to describe these index types, because the class is an abstraction of two-part interval n-grams, you can supply any information as either type of index. If you want one-part melodic n-grams for example, you should supply the relevant interval information as the “vertical” component. The “vertical” and “horizontal” indices can contain an arbitrary number of observations that can get condensed into one value or kept separate in different columns. There is no relationship between the number of index types, though there must be at least one “vertical” index.
The
'vertical'
and'horizontal'
settings determine which columns of the dataframes inscore
are included in the n-gram output.score
is a list of two dataframes, the vertical observationsDataFrame
and the horizontal observationsDataFrame
.The format of the vertical and horizontal settings is very important and will decide the structure of the resulting n-gram results. Both the vertical and horizontal settings should be a list of tuples. If the optional horizontal setting is passed, its list should be of the same length as that of the vertical setting. Inside of each tuple, enter the column names of the observations that you want to include in each value. For example, if you want to make 3-grams of notes in the tenor in a four-voice choral, use the following settings (NB: there is no horizontal element in this simple query so no horizontal setting is passed. In this scenario you would need to pass the noterest indexer results as the only dataframe in the “score” list of dataframes.):
>>> settings = { 'n': 3, 'vertical': [('2',)] }
If you want to look at the 4-grams in the interval pairs between the bass and soprano of a four-voice choral and track the melodic motions of the bass, the
score
argument should be a 2-item list containing the IntervalIndexer results dataframe and theHorizontalIntervalIndexer
dataframe. Note that theHorizontalIntervalIndexer
results must have been calculated with the'horiz_attach_later'
setting set toTrue
(this is in order to avoid an indexing nightmare). The settings dictionary to pass to this indexer would be:>>> settings = { 'n': 4, 'vertical': [('0,3',)], 'horizontal': [('3',)] }
If you want to get ‘figured-bass’ 2-gram output from this same 4-voice choral, use the same 2-item list for the score argument, and then put all of the voice pairs that sound against the bass in the same tuple in the vertical setting. Here’s what the settings should be:
>>> settings = { 'n': 2, 'vertical': [('0,3', '1,3', '2,3')], 'horizontal': [('3',)] }
In the example above, if you wanted stacks of vertical events without the horizontal connecting events, you would just omit the
'horizontal'
setting from the settings dictionary and also only include the vertical observations in thescore
list of dataframes.If instead you want to look at all the pairs of voices in the 4-voice piece, and always track the melodic motions of the lowest voice in that pair, then put each pair in a different tuple, and in the voice to track melodically in the corresponding tuple in the horizontal list. Since there are 6 pairs of voices in a 4-voice piece, both your vertical and horizontal settings should be a list of six tuples. This will cause the resulting n-gram results dataframe to have six columns of observations. Your settings should look like this:
>>> settings = { 'n': 2, 'vertical': [ ('0,1',), ('0,2',), ('0,3',), ('1,2',), ('1,3',), ('2,3') ], 'horizontal': [ ('1',), ('2',), ('3',), ('2',), ('3',), ('3',) ] }
Since we often want to look at all the pairs of voices in a piece, you can set the
'vertical'
setting to'all'
and this will get all the column names from the first dataframe in the score list of dataframes. Similarly, as we often want to always track the melodic motions of the lowest or highest voice in the vertical groups, the horizontal setting can be set to'highest'
or'lowest'
to automate this voice selection. This means that the preceeding query can also be accomplished with the following settings:>>> settings = { 'n': 2, 'vertical': 'all', 'horizontal': 'lowest' }
The
'brackets'
setting will set off all the vertical events at each time point in square brackets ‘[]’ and horizontal observations will appear in parentheses ‘()’. This is particularly useful if there are multiple observations in each vertical or horizontal slice. For example, if we wanted to redo the query above where n = 4, but this time tracking the melodic motions of both the upper and the lower voice, it would be a good idea to set ‘brackets’ toTrue
to make the results easier to read. The settings would look like this:>>> settings = { 'n': 4, 'vertical': [('0,3',)], 'horizontal': [('0', '3',)], 'brackets': True }
If you want n-grams to terminate when finding one or several particular values, you can specify this by passing a list of strings as the
'terminator'
setting.To show that a horizontal event continues, we use
'_'
by default, but you can set this separately, for example to'P1'
'0'
, as seems appropriate.Once you’ve chosen the appropriate settings, to actually run the indexer call it like this:
Example:
>>> from vis.models.indexed_piece import Importer >>> ip = Importer('pathnameToScore.xml') >>> ngram_settings = { 'n': 2, 'vertical': 'all', 'horizontal': 'lowest' } >>> vert_settings = { 'quality': 'chromatic', 'simple or compound': 'simple', 'directed': True } >>> horiz_settings = { 'quality': 'diatonic with quality', 'simple or compound': 'simple', 'directed': True, 'horiz_attach_later': True } >>> vert_ints = ip.get_data('vertical_interval', settings=vert_settings) >>> horiz_ints = ip.get_data('horizontal_interval', settings=horiz_settings) >>> ip.get_data('ngram', data=[vert_ints, horiz_ints], settings=ngram_settings)
-
default_settings
= {'open-ended': False, 'vertical': 'all', 'brackets': True, 'continuer': '_', 'terminator': [], 'horizontal': [], 'align': 'left'}¶
-
possible_settings
= ['horizontal', 'vertical', 'n', 'open-ended', 'brackets', 'terminator', 'continuer', 'align']¶ A list of possible settings for the
NGramIndexer
.Parameters: - 'horizontal' (list of tuples of strings, default []) – Selectors for the columns to consider as “horizontal.”
- 'vertical' (list of tuples of strings, default 'all'.) – Selectors for the column names to consider as “vertical.”
- 'n' (int) – The number of “vertical” events per n-gram.
- 'open-ended' (boolean, default
False
.) – Appends the next horizontal observation to n-grams leaving them open-ended. - 'brackets' (bool, default True.) – Whether to use delimiters around event
observations. Square brakets [] are used to set off vertical
events and round brackets () are used to set off horizontal
events. This is particularly important to leave as
True
(default) for better legibility when there are multiple vertical or multiple horizontal observations at each slice. - 'terminator' (list of str, default []) – Do not find an n-gram with a vertical item that contains any of these values.
- 'continuer' (str, default '_'.) – When there is no “horizontal” event that corresponds to a vertical event, this is printed instead, to show that the previous “horizontal” event continues.
-
required_score_type
= 'pandas.DataFrame'¶
-
noterest
Module¶
Index note and rest objects into pandas DataFrame(s).
-
class
vis.analyzers.indexers.noterest.
MultiStopIndexer
(score)[source]¶ Bases:
vis.analyzers.indexer.Indexer
Index
Note
,Rest
, andChord
objects in aDataFrame
.Rest
objects become'Rest'
, andNote
objects become the string-format version of theirnameWithOctave
attribute.Chord
objects get unpacked into their constituent pitches.This indexer is meant to be called indirectly with a call to
get_data
on an indexed piece in the manner of the following example.Example:
>>> from vis.models.indexed_piece import Importer >>> ip = Importer('path_to_piece.xml') >>> ip.get_data('multistop')
-
required_score_type
= 'pandas.DataFrame'¶
-
run
()[source]¶ Make a new index of the note and rest names in the piece. When a single part has chord objects, those chords get separated out into as many columns as there are notes in the chord with the greatest number of notes. This means that there can be more columns in this dataframe than there are parts in the piece.
Returns: A DataFrame
of the new indices. The columns have aMultiIndex
.Return type: pandas.DataFrame
-
-
class
vis.analyzers.indexers.noterest.
NoteRestIndexer
(score)[source]¶ Bases:
vis.analyzers.indexer.Indexer
Index
Note
andRest
objects in aPart
.Rest
objects become'Rest'
, andNote
objects become the string-format version of theirnameWithOctave
attribute.This indexer is meant to be called indirectly with a call to
get_data
on an indexed piece in the manner of the following example.Example:
>>> from vis.models.indexed_piece import Importer >>> ip = Importer('path_to_piece.xml') >>> ip.get_data('noterest')
-
required_score_type
= 'pandas.DataFrame'¶
-
-
vis.analyzers.indexers.noterest.
multistop_ind_func
(event)[source]¶ Used internally by
MultiStopIndexer
. ConvertNote
andRest
objects into a string and convert theChord
objects into a list of the strings of their consituent pitch objects. The results must be contained in a tuple or a list so that chords can later be unpacked into different 1-voice strands.Parameters: event (A music21 note, rest, or chord object.) – A music21 note, rest, or chord object which get queried for their names. Returns: A one-tuple containing a string representation of the note or rest, or if the event is a chord, a list of the strings of the names of its constituent pitches. Return type: 1-tuple of str or list of strings Examples:
>>> from noterest.py import indexer_func >>> from music21 import note, >>> indexer_func(note.Note('C4')) (u'C4',) >>> indexer_func(note.Rest()) (u'Rest',) >>> indexer_func(chord.Chord([note.Note('C4'), note.Note('E5')])) [u'C4', u'E5']
-
vis.analyzers.indexers.noterest.
noterest_ind_func
(event)[source]¶ Used internally by
NoteRestIndexer
. ConvertNote
,Rest
, andIf you want to keep all the pitches in chords, consider using the :class:`MultiStopIndexer
instead.Parameters: event (A music21 note, rest, or chord object.) – A music21 note, rest, or chord object which get queried for their names. Returns: A one-tuple containing a string representation of the note or rest, or if the event is a chord, a list of the strings of the names of its constituent pitches. Return type: 1-tuple of str or list of strings Examples:
>>> from noterest.py import indexer_func >>> from music21 import note, >>> indexer_func(note.Note('C4')) u'C4' >>> indexer_func(note.Rest()) u'Rest' >>> indexer_func(chord.Chord([note.Note('E5'), note.Note('C4')])) u'E5'
-
vis.analyzers.indexers.noterest.
unpack_chords
(df)[source]¶ The c in nrc in methods like _get_m21_nrc_objs() stands for chord. This method unpacks music21 chords into a list of their constituent pitch objects. These pitch objects can be queried for their
nameWithOctave
in the same way that note objects can in music21. This works by broadcasting the list of pitches in each chord object in each part’s elements to a dataframe of note, pitch, and rest objects. So each part that had chord objects in it gets represented as a dataframe instead of just a series. Then the series from the parts that didn’t have chords in them get concatenated with the parts that did, resulting in potentially more columns in the final dataframe then there are parts in the score.
offset
Module¶
Indexers that modify the “offset” values (floats stored as the “index”
of a pandas.Series
), potentially adding repetitions of or
removing pre-existing events, without modifying the events
themselves.
-
class
vis.analyzers.indexers.offset.
FilterByOffsetIndexer
(score, settings=None)[source]¶ Bases:
vis.analyzers.indexer.Indexer
Indexer that regularizes the “offset” values of observations from other indexers.
The Indexer regularizes observations from offsets spaced any, possibly irregular,
quarterLength
durations apart, so they are instead observed at regular intervals. This has two effects:- events that do not begin at an observed offset will only be included in the output if no other event occurs before the next observed offset
- events that last for many observed offsets will be repeated for those offsets
Since elements’ durations are not recorded, the last observation in a Series will always be included in the results. If it does not start on an observed offset, it will be included as the next observed offset—again, whether or not this is true in the actual music. However, the last observation will only ever be counted once, even if a part ends before others in a piece with many parts. See the doctests for examples.
Examples:
For all, the
quarterLength
is1.0
.When events in the input already appear at intervals of
quarterLength
, input and output are identical.offset 0.0 1.0 2.0 input a
b
c
output a
b
c
When events in the input appear at intervals of
quarterLength
, but there are additional elements between the observed offsets, those additional elements are removed.offset 0.0 0.5 1.0 2.0 input a
A
b
c
output a
b
c
offset 0.0 0.25 0.5 1.0 2.0 input a
z
A
b
c
output a
b
c
When events in the input appear at intervals of
quarterLength
, but not at every observed offset, the event from the previous offset is repeated.offset 0.0 1.0 2.0 input a
c
output a
a
c
When events in the input appear at offsets other than those observed by the specified
quarterLength
, the “most recent” event will appear.offset 0.0 0.25 0.5 1.0 2.0 input a
z
A
c
output a
A
c
When the final event does not appear at an observed offset, it will be included in the output at the next offset that would be observed, even if this offset does not appear in the score file to which the results correspond.
offset 0.0 1.0 1.5 2.0 input a
b
d
output a
b
d
The behaviour in this last example can create a potentially misleading result for some analytic situations that consider meter. It avoids another potentially misleading situation where the final chord of a piece would appear to be dissonant because of a suspension. We chose to lose metric and rythmic precision, which would be more profitably analyzed with indexers built for that purpose. Consider this illustration, where the numbers correspond to scale degrees.
offset 410.0 411.0 411.5 412.0 in-S 2 1 in-A 7 5 in-T 4 ———– 3 in-B 5 1 out-S 2 1 1 out-A 7 5 5 out-T 4 4 3 out-B 5 1 1 If we left out the note event appear in the
in-A
part at offset411.5
, the piece would appear to end with a dissonant sonority!Concerning the “dynamic-offset method”, this can be accessed by passing the string “dynamic” for the quarterLength setting. This type of analysis is still experimental and comes with no guarantee that it will work accurately. It has the important known limitation that it only applies to Renaissance polyphony in which the contrapuntal rhythm is only ever in duple groupings. For more on contrapuntal rhythm, see:
- DeFord, Ruth. Tactus Mensuration, and Rhythm in Renaissance Music.
- Cambridge: Cambridge University Press, 2015.
For a more thorough explanation of the experimental dynamic-offset method see (especially chapter 4):
- Morgan, Alexander. “Renaissance Interval-Succession Theory: Treatises
- and Analysis.” PhD diss., McGill University, 2017.
Helper functions have been implemented to facilitate the use of the dynamic-offset method, so you can run analyses in the following way:
from vis.models.indexed_piece import Importer ip = Importer(‘full_path_to_piece_in_symbolic_notation.xml’) # assuming you want to apply the offset filter to the noterest # indexer results: nr = ip.get_data(‘noterest’) setts = {‘quarterLength’: ‘dynamic’} filtered_nr = ip.get_data(‘offset’, data=nr, settings=setts)
-
default_settings
= {'dom_data': [], 'method': 'ffill', 'mp': True}¶
-
possible_settings
= ['quarterLength', 'dom_data', 'method', 'mp']¶ A
list
of possible settings for theFilterByOffsetIndexer
.Parameters: - 'quarterLength' (float or string) – The quarterLength duration between observations desired in the output. This value must not have more than three digits to the right of the decimal (i.e. 0.001 is the smallest possible value). For dynamic (i.e. variable) and context-dependent value, pass the string ‘dynamic’.
- 'dom_data' – A list of DataFrames and one integer is required here if the ‘quarterLength’ setting is set to ‘dynamic’. This list should contain the dissonance, duration, beatstrength, and noterest indexer dataframes and finally the “highest_time” of the piece or movement in that order. The correct information is automatically fetched if this indexer is called on an IndexedPiece object via the get_data method() if the ‘data’ argument in that method is not passed.
- 'method' (str or None) – The value passed as the
method
kwarg toreindex()
. The default is'ffill'
, which fills in missing indices with the previous value. This is useful for vertical intervals, but not for horizontal, where you should useNone
instead. - 'mp' (boolean) – Multiprocesses when True (default) or processes serially when False.
Examples:
>>> from vis.models.indexed_piece import Importer >>> ip = Importer('path_to_piece.xml') >>> notes = ip.get_data('noterest') >>> setts = {'quarterLength': 2} >>> ip.get_data('offset', data=notes, settings=setts)
# Note that other analysis results can be passed to the offset # indexer too, such as the IntervalIndexer results as in the # following example. Also, the original column names (or names of # the series if a list of series was passed) are retained, though # the highest level of the columnar multi-index gets overwritten
>>> from vis.models.indexed_piece import Importer >>> ip = Importer('path_to_piece.xml') >>> intervals = ip.get_data('vertical_interval') >>> setts = {'quarterLength': 2} >>> ip.get_data('offset', data=intervals, settings=setts)
-
required_score_type
= 'pandas.Series'¶ The
FilterByOffsetIndexer
usespandas.Series
objects.
-
run
()[source]¶ Regularize the observed offsets for the Series input.
Returns: A DataFrame
with offset-indexed values for all inputted parts. The pandas indices (holding music21 offsets) start at the first offset at which there is an event in any of the inputted parts. An offset appears everyquarterLength
until the final offset, which is either the last observation in the piece (if it is divisible by thequarterLength
) or the next-highest value that is divisible byquarterLength
.Return type: pandas.DataFrame
over_bass
Module¶
-
class
vis.analyzers.indexers.over_bass.
OverBassIndexer
(score, settings=None)[source]¶ Bases:
vis.analyzers.indexer.Indexer
Using horizontal events and vertical intervals, this finds the intervals over the bass motion.
Call this indexer via the
get_data()
method of either anindexed_piece
object or anaggregated_pieces
object (see examples below). The DataFrames passed in the ‘score’ argument should be concatenatedParameters: - 'horizontal' (int) – The horizontal voice you wish to use as a bass. If not indicated, this is automatically assigned to the lowest voice.
- 'type' (str) – The type of horizontal event you wish to index.
The default is ‘notes’, which should be used if you are passing
in the results of the
NoteRestIndexer
. If you are passing in the results of theHorizontalIntervalIndexer
.
Example:
>>> from vis.models.indexed_piece import Importer >>> ip = Importer('path_to_piece.xml')
This example provides note names for the tracked voice (usually the bass voice):
>>> input_dfs = [ip.get_data('noterest'), ip.get_data('vertical_interval')] >>> ob_setts = {'type': 'notes'} >>> ip.get_data('over_bass', data=input_dfs, settings=ob_setts)
To use the OverBassIndexer with the melodic intervals of the tracked voice instead, do this:
>>> input_dfs = [ip.get_data('horizontal_interval'), ip.get_data('vertical_interval')] >>> ob_setts = {'type': 'intervals'} >>> ip.get_data('over_bass', data=input_dfs, settings=ob_setts)
-
possible_settings
= ['horizontal', 'type']¶
-
required_score_type
= 'pandas.DataFrame'¶
-
run
()[source]¶ Make a new index of the intervals over the bass motion.
Returns: A DataFrame
of the intervals over the bass.Return type: pandas.DataFrame
repeat
Module¶
Indexers that consider repetition in any way.
-
class
vis.analyzers.indexers.repeat.
FilterByRepeatIndexer
(score, settings=None)[source]¶ Bases:
vis.analyzers.indexer.Indexer
If the same event occurs many times in a row, remove all occurrences but the one with the lowest
offset
value (i.e., the “first” event).Because of how a
DataFrame
‘s index works, many of the events that would have been filtered will instead be replaced withnumpy.NaN
. Please be careful that the behaviour of this indexer matches your expectations.Example:
Prepare an indexed piece:
>>> from vis.models.indexed_piece import Importer >>> ip = Importer('path_to_piece.xml')
This example filters the repeats out of the
NoteRestIndexer
results, but any can be passed:>>> notes = ip.get_data('noterest') >>> ip.get_data('repeat', data=notes)
-
required_score_type
= 'pandas.Series'¶
-
run
()[source]¶ Make a new index of the piece, removing any event that is identical to the preceding.
Returns: A DataFrame
of the new indices.Return type: pandas.DataFrame
-
template
Module¶
Template for writing a new indexer. Use this class to help write a new
:class`Indexer` subclass. The TemplateIndexer
does nothing, and
should only be used by programmers.
Note
Follow these instructions to write a new Indexer
subclass:
- Replace my name with yours in the “codeauthor” directive above.
- Change the “Filename” and “Purpose” on lines 7 and 8.
- Modify the “Copyright” on line 10 or add an additional
- copyright line immediately below.
- Remove the
# pylint: disable=W0613
comment just before indexer_func()
.
- Remove the
- Rename the class.
- Adjust
required_score_type
. - Add settings to
possible_settings
and default_settings
, as required.
- Add settings to
- Rewrite the documentation for
__init__()
. - Rewrite the documentation for
run()
. - Rewrite the documentation for
indexer_func()
. - Write all relevant tests for
__init__()
, run()
, andindexer_func()
.
- Write all relevant tests for
- Follow the instructions in
__init__()
to write that method. - Follow the instructions in
run()
to write - that method.
- Follow the instructions in
- Write a new
indexer_func()
. - Ensure your tests pass, adding additional ones as required.
- Finally, run
pylint
with the VIS style rules.
-
class
vis.analyzers.indexers.template.
TemplateIndexer
(score, settings=None)[source]¶ Bases:
vis.analyzers.indexer.Indexer
Template for an
Indexer
subclass.-
default_settings
= {}¶ The default values for settings named in
possible_settings
. If a setting doesn’t have a value in this constant, then it must be specified to the constructor at runtime, or the constructor should raise aRuntimeException
.
-
possible_settings
= ['fake_setting']¶ This is a list of string that are the names of the settings used in this indexer. Specify the types and reasons for each setting as though it were an argument list, like this:
Parameters: 'fake_setting' (boolean) – This is the description of a fake setting.
-
required_score_type
= 'stream.Part'¶ Depending on how this indexer works, you must provide a
DataFrame
, aScore
, or list ofPart
orSeries
objects. Only choosePart
orSeries
if the input will always have single-integer part combinations (i.e., there are no combinations—it will be each part independently).
-
run
()[source]¶ Make a new index of the piece.
Returns: The new indices. Refer to the note below. Return type: pandas.DataFrame
or list ofpandas.Series
Important
Please be sure you read and understand the rules about return values in the full documentation for
run()
andmake_return()
.
-
-
vis.analyzers.indexers.template.
indexer_func
(obj)[source]¶ The function that indexes.
Parameters: obj (list of objects of the types stored in TemplateIndexer._types
) – The simultaneous event(s) to use when creating this index. (For indexers using aScore
).or
Parameters: obj ( pandas.Series
of strings) – The simultaneous event(s) to use when creating this index. (For indexers using aSeries
).Returns: The value to store for this index at this offset. Return type: str
windexer
Module¶
-
class
vis.analyzers.indexers.windexer.
Windexer
(score, settings=None)[source]¶ Bases:
vis.analyzers.indexer.Indexer
Indexer that creates new a new windowed version of other indexer results.
Parameters: 'window_size' (integer) – The size of the window of the DataFrame that you would like to look at. The default setting is 4. Example:
Prepare an indexed piece:
>>> from vis.models.indexed_piece import Importer >>> ip = Importer('path_to_piece.xml')
This example prepares “windows” of notes and rests, but any dataframe could be used:
>>> notes = ip.get_data('noterest') >>> setts = {'window_size': 4} >>> ip.get_data('windexer', data=notes, settings=setts)
-
default_settings
= {'window_size': 4}¶
-
possible_settings
= ['window_size']¶
-
required_score_type
= 'pandas.DataFrame'¶
-
run
()[source]¶ Make a new windowed index of the indexer results.
Returns: The new windowed DataFrame. Return type: pandas.DataFrame
-