Module music_df.harmony.matching
Functions
def label_pc_matches(music_df: pandas.DataFrame,
chord_df: pandas.DataFrame,
chord_df_pc_key: str = 'chord_pcs',
is_sliced: bool = False,
match_col: str = 'is_chord_match') ‑> pandas.DataFrame-
Expand source code
def label_pc_matches( music_df: pd.DataFrame, chord_df: pd.DataFrame, chord_df_pc_key: str = "chord_pcs", is_sliced: bool = False, match_col: str = "is_chord_match", ) -> pd.DataFrame: if not is_sliced: music_df = slice_df(music_df, chord_df["onset"]) music_df.loc[:, match_col] = False for _, chord_row in chord_df.iterrows(): chord_notes = music_df.loc[ (music_df["onset"] >= chord_row["onset"]) & (music_df["release"] <= chord_row["release"]) & (music_df["type"] == "note") ] chord_pcs = chord_row[chord_df_pc_key] if isinstance(chord_pcs, str): chord_pcs = hex_str_to_pc_ints(chord_pcs, return_set=True) matches = (chord_notes["pitch"] % 12).isin(chord_pcs) music_df.loc[chord_notes.index, match_col] = matches return music_df def percent_chord_df_match(music_df: pandas.DataFrame,
chord_df: pandas.DataFrame,
weight_by_duration: bool = True,
chord_df_pc_key: str = 'chord_pcs',
is_sliced: bool = False,
match_col: str = 'percent_chord_match')-
Expand source code
def percent_chord_df_match( music_df: pd.DataFrame, chord_df: pd.DataFrame, weight_by_duration: bool = True, chord_df_pc_key: str = "chord_pcs", is_sliced: bool = False, match_col: str = "percent_chord_match", ): if is_sliced: sliced_notes = music_df.loc[music_df["type"] == "note"] else: sliced_notes = slice_df(music_df[music_df["type"] == "note"], chord_df["onset"]) chord_pc_matches = [] music_df.loc[:, match_col] = float("nan") music_df.loc[:, chord_df_pc_key] = "" for _, chord_row in chord_df.iterrows(): chord_notes = sliced_notes[ (sliced_notes["onset"] >= chord_row["onset"]) & (sliced_notes["release"] <= chord_row["release"]) ] chord_pc_match = percent_pc_match( chord_notes, chord_row[chord_df_pc_key], weight_by_duration=weight_by_duration, input_contains_only_notes=True, ) chord_pc_matches.append(chord_pc_match) music_df.loc[chord_notes.index, match_col] = chord_pc_match music_df.loc[chord_notes.index, chord_df_pc_key] = chord_row[chord_df_pc_key] return { "macroaverage": sum(chord_pc_matches) / len(chord_pc_matches), "microaverage": music_df[match_col].mean(skipna=True), "music_df": music_df, } def percent_pc_match(passage: pandas.DataFrame,
pitch_classes: set[int] | str,
weight_by_duration: bool = True,
input_contains_only_notes: bool = False) ‑> float-
Expand source code
def percent_pc_match( passage: pd.DataFrame, pitch_classes: set[int] | str, weight_by_duration: bool = True, input_contains_only_notes: bool = False, ) -> float: """ Return the percentage of pitch classes in the passage that match the given pitch classes. Args: passage: A DataFrame with a "type" column and a "pitch" column. pitch_classes: A set of pitch classes or a hex string. weight_by_duration: If True (default), weight the match by the duration of the notes. input_contains_only_notes: If True, the input DataFrame is assumed to contain only notes, which saves filtering the DataFrame by the type column. Returns: The percentage of pitch classes in the passage that match the given pitch classes. >>> df = pd.read_csv( ... io.StringIO( ... ''' ... type,pitch,onset,release ... bar,,0.0,4.0 ... note,60,0.0,2.0 ... note,64,2.0,3.0 ... note,67,3.0,4.0 ... ''' ... ) ... ) >>> percent_pc_match(df, "047") 1.0 >>> percent_pc_match(df, {0, 4, 7}) 1.0 >>> percent_pc_match(df, "049") 0.75 >>> percent_pc_match(df, "049", weight_by_duration=False) # doctest: +ELLIPSIS 0.666... """ if isinstance(pitch_classes, str): pitch_classes = set(hex_str_to_pc_ints(pitch_classes, return_set=True)) if not input_contains_only_notes: notes = passage.loc[passage["type"] == "note"] else: notes = passage matches = (notes["pitch"] % 12).isin(pitch_classes) if not weight_by_duration: return matches.mean() if "duration" in passage.columns: durations = notes["duration"] else: durations = notes["release"] - notes["onset"] return (matches * durations).sum() / durations.sum()Return the percentage of pitch classes in the passage that match the given pitch classes.
Args
passage- A DataFrame with a "type" column and a "pitch" column.
pitch_classes- A set of pitch classes or a hex string.
weight_by_duration- If True (default), weight the match by the duration of the
- notes.
input_contains_only_notes- If True, the input DataFrame is assumed to contain
only notes, which saves filtering the DataFrame by the type column.
Returns
The percentage of pitch classes in the passage that match the given pitch classes.
>>> df = pd.read_csv( ... io.StringIO( ... ''' ... type,pitch,onset,release ... bar,,0.0,4.0 ... note,60,0.0,2.0 ... note,64,2.0,3.0 ... note,67,3.0,4.0 ... ''' ... ) ... ) >>> percent_pc_match(df, "047") 1.0 >>> percent_pc_match(df, {0, 4, 7}) 1.0 >>> percent_pc_match(df, "049") 0.75 >>> percent_pc_match(df, "049", weight_by_duration=False) # doctest: +ELLIPSIS 0.666...