K-medoids[PAM algorithm]
K-medoids method needs to compare the distance between two points in the data set, so time complexity is so enormous. To avoid the time complexity issue, we can use PAM algorithm.
PAM: Partition Around Medoids.
[ Notation ]
: The set of whole objects.
: The set of selected objects.
: The set of unselected objects
: The dissimilarity between and the closest object in
: The dissimilarity between and the second closet object in
U = O - S
[ Process ]
BUILD PHASE
[Initialization] Put the point for which the sum of the distances to all other objects is minimal into set S
For, consider it as a candidate for
For , compute
If select object , let
Compute the total gain.
Choose that object that maximizes
Let and
Repeat 1-7 until objects have been selected.
def dist_others(i,j,target=['idx','dist','second_min']):
# The input data i,j must be 2 dimension arrays
i = np.array(i); j = np.array(j)
if i.shape == (len(i),): # If it is 1 dimension
i = np.expand_dims(i, axis=0)
if j.shape == (len(j),): # If it is 1 dimension
j = np.expand_dims(j, axis=0)
if (len(i)>1)&(len(j)>1): # Multi to Multi
ls = []; sum1=0
for i_idx, elem1 in enumerate(i):
for elem2 in j:
diff = (elem1-elem2)**2
sum1 += sum(diff)
ls.append([i_idx,sum1])
ls = np.array(ls)
dist = np.min(ls, axis=0)[1]
idx = np.argmin(ls, axis=0)[0]
trg = {target=='idx':idx, target=='dist':dist}.get(True)
return(trg)
elif (len(i)==1)&(len(j)>1): # One to Multi
diff = i-j
sum1 = np.sum(diff**2, axis=1)
idx = np.argmin(sum1); dist = np.min(sum1)
try: second_min = sorted(set(sum1))[1]
except : second_min = np.min(sum1)
trg = {target=='idx':idx, target=='dist':dist, target=='second_min':second_min}.get(True)
return(trg)
elif (len(i)==1)&(len(j)==1): # One to One
diff = i-j
sum0 = np.sum(diff**2)
dist = np.min(sum0)
trg = {target=='idx':'No index in one to one case', target=='dist':dist}.get(True)
return(trg)
# step2
def build(obj, sel):
gi = 0; sel_i = 0; gi_sum=0; n = len(obj)
for i in range(n):
gpre = gi
for j in range(n):
if j==i:
continue
gi = 0
Dj = dist_others(obj[j],sel,'dist')
dji = dist_others(obj[j],obj[i],'dist')
Cji = max(Dj-dji,0)
gi += Cji
gi_sum += gi
if (gpre<gi):
sel_i = i
obj = np.delete(obj, sel_i, axis=0)
if (gi_sum==0): return('Stop')
else: return(sel_i)
SWAP PHASE
This phase is the step switching the element in to one in (The other way around also). It improves the quality of the clustering.
Considers all pairs
If
if , then
if , then
In both subcases,
If
if , then ( can be negative)
if , then ( )
In both subcases,
Compute the total result of swap as
Select a pair that minimizes
If , the swap is carried out and andare updated for every object . After it, return to Step1. If , the algorithm halts. (All is also the halting condition.)
def swap(obj,sel,i,h):
n = len(obj); Tih = 0; Kjih = 0
for j in range(n):
dji = dist_others(obj[j],obj[i],'dist')
Dj = dist_others(obj[j],sel,'dist')
djh = dist_others(obj[j],obj[h],'dist')
if dji>Dj:
Kjih = min(djh-Dj,0)
elif dji==Dj:
Ej = dist_others(obj[j],sel,'second_min')
Kjih = min(djh,Ej)-Dj
Tih += Kjih
return(Tih)
Time complexity of numpy expend_dims is much bigger than using for statement.
Last updated
Was this helpful?