# import necessary packages from matplotlib import pyplot as plt import numpy as np from qpsolvers import solve_qp from matplotlib.patches import Arc from sklearn.svm import SVC # enter our data left_points = [2, 3] right_points = [6, 6.5, 8.3] addtl_points = [10.5] # assign classifications x = left_points + right_points + addtl_points y = np.concatenate((np.full((len(left_points), 1), -1), np.full((len(right_points), 1), 1), np.full((len(addtl_points), 1), -1))) # details of our naive transformation: subtract 7 and square offset = np.average(7) def my_kernel(X, Y): return np.dot(X, Y.T) + np.dot((X - offset)**2, (Y.T - offset)**2) # arrange data into a form suitable for the SVC package X = np.reshape(x, (-1, 1)) Y = y.ravel() # a collection of potential solution settings model = SVC(C = 1, kernel = my_kernel) # matches the manual results #model = SVC(C = 1e9, kernel = 'poly', degree=3, gamma = 'scale', coef0 = 1) #model = SVC(C = 1, kernel = 'rbf') # output the results print('Model details: ', model.fit(X, Y)) print('Support vector indices: ', model.support_) print('Number of support vectors in each class: ', model.n_support_) print('Dual coefficients: ', model.dual_coef_) print('Intercept (b): ', model.intercept_) # plot the results font = {'size':16} tickfont = {'size':10} fig, ax = plt.subplots() plt.rcParams['savefig.facecolor'] = '#c0c0c0' plt.rc('font', **font) marker_style = dict(linestyle='none', markersize = 10, markeredgecolor = 'k') ax.set_aspect(1) ax.plot(left_points,np.zeros(len(left_points)),marker="^", markerfacecolor='red', **marker_style) ax.plot(right_points,np.zeros(len(right_points)),marker="s", markerfacecolor='blue', **marker_style) ax.plot(addtl_points,np.zeros(len(addtl_points)),marker="^", markerfacecolor='red', **marker_style) # locate the classification thresholds def classification(x): return model.predict([[x]])[0] class_input_array = np.linspace(0, 12, 5000) class_output = [classification(x) for x in class_input_array] zero_crossings = np.where(np.diff(np.sign(class_output))) (div_1, div_2)=class_input_array[zero_crossings] # expecting two divisions print('Divisions: ', div_1, div_2) print('Classification of far-right point: ', classification(addtl_points[0])) # plot the division points with associated graphics and labeling for i in [div_1, div_2]: ax.annotate('', xy = (i, -0.6), xytext = (i, 0.6), arrowprops={'arrowstyle':'-', 'lw':5, 'color':'0'}, va='center') arc=Arc(((div_1 + div_2)/2, 0.6), div_2 - div_1, 3, linestyle = '--', angle = 0.0, theta1 = 0, theta2 = 180) ax.add_artist(arc) plt.gca().axes.get_yaxis().set_visible(False) ax.annotate('', xy = (0, 1.9), xytext = (0, -1.9), arrowprops={'arrowstyle':'-', 'lw':1, 'facecolor':'k'},va = 'center') fig.set_facecolor('#c0c0c0') plt.xlim([-1, 13]) plt.ylim([-2.5, 2.5]) plt.xticks(np.arange(2, 11, 2), **tickfont) ax.annotate('', xy = (12, 0), xytext = (-.05, 0), arrowprops = {'arrowstyle':'->', 'lw':1, 'facecolor':'k'}, va = 'center', zorder = -1) ax.annotate('$x$', xy = (0, 0), xytext = (12.2, 0), va = 'center') ax.text(1.4, 1, '-1 class', fontsize = '14') ax.annotate('+1 class', xy=(0, 0), xytext = ((div_1 + div_2)/2, 0.8), ha = 'center', fontsize = '14') plt.tick_params(axis = 'x', which='major', labelsize = 10) for i in model.support_: ax.plot(x[i], 0, marker = "o", linestyle = 'none', markersize = 25, markerfacecolor = 'None', markeredgecolor = 'k') plt.box(on = None) ax.spines['bottom'].set_position('zero') ax.spines['bottom'].set_color('none') ax.spines['left'].set_position('zero') ax.spines['left'].set_color('none') ax.spines['left'].set_linewidth(1) ax.spines['bottom'].set_linewidth(1) plt.show()