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 중심값 생성 함수 추가 및 코드 리팩토링 #34

Merged
merged 14 commits into from
Nov 27, 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
67 changes: 51 additions & 16 deletions Project17-C-Map/InteractiveClusteringMap.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,13 @@
372216C12569325600231245 /* DataManagable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372216C02569325600231245 /* DataManagable.swift */; };
372216CD256943D300231245 /* POI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372216CC256943D300231245 /* POI.swift */; };
37279B03256EBC5200EA689A /* KMeansTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37279B02256EBC5200EA689A /* KMeansTests.swift */; };
37279B07256EBC6300EA689A /* BoundingBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46A12E22256CD4DB007BDF3E /* BoundingBox.swift */; };
37279B0B256EBC6E00EA689A /* KMeansClustering.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379DC4C5256E1EC60044DA6C /* KMeansClustering.swift */; };
37311088256F641400350D55 /* MockPOIService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37311087256F641400350D55 /* MockPOIService.swift */; };
37279B0B256EBC6E00EA689A /* KMeans.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379DC4C5256E1EC60044DA6C /* KMeans.swift */; };
3731108C256F641F00350D55 /* POIService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3753DCEF256B4C5B006D26A9 /* POIService.swift */; };
37352CD125695B8A00CAFDFD /* restuarant-list-for-test.json in Resources */ = {isa = PBXBuildFile; fileRef = 37352CD025695B8A00CAFDFD /* restuarant-list-for-test.json */; };
37352CDB25695E7400CAFDFD /* JSONReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37352CDA25695E7400CAFDFD /* JSONReader.swift */; };
37352CE025695EE000CAFDFD /* Place.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37352CDF25695EE000CAFDFD /* Place.swift */; };
3753DCF0256B4C5B006D26A9 /* POIService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3753DCEF256B4C5B006D26A9 /* POIService.swift */; };
376E43BA256F9811002D3EE9 /* MockPOIService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37311087256F641400350D55 /* MockPOIService.swift */; };
379DC4C6256E1EC60044DA6C /* KMeansClustering.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379DC4C5256E1EC60044DA6C /* KMeansClustering.swift */; };
379DC4C6256E1EC60044DA6C /* KMeans.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379DC4C5256E1EC60044DA6C /* KMeans.swift */; };
37EB73292562592700AC44D6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EB73282562592700AC44D6 /* AppDelegate.swift */; };
37EB732B2562592700AC44D6 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EB732A2562592700AC44D6 /* SceneDelegate.swift */; };
37EB732D2562592700AC44D6 /* MapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EB732C2562592700AC44D6 /* MapViewController.swift */; };
Expand All @@ -52,10 +49,18 @@
46B444582568F095001B79D4 /* InteractiveMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46B444572568F095001B79D4 /* InteractiveMapView.swift */; };
C9FF187A077822AA8BD00410 /* Pods_InteractiveClusteringMap.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A551109FA7E11823B10D084F /* Pods_InteractiveClusteringMap.framework */; };
FA81D67825690F360023E123 /* CoreDataStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA81D67725690F360023E123 /* CoreDataStack.swift */; };
FA84D560256F796700ECDD44 /* BoundingBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46A12E22256CD4DB007BDF3E /* BoundingBox.swift */; };
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 */; };
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 */; };
FCE422C225701A390017416F /* Degree.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCE422C125701A390017416F /* Degree.swift */; };
FCE422C725701B8A0017416F /* KMeansCentroidsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCE422C625701B8A0017416F /* KMeansCentroidsTest.swift */; };
FCE422CE25701C760017416F /* Coordinate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCE422AF2570194B0017416F /* Coordinate.swift */; };
FCE422D225701C790017416F /* Cluster.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCE422B7257019650017416F /* Cluster.swift */; };
FCE422D625701CBD0017416F /* Quadrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCE422BC25701A200017416F /* Quadrant.swift */; };
FCE422DA25701CC00017416F /* Degree.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCE422C125701A390017416F /* Degree.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -84,12 +89,11 @@
372216C02569325600231245 /* DataManagable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataManagable.swift; sourceTree = "<group>"; };
372216CC256943D300231245 /* POI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = POI.swift; sourceTree = "<group>"; };
37279B02256EBC5200EA689A /* KMeansTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMeansTests.swift; sourceTree = "<group>"; };
37311087256F641400350D55 /* MockPOIService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockPOIService.swift; sourceTree = "<group>"; };
37352CD025695B8A00CAFDFD /* restuarant-list-for-test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "restuarant-list-for-test.json"; sourceTree = "<group>"; };
37352CDA25695E7400CAFDFD /* JSONReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONReader.swift; sourceTree = "<group>"; };
37352CDF25695EE000CAFDFD /* Place.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Place.swift; sourceTree = "<group>"; };
3753DCEF256B4C5B006D26A9 /* POIService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = POIService.swift; sourceTree = "<group>"; };
379DC4C5256E1EC60044DA6C /* KMeansClustering.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMeansClustering.swift; sourceTree = "<group>"; };
379DC4C5256E1EC60044DA6C /* KMeans.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMeans.swift; sourceTree = "<group>"; };
37EB73252562592700AC44D6 /* InteractiveClusteringMap.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = InteractiveClusteringMap.app; sourceTree = BUILT_PRODUCTS_DIR; };
37EB73282562592700AC44D6 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
37EB732A2562592700AC44D6 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
Expand All @@ -116,6 +120,11 @@
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>"; };
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>"; };
FCE422C125701A390017416F /* Degree.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Degree.swift; sourceTree = "<group>"; };
FCE422C625701B8A0017416F /* KMeansCentroidsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMeansCentroidsTest.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -149,7 +158,8 @@
children = (
37352CDF25695EE000CAFDFD /* Place.swift */,
372216CC256943D300231245 /* POI.swift */,
379DC4C5256E1EC60044DA6C /* KMeansClustering.swift */,
FCE422AF2570194B0017416F /* Coordinate.swift */,
FCE422B7257019650017416F /* Cluster.swift */,
);
path = VO;
sourceTree = "<group>";
Expand Down Expand Up @@ -196,13 +206,13 @@
37EB73272562592700AC44D6 /* InteractiveClusteringMap */ = {
isa = PBXGroup;
children = (
FCE422AD257018E30017416F /* Clustering */,
37352CD925695E4D00CAFDFD /* Util */,
37352CCF25695AA500CAFDFD /* Resource */,
372216CB256943C800231245 /* VO */,
FA81D66E2568FEC50023E123 /* CoreData */,
37EB73282562592700AC44D6 /* AppDelegate.swift */,
37EB732A2562592700AC44D6 /* SceneDelegate.swift */,
FAC9FD04256E2107009FBB41 /* KCoefficient.swift */,
37EB732C2562592700AC44D6 /* MapViewController.swift */,
4623BD1225694A1400940B12 /* MapController.swift */,
46153145256BDDD700BAE674 /* QuadTree */,
Expand All @@ -222,10 +232,10 @@
46A12E3E256CEDD3007BDF3E /* QuadTreeTests */,
37EB73422562592900AC44D6 /* InteractiveClusteringMapTests.swift */,
37279B02256EBC5200EA689A /* KMeansTests.swift */,
37311087256F641400350D55 /* MockPOIService.swift */,
3713D3DB25694C3E00E703EC /* CoreDataStackTests.swift */,
FAC9FD09256E23B1009FBB41 /* KDefineTests.swift */,
37EB73442562592900AC44D6 /* Info.plist */,
FCE422C625701B8A0017416F /* KMeansCentroidsTest.swift */,
);
path = InteractiveClusteringMapTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -294,6 +304,25 @@
path = POI;
sourceTree = "<group>";
};
FCE422AD257018E30017416F /* Clustering */ = {
isa = PBXGroup;
children = (
FCE422AE257018EA0017416F /* KMeans */,
);
path = Clustering;
sourceTree = "<group>";
};
FCE422AE257018EA0017416F /* KMeans */ = {
isa = PBXGroup;
children = (
FAC9FD04256E2107009FBB41 /* KCoefficient.swift */,
379DC4C5256E1EC60044DA6C /* KMeans.swift */,
FCE422BC25701A200017416F /* Quadrant.swift */,
FCE422C125701A390017416F /* Degree.swift */,
);
path = KMeans;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
Expand Down Expand Up @@ -495,18 +524,21 @@
37EB73292562592700AC44D6 /* AppDelegate.swift in Sources */,
4623BD1325694A1400940B12 /* MapController.swift in Sources */,
372216CD256943D300231245 /* POI.swift in Sources */,
379DC4C6256E1EC60044DA6C /* KMeansClustering.swift in Sources */,
379DC4C6256E1EC60044DA6C /* KMeans.swift in Sources */,
3753DCF0256B4C5B006D26A9 /* POIService.swift in Sources */,
FA81D67825690F360023E123 /* CoreDataStack.swift in Sources */,
FCE422B02570194B0017416F /* Coordinate.swift in Sources */,
37EB732D2562592700AC44D6 /* MapViewController.swift in Sources */,
37EB73332562592700AC44D6 /* Model.xcdatamodeld in Sources */,
FCE422B8257019650017416F /* Cluster.swift in Sources */,
192D34AD256E355500861703 /* QuadTree.swift in Sources */,
46A12E1B256CD099007BDF3E /* Extension.swift in Sources */,
FCE422BD25701A200017416F /* Quadrant.swift in Sources */,
37352CDB25695E7400CAFDFD /* JSONReader.swift in Sources */,
46A12E23256CD4DB007BDF3E /* BoundingBox.swift in Sources */,
FAC9FD05256E2107009FBB41 /* KCoefficient.swift in Sources */,
37EB73292562592700AC44D6 /* AppDelegate.swift in Sources */,
376E43BA256F9811002D3EE9 /* MockPOIService.swift in Sources */,
FCE422C225701A390017416F /* Degree.swift in Sources */,
372216C12569325600231245 /* DataManagable.swift in Sources */,
37352CE025695EE000CAFDFD /* Place.swift in Sources */,
37EB732B2562592700AC44D6 /* SceneDelegate.swift in Sources */,
Expand All @@ -517,24 +549,27 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
FCE422D225701C790017416F /* Cluster.swift in Sources */,
FAC9FD0E256E2495009FBB41 /* KCoefficient.swift in Sources */,
192D34BE256E36B900861703 /* Extension.swift in Sources */,
3713D3E425694CD300E703EC /* POI.swift in Sources */,
46A12E2B256CED1B007BDF3E /* POIEntity+CoreDataProperties.swift in Sources */,
37279B0B256EBC6E00EA689A /* KMeansClustering.swift in Sources */,
37279B0B256EBC6E00EA689A /* KMeans.swift in Sources */,
FCE422DA25701CC00017416F /* Degree.swift in Sources */,
FCE422CE25701C760017416F /* Coordinate.swift in Sources */,
3713D3E025694C6B00E703EC /* CoreDataStack.swift in Sources */,
3731108C256F641F00350D55 /* POIService.swift in Sources */,
FCE422D625701CBD0017416F /* Quadrant.swift in Sources */,
46A12E40256CEDF2007BDF3E /* BoundingBoxTests.swift in Sources */,
192D34B6256E36A000861703 /* QuadTree.swift in Sources */,
46A12E2F256CED3A007BDF3E /* AppDelegate.swift in Sources */,
46A12E27256CED18007BDF3E /* POIEntity+CoreDataClass.swift in Sources */,
37EB73432562592900AC44D6 /* InteractiveClusteringMapTests.swift in Sources */,
37311088256F641400350D55 /* MockPOIService.swift in Sources */,
46A12E3A256CED57007BDF3E /* JSONReader.swift in Sources */,

192D34BA256E36A200861703 /* BoundingBox.swift in Sources */,
192D34B2256E368D00861703 /* QuadTreeTests.swift in Sources */,
199DDFF6256B9C8E0065B94E /* Place.swift in Sources */,
FCE422C725701B8A0017416F /* KMeansCentroidsTest.swift in Sources */,
3713D3FA25694D9C00E703EC /* DataManagable.swift in Sources */,
37279B03256EBC5200EA689A /* KMeansTests.swift in Sources */,
FAC9FD0A256E23B1009FBB41 /* KDefineTests.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// Degree.swift
// InteractiveClusteringMap
//
// Created by Seungeon Kim on 2020/11/27.
//

import Foundation

enum Degree {
static let right = 90.0
static let straight = 180.0
static let turn = 360.0
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,73 @@
//
// KMeansClustering.swift
// KMeans.swift
// InteractiveClusteringMap
//
// Created by Oh Donggeon on 2020/11/25.
//

import Foundation

class KMeansClustering {
class KMeans {

private let k: Int

init(k: Int) {
self.k = k
}

func randomCentroids(rangeOfLat lats: ClosedRange<Double>,
rangeOfLng lngs: ClosedRange<Double>) -> [Coordinate] {
var centroids: [Coordinate] = []
for _ in 0..<k {
let lat = Double.random(in: lats)
let lng = Double.random(in: lngs)
let cluster = Coordinate(x: lng, y: lat)
centroids.append(cluster)
}

return centroids
}

func screenCentroids(topLeft: Coordinate, bottomRight: Coordinate) -> [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
}

/// points에 대한 centroid를 계속 계산하여 centroid가 이동한 총 거리가
/// convergeDistance 이하로 움직일 경우 cluster를 반환합니다.
///
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//
// Quadrant.swift
// InteractiveClusteringMap
//
// Created by Seungeon Kim on 2020/11/27.
//

import Foundation

enum Quadrant: Int {
case first = 0, second = 1, third = 2, fourth = 3

static func findQuadrant(angle: Double) -> Quadrant {
guard angle != .zero else { return Quadrant.first }

let number = Int(angle / Degree.right)
if angle.truncatingRemainder(dividingBy: Degree.right) == 0 {
return Quadrant(rawValue: number - 1) ?? Quadrant.first
} else {
return Quadrant(rawValue: number) ?? Quadrant.first
}
}

func theta(angle: Double) -> Double {
if self == .first || self == .third {
return Degree.right - angle
} else {
return angle
}
}

func degree(center: Coordinate, boundary: Coordinate) -> Coordinate {
switch self {
case .first:
return Coordinate(x: center.x + boundary.x, y: center.y)
case .second:
return Coordinate(x: center.x, y: center.y - boundary.y)
case .third:
return Coordinate(x: center.x - boundary.x, y: center.y)
case .fourth:
return Coordinate(x: center.x, y: center.y + boundary.y)
}
}

func convertToCoordinate(center: Coordinate, distance: Coordinate) -> Coordinate {
switch self {
case .first:
return Coordinate(x: center.x + distance.x, y: center.y + distance.y)
case .second:
return Coordinate(x: center.x + distance.x, y: center.y - distance.y)
case .third:
return Coordinate(x: center.x - distance.x, y: center.y - distance.y)
case .fourth:
return Coordinate(x: center.x - distance.x, y: center.y + distance.y)
}
}
}