-
Notifications
You must be signed in to change notification settings - Fork 5
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
Changes from 6 commits
2169959
95d47d1
49c64c4
0aa4c97
bd7e272
725b8a8
d1879f9
aeb1cd7
83a72c7
3cb67f0
822d0bf
7897efb
6ce21bb
745938f
09bc2c8
733cd09
15fd1b4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,28 +7,89 @@ | |
|
||
import Foundation | ||
|
||
class KMeans { | ||
final class KMeans { | ||
|
||
private let k: Int | ||
|
||
init(k: Int) { | ||
self.k = k | ||
} | ||
|
||
func randomCentroids(rangeOfLat lats: ClosedRange<Double>, | ||
func initializeBallCutCentroids(k: Int, | ||
coverage: Double, | ||
coordinates: [Coordinate]) -> [Coordinate] { | ||
var coordinates = coordinates | ||
var centroids: [Coordinate] = [] | ||
let container = createContainer(k: k, coordinates: coordinates) | ||
centroids += pickCentroids(k: k, distance: coverage, container: container) | ||
|
||
if centroids.count != k { | ||
for coordinate in container { | ||
guard let index = coordinates.firstIndex(of: coordinate) else { | ||
return [] | ||
} | ||
coordinates.remove(at: index) | ||
} | ||
|
||
centroids += initializeBallCutCentroids(k: k - centroids.count + 1, coverage: coverage, coordinates: coordinates) | ||
} | ||
|
||
return centroids | ||
} | ||
|
||
private func createContainer(k: Int, coordinates: [Coordinate], mutiplier: Int = 5) -> [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 | ||
} | ||
|
||
var remove: [Coordinate] = [] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기 변수명 바꿔줘야 할 것 같아요! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. indexForRemoveCentroids 어떨까요...? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. removeCoordinates로 수정했습니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
index를 담는 배열이 아니에요!! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ㅎㅎㅎ 죄송합니당 잘못봤네요 |
||
|
||
container.forEach { coordinate in | ||
if centroid.distanceTo(coordinate) <= distance { | ||
remove.append(coordinate) | ||
} | ||
} | ||
|
||
container = container.filter { !remove.contains($0) } | ||
centroids.append(centroid) | ||
} | ||
|
||
return centroids | ||
} | ||
|
||
func initializeRandomCentroids(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) | ||
let cluster = Coordinate.randomGenerate(rangeOfLat: lats, rangeOfLng: lngs) | ||
centroids.append(cluster) | ||
} | ||
|
||
return centroids | ||
} | ||
|
||
func screenCentroids(topLeft: Coordinate, bottomRight: Coordinate) -> [Coordinate] { | ||
func initializeScreenCentroids(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)) | ||
|
@@ -50,7 +111,7 @@ class KMeans { | |
let radian = theta * .pi / Degree.straight | ||
var x: Double | ||
var y: Double | ||
|
||
if theta > pivot { | ||
y = boundary.y | ||
x = y / tan(radian) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
// | ||
// KMeansPerformanceTests.swift | ||
// InteractiveClusteringMapTests | ||
// | ||
// Created by Seungeon Kim on 2020/11/27. | ||
// | ||
|
||
import XCTest | ||
|
||
fileprivate struct Mock { | ||
static let minX = 126.9903617 | ||
static let maxX = 126.9956437 | ||
static let minY = 37.5600365 | ||
static let maxY = 37.5764792 | ||
} | ||
|
||
class KMeansPerformanceTests: XCTestCase { | ||
let group = DispatchGroup() | ||
lazy var coords: [Coordinate] = { () -> [Coordinate] in | ||
var points: [Coordinate] = [] | ||
for _ in 0..<10000 { | ||
points.append(generatePoint()) | ||
} | ||
return points | ||
}() | ||
|
||
func test_performance_measure_kmeans() throws { | ||
measure { | ||
kmeansByScreen(k: 22, coords: coords) | ||
} | ||
} | ||
|
||
func test_performance_measure_kmeans_by_random() throws { | ||
measure { | ||
kmeansByRandom(k: 22, coords: coords) | ||
} | ||
} | ||
|
||
private func asyncNotify(coords: [Coordinate], compltion handler: @escaping ([Coordinate]) -> Void) { | ||
group.notify(queue: DispatchQueue.main) { | ||
handler(coords) | ||
} | ||
} | ||
|
||
private func generatePoint() -> Coordinate { | ||
let lat = Double.random(in: 37.5600365...37.5764792) | ||
let lng = Double.random(in: 126.9903617...126.9956437) | ||
|
||
return Coordinate(x: lng, y: lat) | ||
} | ||
|
||
@discardableResult | ||
private func kmeansByScreen(k: Int, coords: [Coordinate]) -> [Cluster] { | ||
let kmm = KMeans(k: 22) | ||
|
||
let topLeft = Coordinate(x: Mock.minX, y: Mock.maxY) | ||
let bottomRight = Coordinate(x: Mock.maxX, y: Mock.minY) | ||
let centroids = kmm.screenCentroids(topLeft: topLeft, bottomRight: bottomRight) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 메서드 명이 바뀐 거를 깜빡 하셨나 보네요. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵.. 바로 수정했습니다! 😅 |
||
var clusters: [Cluster] = [] | ||
|
||
clusters = kmm.trainCenters(coords, initialCentroids: centroids) | ||
|
||
return clusters | ||
} | ||
|
||
@discardableResult | ||
private func kmeansByRandom(k: Int, coords: [Coordinate]) -> [Cluster] { | ||
let kmm = KMeans(k: 22) | ||
let centroids = kmm.randomCentroids(rangeOfLat: Mock.minY...Mock.maxY, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기도요. |
||
rangeOfLng: Mock.minX...Mock.maxX) | ||
var clusters: [Cluster] = [] | ||
|
||
clusters = kmm.trainCenters(coords, initialCentroids: centroids) | ||
|
||
return clusters | ||
} | ||
|
||
private func timeout(_ timeout: TimeInterval, completion: (XCTestExpectation) throws -> Void) rethrows { | ||
let exp = expectation(description: "Timeout: \(timeout) seconds") | ||
|
||
try completion(exp) | ||
|
||
waitForExpectations(timeout: timeout) { error in | ||
guard let error = error else { return } | ||
XCTFail("Timeout error: \(error)") | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit : var centroids = pickCentroids(k: k, distance: coverage, container: container)
이렇게 하는건 어떤가용 ??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
좋아요!