"""近邻、伯努利朴素贝叶斯和 SVM 的人工小例子；没有基准性能主张。"""

import numpy as np
from sklearn.naive_bayes import BernoulliNB
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC


def bernoulli_probabilities(x, labels, queries, alpha=1.0):
    """二分类教学实现：每个特征只能为 0/1，使用经验类先验。"""
    x, labels, queries = np.asarray(x), np.asarray(labels), np.asarray(queries)
    if alpha <= 0 or set(labels.tolist()) != {0, 1}:
        raise ValueError("需要正平滑强度和两个类别")
    if not np.isin(x, [0, 1]).all() or not np.isin(queries, [0, 1]).all():
        raise ValueError("本例的伯努利特征必须是 0/1")
    scores = []
    for c in [0, 1]:
        subset = x[labels == c]
        p = (subset.sum(axis=0) + alpha) / (len(subset) + 2 * alpha)
        log_score = (queries * np.log(p) + (1 - queries) * np.log1p(-p)).sum(axis=1)
        scores.append(log_score + np.log(len(subset) / len(x)))
    scores = np.stack(scores, axis=1)
    scores -= scores.max(axis=1, keepdims=True)
    probabilities = np.exp(scores)
    return probabilities / probabilities.sum(axis=1, keepdims=True)


def experiment():
    points = np.array([[1000., 1.], [2000., 3.], [4000., 1.]])
    labels, query, scale = [0, 1, 0], np.array([[1200., 3.]]), np.array([1000., 1.])
    raw = KNeighborsClassifier(n_neighbors=1).fit(points, labels)
    scaled = KNeighborsClassifier(n_neighbors=1).fit(points / scale, labels)
    nb_x = np.array([[0, 0], [0, 1], [1, 0], [0, 0],
                     [1, 1], [1, 0], [1, 1], [0, 1]])
    nb_y = np.array([0] * 4 + [1] * 4)
    nb_q = np.array([[1, 1], [1, 0]])
    own = bernoulli_probabilities(nb_x, nb_y, nb_q)
    library = BernoulliNB(alpha=1., binarize=None).fit(nb_x, nb_y).predict_proba(nb_q)
    np.testing.assert_allclose(own, library)
    duplicated = bernoulli_probabilities(np.tile(nb_x, (1, 2)), nb_y, np.tile(nb_q, (1, 2)))

    svm_x = np.array([[-2., -1.], [-2., 1.], [-1., -.5], [-1., .5],
                      [1., -.5], [1., .5], [2., -1.], [2., 1.]])
    svm_y = np.array([-1] * 4 + [1] * 4)
    svm = SVC(C=1., kernel="linear", tol=1e-10).fit(svm_x, svm_y)
    margins = svm_y * svm.decision_function(svm_x)
    xor = np.array([[-1., -1.], [-1., 1.], [1., -1.], [1., 1.]])
    xor_y = np.array([0, 1, 1, 0])
    xor_linear = SVC(C=10., kernel="linear").fit(xor, xor_y)
    xor_rbf = SVC(C=10., kernel="rbf", gamma=1.).fit(xor, xor_y)
    return dict(points=points, query=query, scale=scale, labels=labels,
                raw=int(raw.predict(query)[0]), scaled=int(scaled.predict(query / scale)[0]),
                nb=own, duplicated=duplicated, svm=svm, svm_x=svm_x, svm_y=svm_y,
                margins=margins, xor=xor, xor_y=xor_y, xor_linear=xor_linear, xor_rbf=xor_rbf)


def main():
    r = experiment()
    print(f'1-NN：原始量纲类别 {r["raw"]}，指定尺度后类别 {r["scaled"]}')
    print("朴素贝叶斯的类别 1 概率：", [round(v, 6) for v in r["nb"][:, 1].tolist()])
    print(f'复制全部特征后，[1,1] 的类别 1 概率：{r["duplicated"][0, 1]:.6f}')
    print("朴素贝叶斯与库实现：一致")
    print("线性 SVM 权重：", np.round(r["svm"].coef_[0], 6).tolist())
    print(f'线性 SVM 最小函数间隔：{min(r["margins"]):.6f}')
    print(f'XOR 四个训练点：线性 {r["xor_linear"].score(r["xor"], r["xor_y"]):.3f}，'
          f'RBF {r["xor_rbf"].score(r["xor"], r["xor_y"]):.3f}')
    print("这些是人工算例和训练点拟合，不是独立测试成绩。")


if __name__ == "__main__":
    main()
