(version 2022_01_24)
1. Abfrage von einfachen statistischen Angaben
Dieses Tutorial führt anhand von Notenbeispielen in die computergestützten Möglichkeiten einfacher statistischer Abfragen auf der Grundlage von CAMAT (Computer-Assisted Music Analysis Tool) ein.
Das Durcharbeiten und Nachvollziehen des Tutoriums soll es Ihnen ermöglichen, mit den vorgestellten computergestützten Methoden eigene Musikbeispiele (Notendateien) zu untersuchen.
Jeder Start eines Jupyter Notebooks beginnt mit dem Import einer Reihe von Python-Bibliotheken, die für die Analyse erforderlich sind:
import sys
import os
sys.path.append(os.getcwd().replace(os.path.join('music_xml_parser', 'ipynb'), ''))
import music_xml_parser as mp
from music21 import *
import csv
from IPython.display import HTML, display
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# Mit diesen Befehlen wird nicht nur der CAMAT music_xml_parser,
# sondern darüber hinaus die Bibliotheken 'numpy' und 'pandas' für statische Auswertungen,
# 'music21' sowieund 'matplotlib' für grafische Darstellungen geladen.
# Mit dem folgenden Befehl wird der Download von xml-Dateien aus dem Internet ermöglicht.
environment.set('autoDownload', 'allow')
# Mit den folgenden Befehlen wird die Formtierung für die Tabellen festgelegt,
# die weiter unten dargestellt werden - '9999' ist der Maximalwert:
pd.set_option('display.max_rows', 10)
pd.set_option('display.max_columns', 9999)
pd.set_option('display.width', 9999)
Anschließend müssen Sie die Datei laden, die Sie untersuchen wollen (aus dem Internet oder von Ihrer Festplatte bzw. aus dem Notenordner) und den xml-parser aktivieren. Dabei wird aus der xml-Datei ein neuer dataframe ('m_df') erzeugt, der den folgenden statistischen Abfragen zugrunde liegt (vgl. hierzu https://analyse.hfm-weimar.de/jupyter/CAMAT_Basics_Part1_Einfuehrung.html).
Wir wählen für unser Tutorial als Beispiel den ersten Satz aus dem Streichquartett KV. 171 von Wolfgang Amadeus Mozart (vgl. Basics Part 1).
xml_file = 'https://analyse.hfm-weimar.de/database/03/MoWo_K171_COM_1-4_StringQuar_003_00867.xml'
m_df = mp.parse.with_xml_file(file=xml_file,
save_file_name=None,
do_save=False)
# Sie können statt 'm_df' auch einen anderen Variablennamen vergeben.
# Dies ist insbesondere dann sinnvoll, wenn Sie parallel mit mehreren Notendateien arbeiten wollen.
# Zur Einstellung der beiden Parameter vgl. Sie bitte ebenfalls das Einführungs-Tutorial (Part1).
File at: ../music_xml_parser/data/xmls_to_parse/hfm_database/MoWo_K171_COM_1-4_StringQuar_003_00867.xml
# Wenn Sie die Dataframe-Tabelle in Ausschnitten im Browser (und nicht in der externen csv-Datei) anschauen wollen,
# so aktivieren Sie den Befehl 'm_df' durch Löschen der Raute-Taste.
# m_df
# Für eine komplette Ansicht nutzen Sie den folgenden Befehl:
# mp.utils.print_full_df(m_df)
# ACHTUNG: Dies kann sehr rechenintensiv sein!!
Wir beginnen mit einfachen statistischen Abfragen der Anzahl: der Stimmen, der Länge in Takten, der Anzahl der Töne (insgesamt und pro Stimmen) und dem Ambitus der einzelnen Stimmen.
Bitte öffnen Sie die Notendatei parallel in Ihrem Noteneditor (z.B. MuseScore).
Achtung: Da mit dem ersten Auswertungsbefehl die Daten erstmals eingelesen werden, braucht die Ausführung erfahrungsgemäß relativ lange (abhängig von Ihrem Computer und der Dateigröße bis zu mehreren Minuten). Alle späteren Befehle gehen dann aber sehr rasch!!
v = m_df[['PartID','PartName']].drop_duplicates().to_numpy()
mp.utils.display_table(data=v,
columns=['Part ID', 'Part Name'])
# In der ersten Befehlszeile werden aus der dataframe-Liste des xml-Parser ('m_df')
# die PartIDs und PartNames,
# also die IDs und Bezeichnungen der einzelnen Stimmen, abgefragt.
# Hierfür wird die Variable 'v' vergeben.
# In der zweiten Zeile werden die Spaltenüberschriften der Tabelle festgelegt.
# ACHTUNG: Ist in einer MusicXML-Datei keine Stimmbezeichnung festgelegt,
# so kann diese hier natürlich auch nicht dargestellt werden ('None')!
Part ID | Part Name |
---|---|
1 | Violino I |
2 | Violino II |
3 | Viola |
4 | Violoncello |
Abfrage der Stückänge in Takten:
m = m_df['Measure'].to_numpy(dtype=int)
max(m)
159
Abfrage der Anzahl der Töne per Stimme, wobei übergebundene Noten jeweils als eine Note gezählt werden:
n_notes, c_notes = np.unique(m_df['PartName'], return_counts=True)
data = [[i, c] for i, c in zip(n_notes, c_notes) ]
mp.utils.display_table(data=data,
columns=['Part Name', 'Notenanzahl'])
# Falls die Stimmen in der xml-Datei keine Bezeichnungen besitzen,
# so muss 'PartName' (und 'Part Name') durch 'PartID' ersetzt werden!
Part Name | Notenanzahl |
---|---|
Viola | 382 |
Violino I | 576 |
Violino II | 626 |
Violoncello | 385 |
Der Ambitus per Stimme wird in Halbtonschritten, also die Differenz zwischen dem niedrigsten (min) und höchsten (max) Ton; angegeben in MIDI-Werten mit c' = C4 = 60; c'' = C5 = 72 usw.:
ambitus = mp.analyse.ambitus(m_df,
output_as_midi=True)
# Durch den Parameter 'output_as_midi=False' werden die Töne mit Tonnamen angegeben.
# Der folgende Befehl legt die Output-Tabelle fest:
mp.utils.display_table(data=ambitus,
columns=['Part ID', 'PartName', 'min', 'max', 'Ambitus'])
# In der letzten Befehlszeile werden die Tabellenspalten benannt.
# Sie können dort beliebig umbenannt werden.
Part ID | PartName | min | max | Ambitus |
---|---|---|---|---|
1 | Violino I | 57 | 87 | 30 |
2 | Violino II | 55 | 82 | 27 |
3 | Viola | 48 | 75 | 27 |
4 | Violoncello | 39 | 67 | 28 |
# Durch den Zusatz 'output_as_midi=False' werden die Töne mit Tonnamen angegeben.
ambitus = mp.analyse.ambitus(m_df,
output_as_midi=False)
mp.utils.display_table(data=ambitus,
columns=['Part ID', 'PartName', 'min', 'max', 'Ambitus'])
Part ID | PartName | min | max | Ambitus |
---|---|---|---|---|
1 | Violino I | A3 | D#6 | 30 |
2 | Violino II | G3 | A#5 | 27 |
3 | Viola | C3 | D#5 | 27 |
4 | Violoncello | D#2 | G4 | 28 |
Zur Charakterisierung einzelner Musikstücke und zum Vergleich zwischen verschiedenen Musikstücken kann es sinnvoll sein, die Häufigkeit bestimmter Elemente (Tonhöhen, Notendauern etc.) zu bestimmen. Für solche Fragen lassen sich Häufigkeitstabellen und grafische Darstellungen, sog. Histogramme, erstellen.
Welche Töne tauchen wie häufig auf? Wie diatonisch ist der Tonraum der Komposition, wie viele zusätzliche chromatische Töne tauchen auf?
pitch_hist = mp.analyse.pitch_histogram(m_df,
do_plot=True,
visulize_midi_range=None)
# Mit der ersten Zeile wird aus 'm_df' eine Histogrammdarstellung mit dem Namen 'pitch_hist' erzeugt.
# Durch den Parameter 'do_plot=True' wird die Grafik dargestellt.
# Durch 'visulize_midi_range=None' wird eine Tabellendarstellung (s. unten) verhindert.
Was können wir erkennen?
Mozart verwendet in der Komposition anscheinend hauptsächlich Töne der Es-Dur-Tonleiter (es=D#, as=G#, b=a# usw.) und kaum chromatische Töne.
Tipp: Die Grafik lässt sich auch in einem externen Pop-Up-Fenster des Programms Matplotlib darstellen und dort weiterverarbeiten, vergrößern, umformatieren und speichern etc. Dazu muss dem Code der Befehl '%matplotlib' vorangestellt werden. Anschließend muss Matplotlib durch den Befehl '%matplotlib inline' wieder ausgeschaltet werden. Ansonsten werden alle folgenden Grafiken ebenfalls extern dargestellt.
Wir wollen nun genau wissen, wie häufig die einzelnen Töne auftauchen!
# Die Häufigkeitstabelle wird erzeugt, wenn wir die Parameter umstellen:
# 'do_plot=None'
pitch_hist = mp.analyse.pitch_histogram(m_df,
do_plot=None,
do_plot_full_axis=True,
visulize_midi_range=None,
filter_dict=None,
enharmonic=True)
mp.utils.display_table(data=pitch_hist,
columns=['MIDI', 'Pitch', 'Octave', 'Occurences'])
# Mit dem zweiten Befehl wird die Tabelle dargestellt und beschriftet.
MIDI | Pitch | Octave | Occurences |
---|---|---|---|
39 | E-1 | 2 | 7 |
41 | F | 2 | 7 |
43 | G | 2 | 9 |
44 | A-1 | 2 | 13 |
45 | A0 | 2 | 6 |
46 | B-1 | 2 | 51 |
47 | B0 | 2 | 4 |
48 | C | 3 | 22 |
49 | D-1 | 3 | 2 |
50 | D | 3 | 25 |
51 | E-1 | 3 | 59 |
53 | F | 3 | 46 |
55 | G | 3 | 45 |
56 | A-1 | 3 | 39 |
57 | A0 | 3 | 35 |
57 | A | 3 | 2 |
58 | B-1 | 3 | 121 |
59 | B0 | 3 | 11 |
60 | C | 4 | 70 |
62 | D | 4 | 98 |
63 | E-1 | 4 | 174 |
65 | F | 4 | 119 |
66 | F1 | 4 | 2 |
67 | G | 4 | 84 |
68 | A-1 | 4 | 73 |
69 | A0 | 4 | 30 |
70 | B-1 | 4 | 101 |
71 | B0 | 4 | 2 |
72 | C | 5 | 53 |
74 | D | 5 | 54 |
75 | E-1 | 5 | 88 |
76 | E0 | 5 | 1 |
77 | F | 5 | 42 |
78 | F1 | 5 | 2 |
79 | G | 5 | 34 |
80 | A-1 | 5 | 25 |
81 | A0 | 5 | 13 |
82 | B-1 | 5 | 29 |
83 | B0 | 5 | 1 |
84 | C | 6 | 5 |
86 | D | 6 | 5 |
87 | E-1 | 6 | 7 |
In der Tabelle werden auch die jeweiligen Vorzeichen in der Partitur angezeigt. Dies kann sinnvoll sein, wenn z.B. in entfernte Tonarten moduliert wird. Dabei steht -1 für ein b-Vorzeichen, 1 für ein #-Vorzeichen, 0 für ein Auflösungszeichen, -2 für ein doppeltes bb usw. Das bedeutet:
Achten Sie bitte auf das A3: Midi-Pitch 57 taucht in zwei Zeilen auf, da das A 35mal mit Auflösungszeichen in der Partitur auftritt, zwei Mal jedoch ohne Auflösungszeichen (vermutlich später im Takt eines bereits aufgelösten Taktes).
Sind mir diese Angaben zu differenziert, so kann ich sie im Zusatz 'enharmonic=False' ausschalten. Nun wird Es zu Dis (D#), As zu Gis (G#) usw. Zusätzlich muss ich die Spaltennamen der Tabelle nun neu benennen, da nun Tonhöhe und Oktavlage in eine Spalte zusammengezogen werden:
pitch_hist = mp.analyse.pitch_histogram(m_df,
do_plot=None,
do_plot_full_axis=True,
visulize_midi_range=None,
filter_dict=None,
enharmonic=False)
mp.utils.display_table(data=pitch_hist,
columns=['MIDI', 'Pitch', 'Occurences'])
# Mit dem zweiten Befehl wird die Tabelle neu dargestellt und beschriftet.
MIDI | Pitch | Occurences |
---|---|---|
39 | D#2 | 7 |
41 | F2 | 7 |
43 | G2 | 9 |
44 | G#2 | 13 |
45 | A2 | 6 |
46 | A#2 | 51 |
47 | B2 | 4 |
48 | C3 | 22 |
49 | C#3 | 2 |
50 | D3 | 25 |
51 | D#3 | 59 |
53 | F3 | 46 |
55 | G3 | 45 |
56 | G#3 | 39 |
57 | A3 | 37 |
58 | A#3 | 121 |
59 | B3 | 11 |
60 | C4 | 70 |
62 | D4 | 98 |
63 | D#4 | 174 |
65 | F4 | 119 |
66 | F#4 | 2 |
67 | G4 | 84 |
68 | G#4 | 73 |
69 | A4 | 30 |
70 | A#4 | 101 |
71 | B4 | 2 |
72 | C5 | 53 |
74 | D5 | 54 |
75 | D#5 | 88 |
76 | E5 | 1 |
77 | F5 | 42 |
78 | F#5 | 2 |
79 | G5 | 34 |
80 | G#5 | 25 |
81 | A5 | 13 |
82 | A#5 | 29 |
83 | B5 | 1 |
84 | C6 | 5 |
86 | D6 | 5 |
87 | D#6 | 7 |
Nun taucht A3 nur in einer Spalte auf, und zwar mit der Häufigkeit 37.
Durch den folgenden Befehl wird die Liste der Tonhöhenhäufigkeiten als csv-Datei exportiert (csv = comma separated variables; lesbar und weiterverarbeitbar u.a. in Excel oder im Text-Editor). Durch den Export lassen sich Tabellen zum Stückvergleich und zur Korpusanalyse generieren. Die csv-Datei wird Export-Ordner gespeichert und kann mit einem Texteditor oder einem Tabellenkalkulationsprogramm (z.B. Excel) geöffnet werden.
mp.utils.export_as_csv(data=pitch_hist,
columns=['MIDI','Pitch','Occurrences'],
save_file_name ='pitch_histogram.csv', # auch andere Dateinamen sind möglich
do_save=True, # Befehl für die Speicherung
do_print=False, # bei 'True' wird die Datei nochmals im Browser angezeigt
sep=';', # als Trennzeichen wird ein Semikolon verwendet
header=True) # die Überschriften ('header') der Spalten werden angezeigt
# Die Datei pitch_histogram.csv wird automatisch im Ordner music_xml_parser\data\exports\ gespeichert.
# Wenn Sie in einem anderen Ordner abspeichern wollen,
# so müssen Sie unter save_file_name einen Pfad eingeben (z.B. 'C:/pitch_histogram.csv')
Es gibt zwei Möglichkeiten, die grafische Darstellung ein wenig übersichtlicher zu gestalten: Einerseits kann die Darstellung auf einen bestimmten Tonhöhenbereich eingeschränkt werden. Andererseits können nur jene Tonhöhen ausgewählt werden, die tatsächlich auftreten.
ph = mp.analyse.pitch_histogram(m_df,
do_plot=True,
visulize_midi_range=[50, 90])
# Durch den Zusatz 'visulize_midi_range=[50, 90]' wird der dargestellte Ausschnitt
# auf den Bereich zwischen MIDI-Pitch 50 (= D3) und 90 (= F6) eingegrenzt.
ph2 = mp.analyse.pitch_histogram(m_df,
do_plot=True,
visulize_midi_range=None,
do_plot_full_axis=False)
# Durch den Zusatz 'do_plot_full_axis=False,' werden bei der Grafik
# nur die Häufigkeiten von Tönen angezeigt, die auch tatsächlich auftauchen.
# Alle anderen Töne werden auf der x-Achse gelöscht.
Für harmonische Analysen ist es viel übersichtlicher, nicht die einzelnen Tonhöhen, sondern diese zu Tonhöhenklassen ('pitch class') zusammenzufassen.
pitchclass_hist = mp.analyse.pitch_class_histogram(m_df,
do_plot=True)
Nun ist auf einen Blick zu erkennen: Mozart verwendet fast ausschließlich die Töne der Es-Dur Tonleiter - mit einer interessanten Ausnahme: Der Tritonus a taucht relativ häufig auf!
Womit könnte dies zusammenhängen? Um diese Frage zu beantworten, müssen Sie natürlich in die Partitur schauen und dort die Verwendungsweisen des Tons a überprüfen. Könnte es mit dem Einsatz von Doppeldominanten (F-Dur) zu tun haben?
Ähnlich wie bei den Tonhöhen (s. Abschnitt 2.1) lassen sich auch bei den Tonhöhenklassen Tabellen anzeigen. Die Häufigkeitstabelle wird automatisch erzeugt, sobald im Befehl den Plot-Parameter ('do_plot=None') ausgestellt wird:
pitchclass_hist = mp.analyse.pitch_class_histogram(m_df,
do_plot=None)
mp.utils.display_table(data=pitchclass_hist,
columns=['Tonhöhenklasse','Häufigkeit'])
# Mit dem zweiten Befehl wird die Tabelle dargestellt und beschriftet.
# Sie können die Beschriftung selbst festlegen ('rote Ausdrücke')
Tonhöhenklasse | Häufigkeit |
---|---|
C | 150 |
C# | 2 |
D | 182 |
D# | 335 |
E | 1 |
F | 214 |
F# | 4 |
G | 172 |
G# | 150 |
A | 86 |
A# | 302 |
B | 18 |
# Die Häufigkeiten der Tonhöhenklassen werden mit dem folgenden Befehl als csv-Datei exportiert:
mp.utils.export_as_csv(data=pitchclass_hist,
columns=['Pitch Class','Occurrences'],
save_file_name ='pitch_class_hist.csv',
do_save=False,
do_print=None,
sep=';',
header=True)
# (Zu den Parametern vgl. oben, 2.1)
Wie häufig kommen bestimmte Intervallschritte in den einzelnen Stimmen vor? Haben alle Stimmen einen ähnlichen Intervallverlauf – oder gibt es z.B. in den Unterstimmen mehr Sprünge, in der Melodiestimme mehr Schritte?
Schauen wir uns zunächst die Intervallverteilung in der ersten Violine an.
v = m_df[['PartID','PartName']].drop_duplicates().to_numpy()
print(v)
# Durch diesen Befehl werden zunächst die Stimmen und ihre Bezeichnung angezeigt.
[['1' 'Violino I'] ['2' 'Violino II'] ['3' 'Viola'] ['4' 'Violoncello']]
# Nun wählen wir die erste Stimme (mit dem PartID=1) aus: 'part='1'
interval_hist = mp.analyse.interval(m_df,
part='1',
do_plot=True)
Die erste Violine schreitet vorwiegend in Sekunden, Terzen und Quarten voran, wobei absteigende Schritte häufiger sind als aufsteigende. Größere Intervalle kommen auch vor, sind aber viel seltener.
# Mit dem folgenden Befehl wird die Verteilung der Intervallhäufigkeiten
# als Tabelle angezeigt (nur falls 'do_print=True')
# und die Tabelle in die Datei 'interval_1.csv' exportiert (nur falls 'do_save=True').
mp.utils.export_as_csv(data=interval_hist,
columns=['Intervall', 'Häufigkeit'],
save_file_name ='interval_1.csv',
do_save=True,
do_print=False,
do_return_pd=False,
sep=';',
index=False,
header=True)
Wie steht es nun um die Cello-Stimme?
Dazu müssen Sie an der richtigen Stelle einfach die '1' durch eine '4' ersetzen...
Kopieren Sie den gesamten Befehl einfach in eine neue Code Cell und passen Sie die Stimmenauswhal an:
# Hier der angepasste Befehl:
interval_hist = mp.analyse.interval(m_df,
part='4',
do_plot=True)
Es fällt auf, dass Quarten aufwärts (5), Sekunden aufwärts (2) sowie Quinten abwärts (-7) recht häufig vorkommen. Vielleicht ist dies sogar eine typische der Grundtöne, die sich harmonisch interpretieren lässt?
Nun wenden wir uns der rhythmischen Gestaltung zu: Welche Dauernwerte werden in der Komposition verwendet und wie häufig kommen diese jeweils vor?
In der folgenden Auswertung erhält die Viertelnote den Wert 1. Kürzere und längere Notenwerte werden entsprechend als Vielfache bzw. Teiler von 1 benannt.
quarter_dur_hist = mp.analyse.quarterlength_duration_histogram(m_df,
do_plot=True)
Wie erwartet verwendet Mozart vorwiegend Viertelnoten (1) und kleinere Notenwerte (<1). Es kommen aber auch ein paar längere Noten vor. Wenn wir die jeweilige Anzahl genau wissen wollen und uns zudem der Bereich <1 genauer interessiert, müssen wir uns wieder die Häufigkeitstabelle anzeigen lassen:
quarter_dur_hist = mp.analyse.quarterlength_duration_histogram(m_df,
do_plot=False)
# Bei 'do_plot=False' oder 'do_plot=None' wird nur die Tabelle angezeigt.
# Die Tabelle wird durch den folgenden Befehl erzeugt und beschriftet:
mp.utils.display_table(data=quarter_dur_hist,
columns=['Dauernklasse','Häufigkeit'])
Dauernklasse | Häufigkeit |
---|---|
0.125 | 38 |
0.250 | 202 |
0.375 | 4 |
0.500 | 497 |
0.750 | 22 |
1.000 | 694 |
1.500 | 30 |
2.000 | 51 |
3.000 | 42 |
4.000 | 15 |
5.000 | 1 |
6.000 | 18 |
7.000 | 1 |
10.000 | 1 |
Zur ERläuterung der Werte: Es handelt sich jeweils um Vielfache oder TEiler einer Viertelnote (=1). Also:
# Hier noch der Befehl zum Abspeichern der Tabelle:
mp.utils.export_as_csv(data=quarter_dur_hist,
columns=['Dauernwerte', 'Häufigkeiten'],
save_file_name ='quarter_duration_hist.csv',
do_save=True,
do_print=False,
sep=';',
index=False,
header=True)
Wie deutlich wird das Metrum in den einzelnen Stimmen einer Komposition artikuliert – durch die Platzierung der Töne auf Taktanfängen und den metrisch wichtigen Positionen innerhalb des Taktes (z.B. der Taktmitte oder auf den Viertelpositionen)? Hierzu lässt sich in eine Liste der Häufigkeiten von Tönen auf den verschiedenen metrischen Positionen in den einzelnen Stimmen anzeigen.
Ein solches Profil hat natürlich zur Voraussetzung, dass das untersuchte Stück in einem einzigen Metrum steht und keine Taktwechsel hat. Das überprüft man mit dem folgenden Befehl:
ts_hist = mp.analyse.time_signature_histogram(m_df,
do_plot=False)
mp.utils.display_table(data=ts_hist,
columns=['Taktart', 'Anzahl'])
Taktart | Anzahl |
---|---|
4/4 | 31 |
3/4 | 128 |
In unserem Mozart-Satz tauchen sowohl 4/4-Takte als auch 3/4-Takte auf, wobei der 3/4-Takt sogar überwiegt - obwohl das Stück ja im 4/4-Takt beginnt. Mit dem folgenden Befehl werden daher zwei unterschiedliche metrische Profile erzeugt - einmal für die 4/4-Takte, einmal für die 3/4-Takte.
mp_ts_dict_2d = mp.analyse.metric_profile_split_time_signature(m_df,
do_plot=True)
# Der Befehl für die Tabellenanzeige (do_print=True)
# und den csv-Export (do_save=True) sieht zwar etwas kompliziert aus,
# leistet jedoch dasselbe wie immer:
for k2 in mp_ts_dict_2d.keys():
print(f"Time Signature {k2}")
saveas = 'metric_profile_ts_'+k2.replace('/','-')+'.csv'
mp.utils.export_as_csv(data=mp_ts_dict_2d[k2],
columns=['Metric Profile','Occurrences'],
save_file_name ='metric_profile_hist.csv',
do_save=False,
do_print=True,
do_return_pd=False,
sep=';',
index=False,
header=True)
Time Signature 3/4
Metric Profile | Occurrences |
---|---|
1.00 | 344 |
1.50 | 55 |
1.75 | 18 |
1.88 | 13 |
2.00 | 227 |
2.50 | 78 |
3.00 | 258 |
3.25 | 3 |
3.50 | 65 |
3.75 | 9 |
3.88 | 4 |
Time Signature 4/4
Metric Profile | Occurrences |
---|---|
1.00 | 85 |
1.25 | 12 |
1.38 | 4 |
1.50 | 27 |
1.75 | 12 |
2.00 | 71 |
2.25 | 12 |
2.50 | 46 |
2.75 | 12 |
3.00 | 91 |
3.25 | 16 |
3.50 | 29 |
3.75 | 13 |
4.00 | 52 |
4.25 | 13 |
4.50 | 34 |
4.75 | 13 |
Wir haben uns bereits die Häufigkeiten von Tonhöhen bzw. Tonhöhenklassen angeschaut. Nun könnte man sagen: Längere Töne haben natürlich mehr Gewicht als kurze Töne oder Töne zwischen den Zählzeiten. Diesen Gedanken können wir weiterverfolgen, indem wir uns kombinierte, ‚doppelte‘ oder ‚bivariate‘ Häufigkeitsverteilungen: Also z.B. die Häufigkeiten der Tonhöhen für jeweils unterschiedliche Dauernwerte oder die Häufigkeiten der Tonhöhenklasse für die verschiedenen metrischen Positionen. Hierum soll es im Folgenden anhand von zwei Beispielen gehen.
Beispiel 1: Dauern pro Tonhöhenklassen. Gibt es Unterschiede bei den Dauernwerten bzgl. der verschiedenen Tonhöhenklassen?
Beispiel 2: Tonhöhen auf metrischen Positionen. Gibt es Unterschiede bei verschiedenen Positionen im Takt bzgl. der verschiedenen Tonhöhenklassen?
Mit dem folgenden Befehl wird eine sog. 3D-Grafik erzeugt, bei der die Häufigkeiten von Daunerwerte pro Tonhöhenklasse angezeigt wird. Sowohl die Höhe als auch die Farbe der Säulen steht für die jeweilige Häufigkeit (von blau=sehr selten über grün und gelb bis rot=sehr häufig):
dur_hist = mp.analyse.quarterlength_duration_histogram(m_df,
plot_with='PitchClass',
do_plot=True)
Da die Zuordnung der Balken zu den Notenwerten ein wenig unübersichtlich ist (die Zahlen beziehen sich der Reihe nach auf die Felder), lassen wir uns mit dem folgenden Befehl die entsprechende Häufigkeitstabelle anzeigen:
dur_hist = mp.analyse.quarterlength_duration_histogram(m_df,
plot_with='PitchClass',
do_plot=False)
mp.utils.export_as_csv(data=dur_hist,
columns=['Tonhöhenklasse', 'Dauernwert', 'Anzahl'],
save_file_name='QuaterLength.csv',
do_save=False, # Bei =True wird ein csv-Datei gespeichert.
do_print=True, # Bei =True wird die Tabelle angezeigt, bei =False nicht.
do_return_pd=False,
sep=';',
index=False,
header=True)
Tonhöhenklasse | Dauernwert | Anzahl |
---|---|---|
C | 0.125 | 5 |
C | 0.25 | 16 |
C | 0.5 | 38 |
C | 0.75 | 4 |
C | 1.0 | 67 |
C | 1.5 | 6 |
C | 2.0 | 7 |
C | 3.0 | 3 |
C | 4.0 | 4 |
C# | 0.5 | 2 |
A# | 0.125 | 4 |
A# | 0.25 | 32 |
A# | 0.5 | 91 |
A# | 0.75 | 3 |
A# | 1.0 | 135 |
A# | 1.5 | 7 |
A# | 10.0 | 1 |
A# | 2.0 | 11 |
A# | 3.0 | 10 |
A# | 4.0 | 2 |
A# | 6.0 | 5 |
A# | 7.0 | 1 |
B | 0.5 | 2 |
B | 1.0 | 16 |
D | 0.125 | 4 |
D | 0.25 | 19 |
D | 0.5 | 69 |
D | 0.75 | 4 |
D | 1.0 | 79 |
D | 2.0 | 2 |
D | 3.0 | 5 |
D# | 0.125 | 5 |
D# | 0.25 | 41 |
D# | 0.375 | 2 |
D# | 0.5 | 100 |
D# | 0.75 | 2 |
D# | 1.0 | 144 |
D# | 1.5 | 7 |
D# | 2.0 | 13 |
D# | 3.0 | 15 |
D# | 4.0 | 2 |
D# | 5.0 | 1 |
D# | 6.0 | 3 |
E | 0.5 | 1 |
F | 0.125 | 8 |
F | 0.25 | 33 |
F | 0.5 | 63 |
F | 0.75 | 3 |
F | 1.0 | 88 |
F | 1.5 | 6 |
F | 2.0 | 7 |
F | 3.0 | 4 |
F | 4.0 | 2 |
F# | 1.0 | 4 |
G | 0.125 | 6 |
G | 0.25 | 28 |
G | 0.5 | 53 |
G | 0.75 | 3 |
G | 1.0 | 67 |
G | 1.5 | 2 |
G | 2.0 | 1 |
G | 3.0 | 5 |
G | 4.0 | 4 |
G | 6.0 | 3 |
G# | 0.125 | 2 |
G# | 0.25 | 29 |
G# | 0.375 | 2 |
G# | 0.5 | 46 |
G# | 0.75 | 1 |
G# | 1.0 | 51 |
G# | 1.5 | 2 |
G# | 2.0 | 9 |
G# | 4.0 | 1 |
G# | 6.0 | 7 |
A | 0.125 | 4 |
A | 0.25 | 4 |
A | 0.5 | 32 |
A | 0.75 | 2 |
A | 1.0 | 43 |
A | 2.0 | 1 |
Nicht sehr überraschend ist, dass Grundton Es (=D#) und Quinte B (=A#) vor allem als Achtelnoten (0.5) und Viertelnoten (1.0) vorkommen. Denn das sind ja auch die häufigsten Dauernwerte!
Die Darstellung kann von Tonhöhenklassen auf Tonhöhen umgestellt werden, indem beim Parameter plot_with='Pitch' (in einfachen Anführungszeichen) gewählt wird. Nun wird die 3D-Grafik allerdings noch ein wenig unübersichtlicher...
dur_pitch_hist = mp.analyse.quarterlength_duration_histogram(m_df,
plot_with='Pitch',
do_plot=True)
# Für die Tabellendarstellung sowie zum Datenexport (vgl. oben)
# Hier muss dann die Bezeichung (colums= ) von 'Tonhöhenklasse' (PitchClass) in 'Tonhöhe' (Pitch) geändert werden.
dur_pitch_hist = mp.analyse.quarterlength_duration_histogram(m_df,
plot_with='Pitch',
do_plot=False)
mp.utils.export_as_csv(data=dur_pitch_hist,
columns=['Tonhöhe','Dauernwert', 'Anzahl'],
save_file_name='QuaterLength.csv',
do_save=False,
do_print=True,
do_return_pd=False,
sep=';',
index=False,
header=True)
Tonhöhe | Dauernwert | Anzahl |
---|---|---|
D#2 | 0.5 | 2 |
D#2 | 1.0 | 4 |
D#2 | 3.0 | 1 |
F2 | 0.5 | 2 |
F2 | 1.0 | 4 |
F2 | 2.0 | 1 |
G2 | 0.5 | 2 |
G2 | 1.0 | 6 |
G2 | 3.0 | 1 |
G#2 | 0.5 | 3 |
G#2 | 1.0 | 8 |
G#2 | 1.5 | 1 |
G#2 | 6.0 | 1 |
A2 | 1.0 | 6 |
A#2 | 0.5 | 18 |
A#2 | 1.0 | 27 |
A#2 | 2.0 | 2 |
A#2 | 3.0 | 4 |
B2 | 0.5 | 1 |
B2 | 1.0 | 3 |
C3 | 0.5 | 7 |
C3 | 1.0 | 12 |
C3 | 2.0 | 2 |
C3 | 4.0 | 1 |
C#3 | 0.5 | 2 |
D3 | 0.5 | 4 |
D3 | 1.0 | 18 |
D3 | 2.0 | 1 |
D3 | 3.0 | 2 |
D#3 | 0.5 | 8 |
D#3 | 1.0 | 39 |
D#3 | 1.5 | 2 |
D#3 | 2.0 | 1 |
D#3 | 3.0 | 8 |
D#3 | 5.0 | 1 |
F3 | 0.5 | 11 |
F3 | 1.0 | 28 |
F3 | 1.5 | 2 |
F3 | 2.0 | 2 |
F3 | 3.0 | 1 |
F3 | 4.0 | 2 |
G3 | 0.25 | 2 |
G3 | 0.5 | 11 |
G3 | 1.0 | 25 |
G3 | 1.5 | 1 |
G3 | 3.0 | 4 |
G3 | 4.0 | 2 |
G#3 | 0.25 | 5 |
G#3 | 0.5 | 11 |
G#3 | 1.0 | 15 |
G#3 | 1.5 | 1 |
G#3 | 2.0 | 3 |
G#3 | 4.0 | 1 |
G#3 | 6.0 | 3 |
A3 | 0.25 | 4 |
A3 | 0.5 | 5 |
A3 | 1.0 | 27 |
A3 | 2.0 | 1 |
A#3 | 0.25 | 15 |
A#3 | 0.5 | 9 |
A#3 | 1.0 | 72 |
A#3 | 1.5 | 4 |
A#3 | 2.0 | 7 |
A#3 | 3.0 | 6 |
A#3 | 4.0 | 1 |
A#3 | 6.0 | 5 |
A#3 | 7.0 | 1 |
A#3 | 10.0 | 1 |
B3 | 0.5 | 1 |
B3 | 1.0 | 10 |
C4 | 0.25 | 11 |
C4 | 0.5 | 12 |
C4 | 0.75 | 2 |
C4 | 1.0 | 36 |
C4 | 1.5 | 3 |
C4 | 2.0 | 1 |
C4 | 3.0 | 3 |
C4 | 4.0 | 2 |
D4 | 0.25 | 19 |
D4 | 0.5 | 27 |
D4 | 0.75 | 2 |
D4 | 1.0 | 46 |
D4 | 2.0 | 1 |
D4 | 3.0 | 3 |
D#4 | 0.25 | 37 |
D#4 | 0.5 | 41 |
D#4 | 1.0 | 74 |
D#4 | 1.5 | 4 |
D#4 | 2.0 | 7 |
D#4 | 3.0 | 6 |
D#4 | 4.0 | 2 |
D#4 | 6.0 | 3 |
F4 | 0.25 | 29 |
F4 | 0.5 | 30 |
F4 | 1.0 | 50 |
F4 | 1.5 | 3 |
F4 | 2.0 | 4 |
F4 | 3.0 | 3 |
F#4 | 1.0 | 2 |
G4 | 0.125 | 2 |
G4 | 0.25 | 23 |
G4 | 0.5 | 27 |
G4 | 1.0 | 25 |
G4 | 1.5 | 1 |
G4 | 2.0 | 1 |
G4 | 4.0 | 2 |
G4 | 6.0 | 3 |
G#4 | 0.125 | 1 |
G#4 | 0.25 | 20 |
G#4 | 0.5 | 21 |
G#4 | 0.75 | 1 |
G#4 | 1.0 | 23 |
G#4 | 2.0 | 4 |
G#4 | 6.0 | 3 |
A4 | 0.125 | 3 |
A4 | 0.5 | 18 |
A4 | 0.75 | 1 |
A4 | 1.0 | 8 |
A#4 | 0.125 | 3 |
A#4 | 0.25 | 14 |
A#4 | 0.5 | 45 |
A#4 | 0.75 | 2 |
A#4 | 1.0 | 31 |
A#4 | 1.5 | 3 |
A#4 | 2.0 | 2 |
A#4 | 4.0 | 1 |
B4 | 1.0 | 2 |
C5 | 0.125 | 5 |
C5 | 0.25 | 5 |
C5 | 0.5 | 18 |
C5 | 0.75 | 2 |
C5 | 1.0 | 18 |
C5 | 1.5 | 3 |
C5 | 2.0 | 1 |
C5 | 4.0 | 1 |
D5 | 0.125 | 4 |
D5 | 0.5 | 34 |
D5 | 0.75 | 2 |
D5 | 1.0 | 14 |
D#5 | 0.125 | 5 |
D#5 | 0.25 | 4 |
D#5 | 0.375 | 2 |
D#5 | 0.5 | 43 |
D#5 | 0.75 | 2 |
D#5 | 1.0 | 26 |
D#5 | 1.5 | 1 |
D#5 | 2.0 | 5 |
E5 | 0.5 | 1 |
F5 | 0.125 | 8 |
F5 | 0.25 | 4 |
F5 | 0.5 | 20 |
F5 | 0.75 | 3 |
F5 | 1.0 | 6 |
F5 | 1.5 | 1 |
F#5 | 1.0 | 2 |
G5 | 0.125 | 4 |
G5 | 0.25 | 3 |
G5 | 0.5 | 13 |
G5 | 0.75 | 3 |
G5 | 1.0 | 11 |
G#5 | 0.125 | 1 |
G#5 | 0.25 | 4 |
G#5 | 0.375 | 2 |
G#5 | 0.5 | 11 |
G#5 | 1.0 | 5 |
G#5 | 2.0 | 2 |
A5 | 0.125 | 1 |
A5 | 0.5 | 9 |
A5 | 0.75 | 1 |
A5 | 1.0 | 2 |
A#5 | 0.125 | 1 |
A#5 | 0.25 | 3 |
A#5 | 0.5 | 19 |
A#5 | 0.75 | 1 |
A#5 | 1.0 | 5 |
B5 | 1.0 | 1 |
C6 | 0.5 | 1 |
C6 | 1.0 | 1 |
C6 | 2.0 | 3 |
D6 | 0.5 | 4 |
D6 | 1.0 | 1 |
D#6 | 0.5 | 6 |
D#6 | 1.0 | 1 |
# Bei der externen Darstellung kann die Grafik gedreht und vergrößert werden.
# Bitte dazu die # vor dem Befehl löschen:
# %matplotlib
dur_p_hist = mp.analyse.quarterlength_duration_histogram(m_df,
plot_with='Pitch',
do_plot=True)
Using matplotlib backend: Qt5Agg
# Ausschalten der externen Darstellung wählen Sie bitte den folgenden Befehl:
%matplotlib inline
Nun zur Frage: auf welchen Positionen im Takt stehen welche Tonhöhenklassen? Mit folgendem Befehl erzeugen wird die entsprechende 3D-Grafik.
mp_p_hist = mp.analyse.metric_profile(m_df,
plot_with='PitchClass',
do_plot=True)
# Hier die entsprechende Tabelle mit der üblichen Export-Funktion.
mp_p_hist = mp.analyse.metric_profile(m_df,
plot_with='PitchClass',
do_plot=False)
mp.utils.export_as_csv(data=mp_p_hist,
columns=['Tonhöhenklasse', 'Metrische Position', 'Anzahl'],
save_file_name ='metric_profile_hist.csv',
do_save=True,
do_print=True,
do_return_pd=False,
sep=';',
index=False,
header=True)
Tonhöhenklasse | Metrische Position | Anzahl |
---|---|---|
C | 0.0 | 48 |
C | 0.375 | 2 |
C | 0.5 | 4 |
C | 0.75 | 2 |
C | 0.875 | 1 |
C | 1.0 | 23 |
C | 1.25 | 3 |
C | 1.5 | 13 |
C | 2.0 | 33 |
C | 2.5 | 9 |
C | 3.0 | 3 |
C | 3.25 | 2 |
C | 3.5 | 7 |
C# | 0.0 | 1 |
C# | 1.0 | 1 |
D | 0.0 | 34 |
D | 0.25 | 1 |
D | 0.5 | 11 |
D | 0.75 | 2 |
D | 0.875 | 2 |
D | 1.0 | 42 |
D | 1.25 | 1 |
D | 1.5 | 14 |
D | 1.75 | 3 |
D | 2.0 | 46 |
D | 2.5 | 12 |
D | 3.0 | 6 |
D | 3.25 | 4 |
D | 3.5 | 4 |
D# | 0.0 | 108 |
D# | 0.25 | 4 |
D# | 0.5 | 21 |
D# | 0.75 | 8 |
D# | 0.875 | 2 |
D# | 1.0 | 47 |
D# | 1.5 | 22 |
D# | 1.75 | 1 |
D# | 2.0 | 65 |
D# | 2.25 | 6 |
D# | 2.5 | 24 |
D# | 2.75 | 7 |
D# | 3.0 | 11 |
D# | 3.5 | 2 |
D# | 3.75 | 7 |
E | 1.0 | 1 |
F | 0.0 | 58 |
F | 0.25 | 1 |
F | 0.375 | 2 |
F | 0.5 | 3 |
F | 0.75 | 6 |
F | 0.875 | 1 |
F | 1.0 | 35 |
F | 1.25 | 3 |
F | 1.5 | 27 |
F | 1.75 | 3 |
F | 2.0 | 42 |
F | 2.25 | 3 |
F | 2.5 | 8 |
F | 2.75 | 7 |
F | 2.875 | 2 |
F | 3.0 | 5 |
F | 3.5 | 8 |
F# | 2.0 | 4 |
G | 0.0 | 45 |
G | 0.5 | 7 |
G | 0.75 | 1 |
G | 0.875 | 3 |
G | 1.0 | 30 |
G | 1.25 | 4 |
G | 1.5 | 9 |
G | 2.0 | 42 |
G | 2.5 | 17 |
G | 2.75 | 2 |
G | 3.25 | 6 |
G | 3.5 | 6 |
G# | 0.0 | 26 |
G# | 0.25 | 4 |
G# | 0.5 | 18 |
G# | 0.75 | 5 |
G# | 0.875 | 1 |
G# | 1.0 | 35 |
G# | 1.5 | 13 |
G# | 2.0 | 20 |
G# | 2.25 | 7 |
G# | 2.5 | 2 |
G# | 3.0 | 16 |
G# | 3.25 | 1 |
G# | 3.75 | 2 |
A | 0.0 | 8 |
A | 0.25 | 1 |
A | 0.5 | 4 |
A | 0.75 | 2 |
A | 1.0 | 26 |
A | 1.5 | 5 |
A | 1.75 | 3 |
A | 2.0 | 28 |
A | 2.5 | 6 |
A | 2.875 | 2 |
A | 3.0 | 1 |
A# | 0.0 | 101 |
A# | 0.25 | 1 |
A# | 0.5 | 14 |
A# | 0.75 | 4 |
A# | 0.875 | 3 |
A# | 1.0 | 46 |
A# | 1.25 | 1 |
A# | 1.5 | 21 |
A# | 1.75 | 2 |
A# | 2.0 | 63 |
A# | 2.25 | 3 |
A# | 2.5 | 16 |
A# | 2.75 | 6 |
A# | 3.0 | 10 |
A# | 3.5 | 7 |
A# | 3.75 | 4 |
B | 1.0 | 12 |
B | 2.0 | 6 |
Dasselbe geht auch mit Tonhöhen. Dazu müssen wir einfach beim Parameter plot_with 'PitchClass' durch 'Pitch' ersetzen:
mp_pc_hist = mp.analyse.metric_profile(m_df,
plot_with='Pitch',
do_plot=True)
# Tabelle (do_print=True) und csv-Export (do_save=True):
mp_pc_hist = mp.analyse.metric_profile(m_df,
plot_with='Pitch',
do_plot=None)
# Bei der Tabellenausgabe gibt es nun vier Spalten,
# daher müssen auch vier Bezeichnungen gewählt werden:
mp.utils.export_as_csv(data=mp_pc_hist,
columns=['MIDI-Pitch','Tonhöhe','Metrische Position','Anzahl'],
save_file_name ='metric_profile_hist.csv',
do_save=False,
do_print=False,
do_return_pd=False,
sep=';',
index=False,
header=True)
Auch hier kann es sinnvoll sein, sich die Grafik im externen Pop-Up-Fenster von Matplotlib anzuschauen.
# %matplotlib
# mp_pc_hist = mp.analyse.metric_profile(m_df, plot_with='PitchClass', do_plot=True)
%matplotlib inline
Allerdings haben wir jetzt einen Fehler gemacht: Der Mozart-Satz hat ja einen Taktwechsel!! Wir haben in unserer Auswertung jedoch nicht zwischen 4/4-Takt und 3/4-Takt unterschieden. Daher müssen wir alle Befehle nochmals wiederholen, nachdem wir in die beiden Takttypen unterschieden haben.
# Hier nochmals der Befehl zur Unterscheidung der Takttypen:
# In direktem Anschluss werden die Tonhöhen-Dauern-Profile getrennt dargestellt:
mp_ts_dict_2d = mp.analyse.metric_profile_split_time_signature(m_df,
plot_with=None,
do_plot=True)
# Hier nun der Befehl für Anzeige
# der 3D-Grafiken (do_plot=True)
# der Tabelle (do_print=True)
# und den csv-Export (do_save=True):
mp_ts_dict_p = mp.analyse.metric_profile_split_time_signature(m_df,
plot_with='PitchClass',
do_plot=True)
for k2p in mp_ts_dict_p.keys():
print(f"Time Signature {k2p}")
saveas = 'metric_profile_ts_p_'+k2p.replace('/','-')+'.csv'
mp.utils.export_as_csv(data=mp_ts_dict_p[k2p],
columns=['MIDI', 'Metric Profile','Occurrences'],
save_file_name ='metric_profile_hist.csv',
do_save=False,
do_print=False,
do_return_pd=False,
sep=';',
index=False,
header=True)
# Die Tabelle ist sehr lang geworden und muss daher gescrollt werden!
Time Signature 3/4 Time Signature 4/4
Alle statistischen Abfragen lassen sich durch eine einfach zu bedienende Filterfunktion auf beliebige Ausschnitte und Stimmen eingrenzen (vgl. Tutorial Part 1, Abschnitt 5). Dazu muss man nur die entsprechenden Taktangaben und Stimmenbezeichnungen eingeben.
Hier zwei Beispiele:
filter_dict_interval ={'PartID':'1-2', 'Measure':'1-5'}
interval_hist_example = mp.analyse.interval(m_df,
do_plot=True,
filter_dict = filter_dict_interval)
mp.utils.export_as_csv(data=interval_hist_example,
columns=['Intervall', 'Häufigkeit'],
save_file_name ='interval.csv',
do_save=False,
do_print=False,
do_return_pd=False,
sep=';',
index=False,
header=True)
#filter_dict_cello ={'PartID':'4', 'Measure':'1-10'}
filter_dict_cello ={'PartID':'4'}
pitchclass_hist_cello = mp.analyse.pitch_class_histogram(m_df,
do_plot=True,
filter_dict = filter_dict_cello)
mp.utils.export_as_csv(data=pitchclass_hist_cello,
columns=['Tonhöhenklasse', 'Häufigkeit'],
save_file_name ='cello.csv',
do_save=False,
do_print=True,
do_return_pd=False,
sep=';',
index=False,
header=True)
Tonhöhenklasse | Häufigkeit |
---|---|
C | 26 |
C# | 2 |
D | 22 |
D# | 49 |
F | 33 |
G | 21 |
G# | 19 |
A | 8 |
A# | 57 |
B | 4 |
Wir haben uns die Ergebnisse bislang nur anhand eines einzigen Stückes angeschaut. Wie stellt sich nun aber die Lage dar, wenn wir mehrere Stücke, z.B. mehrere oder alle Sätze der Komposition, miteinander – und mit anderen Stücken – vergleichen? Gibt es stilistische Regelmäßigkeiten – oder überwiegen die Unterschiede?
Laden Sie Kompositionen Ihrer Wahl (verschiedene Gattungen, Komponisten und Epochen) und vergleichen Sie diese Stücke hinsichtlich der Häufigkeiten von Tonhöhen, Tonhöhenklassen, Notenwerten und Intervallen miteinander. Interpretieren Sie die Ergebnisse jeweils mit Blick auf den Notentex!