Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KMeans 중심값 생성 함수 추가 (BallCut) #37

Merged
merged 17 commits into from
Nov 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
46 changes: 41 additions & 5 deletions Project17-C-Map/InteractiveClusteringMap.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@
FAC9FD05256E2107009FBB41 /* KCoefficient.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC9FD04256E2107009FBB41 /* KCoefficient.swift */; };
FAC9FD0A256E23B1009FBB41 /* KDefineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC9FD09256E23B1009FBB41 /* KDefineTests.swift */; };
FAC9FD0E256E2495009FBB41 /* KCoefficient.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC9FD04256E2107009FBB41 /* KCoefficient.swift */; };
FC2A9D6D25723C6100D524B0 /* RandomCentroidGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC2A9D6C25723C6100D524B0 /* RandomCentroidGenerator.swift */; };
FC2A9D7525723C9800D524B0 /* ScreenCentroidGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC2A9D7425723C9800D524B0 /* ScreenCentroidGenerator.swift */; };
FC2A9D7D25723D1100D524B0 /* BallCutCentroidGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC2A9D7C25723D1100D524B0 /* BallCutCentroidGenerator.swift */; };
FCE422B02570194B0017416F /* Coordinate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCE422AF2570194B0017416F /* Coordinate.swift */; };
FCE422B8257019650017416F /* Cluster.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCE422B7257019650017416F /* Cluster.swift */; };
FCE422BD25701A200017416F /* Quadrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCE422BC25701A200017416F /* Quadrant.swift */; };
Expand Down Expand Up @@ -120,6 +123,9 @@
FA81D67725690F360023E123 /* CoreDataStack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataStack.swift; sourceTree = "<group>"; };
FAC9FD04256E2107009FBB41 /* KCoefficient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KCoefficient.swift; sourceTree = "<group>"; };
FAC9FD09256E23B1009FBB41 /* KDefineTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KDefineTests.swift; sourceTree = "<group>"; };
FC2A9D6C25723C6100D524B0 /* RandomCentroidGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RandomCentroidGenerator.swift; sourceTree = "<group>"; };
FC2A9D7425723C9800D524B0 /* ScreenCentroidGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenCentroidGenerator.swift; sourceTree = "<group>"; };
FC2A9D7C25723D1100D524B0 /* BallCutCentroidGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BallCutCentroidGenerator.swift; sourceTree = "<group>"; };
FCE422AF2570194B0017416F /* Coordinate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coordinate.swift; sourceTree = "<group>"; };
FCE422B7257019650017416F /* Cluster.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cluster.swift; sourceTree = "<group>"; };
FCE422BC25701A200017416F /* Quadrant.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Quadrant.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -229,13 +235,11 @@
37EB73412562592900AC44D6 /* InteractiveClusteringMapTests */ = {
isa = PBXGroup;
children = (
FC9F5C30257094710029A53B /* KMeans */,
46A12E3E256CEDD3007BDF3E /* QuadTreeTests */,
37EB73422562592900AC44D6 /* InteractiveClusteringMapTests.swift */,
37279B02256EBC5200EA689A /* KMeansTests.swift */,
3713D3DB25694C3E00E703EC /* CoreDataStackTests.swift */,
FAC9FD09256E23B1009FBB41 /* KDefineTests.swift */,
37EB73442562592900AC44D6 /* Info.plist */,
FCE422C625701B8A0017416F /* KMeansCentroidsTest.swift */,
);
path = InteractiveClusteringMapTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -304,6 +308,35 @@
path = POI;
sourceTree = "<group>";
};
FC2A9D5E25721F5C00D524B0 /* Metrics */ = {
isa = PBXGroup;
children = (
FCE422BC25701A200017416F /* Quadrant.swift */,
FCE422C125701A390017416F /* Degree.swift */,
);
path = Metrics;
sourceTree = "<group>";
};
FC2A9D682572371C00D524B0 /* Centroids */ = {
isa = PBXGroup;
children = (
FC2A9D6C25723C6100D524B0 /* RandomCentroidGenerator.swift */,
FC2A9D7425723C9800D524B0 /* ScreenCentroidGenerator.swift */,
FC2A9D7C25723D1100D524B0 /* BallCutCentroidGenerator.swift */,
);
path = Centroids;
sourceTree = "<group>";
};
FC9F5C30257094710029A53B /* KMeans */ = {
isa = PBXGroup;
children = (
FCE422C625701B8A0017416F /* KMeansCentroidsTest.swift */,
FAC9FD09256E23B1009FBB41 /* KDefineTests.swift */,
37279B02256EBC5200EA689A /* KMeansTests.swift */,
);
path = KMeans;
sourceTree = "<group>";
};
FCE422AD257018E30017416F /* Clustering */ = {
isa = PBXGroup;
children = (
Expand All @@ -315,10 +348,10 @@
FCE422AE257018EA0017416F /* KMeans */ = {
isa = PBXGroup;
children = (
FC2A9D5E25721F5C00D524B0 /* Metrics */,
FC2A9D682572371C00D524B0 /* Centroids */,
FAC9FD04256E2107009FBB41 /* KCoefficient.swift */,
379DC4C5256E1EC60044DA6C /* KMeans.swift */,
FCE422BC25701A200017416F /* Quadrant.swift */,
FCE422C125701A390017416F /* Degree.swift */,
);
path = KMeans;
sourceTree = "<group>";
Expand Down Expand Up @@ -527,10 +560,12 @@
379DC4C6256E1EC60044DA6C /* KMeans.swift in Sources */,
3753DCF0256B4C5B006D26A9 /* POIService.swift in Sources */,
FA81D67825690F360023E123 /* CoreDataStack.swift in Sources */,
FC2A9D7D25723D1100D524B0 /* BallCutCentroidGenerator.swift in Sources */,
FCE422B02570194B0017416F /* Coordinate.swift in Sources */,
37EB732D2562592700AC44D6 /* MapViewController.swift in Sources */,
37EB73332562592700AC44D6 /* Model.xcdatamodeld in Sources */,
FCE422B8257019650017416F /* Cluster.swift in Sources */,
FC2A9D7525723C9800D524B0 /* ScreenCentroidGenerator.swift in Sources */,
192D34AD256E355500861703 /* QuadTree.swift in Sources */,
46A12E1B256CD099007BDF3E /* Extension.swift in Sources */,
FCE422BD25701A200017416F /* Quadrant.swift in Sources */,
Expand All @@ -541,6 +576,7 @@
FCE422C225701A390017416F /* Degree.swift in Sources */,
372216C12569325600231245 /* DataManagable.swift in Sources */,
37352CE025695EE000CAFDFD /* Place.swift in Sources */,
FC2A9D6D25723C6100D524B0 /* RandomCentroidGenerator.swift in Sources */,
37EB732B2562592700AC44D6 /* SceneDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//
// BallCutCentroidGenerator.swift
// InteractiveClusteringMap
//
// Created by Seungeon Kim on 2020/11/28.
//

import Foundation

final class BallCutCentroidGenerator: CentroidCreatable {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제가 보기엔 모두 순수함수로 작성하셔서 struct도 괜찮을거같은데 어떠신가요??

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이부분은 의논을 해보죠. 매니저 클래스 만들고 결정해봐야할 것 같아요!


private let coverage: Double
private let k: Int
private let points: [Coordinate]
private let mutiplier: Int = 5

init(k: Int, coverage: Double, coordinates: [Coordinate]) {
self.k = k
self.coverage = coverage
self.points = coordinates
}

func centroids() -> [Coordinate] {
return recursiveClassify(k: k, coordinates: points)
}

private func recursiveClassify(k: Int, coordinates: [Coordinate]) -> [Coordinate] {
var coordinates = coordinates
let container = createContainer(k: k, coordinates: coordinates)
var centroids: [Coordinate] = pickCentroids(k: k, distance: coverage, container: container)

guard centroids.count == k else { return centroids }

for coordinate in container {
guard let index = coordinates.firstIndex(of: coordinate) else {
return []
}
coordinates.remove(at: index)
}

centroids += recursiveClassify(k: k - centroids.count + 1, coordinates: coordinates)
return centroids
}

private func createContainer(k: Int, coordinates: [Coordinate]) -> [Coordinate] {
var coordinates = coordinates
var container: [Coordinate] = []
let count = k * mutiplier

for _ in 0..<count {
guard let coordinate = coordinates.randomElement(),
let index = coordinates.firstIndex(of: coordinate)
else {
return []
}
container.append(coordinate)
coordinates.remove(at: index)
}

return container
}

private func pickCentroids(k: Int, distance: Double, container: [Coordinate]) -> [Coordinate] {
var centroids: [Coordinate] = []
var container = container

for _ in 0..<k {
guard let centroid = container.randomElement() else {
return centroids
}

container = container.filter { centroid.distanceTo($0) >= distance }
centroids.append(centroid)
}

return centroids
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// RandomCetroidGenerator.swift
// InteractiveClusteringMap
//
// Created by Seungeon Kim on 2020/11/28.
//

import Foundation

final class RandomCentroidGenerator: CentroidCreatable {

private let k: Int
private let lats: ClosedRange<Double>
private let lngs: ClosedRange<Double>

init(k: Int, rangeOfLat: ClosedRange<Double>, rangeOfLng: ClosedRange<Double>) {
self.k = k
self.lats = rangeOfLat
self.lngs = rangeOfLng
}

func centroids() -> [Coordinate] {
var centroids: [Coordinate] = []
for _ in 0..<k {
let cluster = Coordinate.randomGenerate(rangeOfLat: lats, rangeOfLng: lngs)
centroids.append(cluster)
}

return centroids
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//
// ScreenCentroidGenerator.swift
// InteractiveClusteringMap
//
// Created by Seungeon Kim on 2020/11/28.
//

import Foundation

final class ScreenCentroidGenerator: CentroidCreatable {

private let topLeft: Coordinate
private let bottomRight: Coordinate
private let k: Int

init(k: Int, topLeft: Coordinate, bottomRight: Coordinate) {
self.k = k
self.topLeft = topLeft
self.bottomRight = bottomRight
}

func centroids() -> [Coordinate] {
let center = (topLeft + bottomRight) / 2.0
let boundary = Coordinate(x: bottomRight.x - center.x, y: topLeft.y - center.y)
let pivot = center.findTheta(vertex: Coordinate(x: bottomRight.x, y: topLeft.y))

let increase = Degree.turn / Double(k)
var angle = increase
var coords: [Coordinate] = []

for _ in 0..<k {
let quadrant = Quadrant.findQuadrant(angle: angle)
let modulus = angle.truncatingRemainder(dividingBy: Degree.right)

if modulus == 0 {
let distance = boundary / 2
let coord = quadrant.degree(center: center, boundary: distance)
coords.append(coord)
} else {
let theta = quadrant.theta(angle: modulus)
let radian = theta * .pi / Degree.straight
var x: Double
var y: Double

if theta > pivot {
y = boundary.y
x = y / tan(radian)
} else {
x = boundary.x
y = tan(radian) * x
}
let distance = Coordinate(x: x, y: y) / 2
let coord = quadrant.convertToCoordinate(center: center, distance: distance)
coords.append(coord)
}
angle += increase
}

return coords
}

}