File size: 1,580 Bytes
37aeb1f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
"""Reference "dumb humanizer" baselines the learned model must beat (design §7)."""

from __future__ import annotations

import numpy as np
import pandas as pd

from ..data.features import N_PHASE_BINS


def _phase_bin(phase_beat: pd.Series) -> pd.Series:
    return np.minimum((phase_beat * N_PHASE_BINS).astype(int), N_PHASE_BINS - 1)


class GlobalMeanBaseline:
    def fit(self, df: pd.DataFrame):
        self.mean_ = float(df["velocity"].mean())
        return self

    def predict(self, df: pd.DataFrame) -> np.ndarray:
        return np.full(len(df), self.mean_, dtype=float)


class LookupTableBaseline:
    """Mean velocity per (voice, genre, phase_bin), backing off to coarser keys."""

    def fit(self, df: pd.DataFrame):
        d = df.copy()
        d["phase_bin"] = _phase_bin(d["phase_beat"])
        self.global_ = float(d["velocity"].mean())
        self.by_voice_ = d.groupby("voice")["velocity"].mean().to_dict()
        self.by_vg_ = d.groupby(["voice", "genre"])["velocity"].mean().to_dict()
        self.by_vgp_ = d.groupby(["voice", "genre", "phase_bin"])["velocity"].mean().to_dict()
        return self

    def predict(self, df: pd.DataFrame) -> np.ndarray:
        pb = _phase_bin(df["phase_beat"]).to_numpy()
        v = df["voice"].to_numpy()
        g = df["genre"].to_numpy()
        out = np.empty(len(df), dtype=float)
        for i in range(len(df)):
            out[i] = self.by_vgp_.get((v[i], g[i], pb[i]),
                     self.by_vg_.get((v[i], g[i]),
                     self.by_voice_.get(v[i], self.global_)))
        return out