In [34]:
import numpy as np
from sklearn.neighbors import NearestNeighbors
In [55]:
r = 1 # Initial radius for model
x = 1 # increment for expanding radius
d = .01 # distance threshold for determing if two points near equidistant
# existing points and corresponding classes
existingPoints = np.array([[2,3,4,5,6,7,8],[1,3,4,5,6,7,8],[6,2,3,4,5,6,7]]) # existing points in model
existingClasses = np.array([1,2,3])
# initialize classifier
neigh = NearestNeighbors(radius = r)
# read in new point
newPoints = np.array([2,3,4,5,6,7,8],ndmin=2)
# fit existing points to classifier
neigh.fit(existingPoints)
#distances,indices = neigh.radius_neighbors(newPoints)
newClass = classify(neigh,existingClasses,newPoints)
# Add the new point and new class to the model. Class = 0 if point is unclassified
existingPoints = np.append(existingPoints,newPoints)
existingClasses = np.append(existingClasses,newClass)
print(newClass)
In [54]:
def classify(neigh,existingClasses,newPoints):
distances,indices = neigh.radius_neighbors(newPoints)
# if there are no points in the radius, expand radius by x and check again before classifying point.
if len(indices)==0 or len(indices)==1:
neigh.radius = r+x
distances,indices = neigh.radius_neighbors(newPoints)
# else if there are two points from different classes that are close to the same distance
# (within distance threshold), expand radius to see if there is another very close point
elif len(indices) ==2 and existingClasses[indices[0]]!=existingClasses[indices[1]]:
if abs(distances[0]-distances[1]) <= d:
neigh.radius = r+x
# predict class of new data
if len(indices)!=0:
# calculate weights (arbitrary weights for now)
weights = np.array([0,5,5])
# sum weights for each class
classes = existingClasses[indices[0]]
classes = np.unique(classes[np.where(classes!=0)]) # ignore zero class (outliers)
classWeight = np.zeros(len(classes)) # initialize weight array
for i,cl in enumerate(classes):
classWeight[i] = sum(weights[np.where(classes==cl)])
newClass = classes[np.argmax(classWeight)]
else:
newClass = 0
return(newClass)
In [4]:
## Since we are including PAMguard into feature space, use an enum to make np.array work
In [10]:
from datetime import datetime
class DetectedTarget:
features_label = ["","","","","","","",""]
def __init__(self, features=[], source="", date=datetime.now(), classification=None):
self.features = features
self.source = source
self.date = date
self.classification = classification
In [88]:
def rescaleFrom0To1(features):
for i in range(features.shape[1]):
if features[:,i].min() == features[:,i].max():
features[:,i] = 0.5
else:
features[:,i] = (features[:,i] - features[:,i].min()) / (features[:,i].max() - features[:,i].min())
return features
In [70]:
features
Out[70]:
In [89]:
rescaleFrom0To1(features)
Out[89]:
In [111]:
from enum import Enum
import numpy as np
from sklearn.neighbors import NearestNeighbors
from datetime import datetime
import os.path
import csv
class Classes(Enum):
Interesting = 1.1
NotInteresting = 1.2
FastLarge = 2.1
FastSmall = 2.2
SlowLarge = 2.3
SlowSmall = 2.4
# SchoolOfFish = 3.1
# SingleFish = 3.2
# Kelp = 3.3
# DolphinOrPorpoise = 3.4
class DetectedTarget:
features_label = ["","","","","","","",""]
def __init__(self, features=[], source="Unknown", date=datetime.now(), classification=None):
self.features = features
self.source = source
self.date = date
self.classification = classification
class weightedNeighbors:
def __init__(self):
# Global variables
self.INITIAL_RADIUS = 3.0
self.SIZE_FEATURE_WEIGHT = 1.0
self.SPEED_FEATURE_WEIGHT = 1.0
self.SPEED_RELATIVE_TO_CURRENT_FEATURE_WEIGHT = 1.0
self.TARGET_STRENGTH_FEATURE_WEIGHT = 1.0
self.CURRENT_SPEED_FEATURE_WEIGHT = 1.0
self.TIME_OF_DAY_FEATURE_WEIGHT = 1.0
self.PASSIVE_ACOUSTICS_FEATURE_WEIGHT = 1.0
self.RADIUS_INCREMENT = 0.1 # increment for expanding radius
self.DISTANCE_THRESHOLD = 0.01 # distance threshold for determing if two points near equidistant
# load current model and initialize NearestNeighbors model
self.current_model_targets = self.load_detectedTargets()
self.model = NearestNeighbors(radius=self.INITIAL_RADIUS)
def load_detectedTargets(self):
'''
Load existing targets from current_model_targets.csv
'''
current_model_targets = []
if os.path.isfile('current_model_targets.csv'):
current_model_targets = []
with open('current_model_targets.csv', 'r') as f:
reader = csv.reader(f,delimiter = ";")
next(reader,None)
for target in reader:
current_model_targets.append( DetectedTarget(
features=list(target[1:8]), source=target[8], date=target[10],
classification=Classes(float(target[-1]))))
else:
print('No existing targets for model')
return current_model_targets
def fitModel(self):
'''
Fit current model targets to model
'''
self.model.fit(np.array(list(map(lambda x: x.features, self.current_model_targets))))
def determine_weights(self,indices):
'''
This function will return the weights of points with the desired indices
'''
pass
return np.array([0,5,5])
def classify(self,newPoint):
'''
Predict class of new target detection(s)
'''
x = self.RADIUS_INCREMENT
r = self.DISTANCE_THRESHOLD
existingClasses = np.array(list(map(lambda x: x.classification, self.current_model_targets)))
distances,indices = self.model.radius_neighbors(newPoint)
# if there are no points in the radius, expand radius by x and check again before classifying point.
if len(indices)==0 or len(indices)==1:
neigh.radius = r+x
distances,indices = self.model.radius_neighbors(newPoint)
# else if there are two points from different classes that are close to the same distance
# (within distance threshold), expand radius to see if there is another very close point
elif len(indices) ==2 and existingClasses[indices[0]]!=existingClasses[indices[1]]:
if abs(distances[0]-distances[1]) <= d:
neigh.radius = r+x
distances,indices = self.model.radius_neighbors(newPoint)
# predict class of new data
if len(indices)!=0:
# calculate weights (arbitrary weights for now)
weights = self.determine_weights(indices)
# sum weights for each class
classes = existingClasses[indices[0]]
classes = np.unique(classes[np.where(classes!=0)]) # ignore zero class (outliers)
classWeight = np.zeros(len(classes)) # initialize weight array
for i,cl in enumerate(classes):
classWeight[i] = sum(weights[np.where(classes==cl)])
newClass = classes[np.argmax(classWeight)]
else:
newClass = 0
return(newClass)
In [112]:
neigh = weightedNeighbors()
neigh.fitModel()
neigh.classify(np.array([2,3,4,5,6,7,8],ndmin=2))
Out[112]:
In [1]:
In [ ]: