x > 0 ๋ฐ allow_single_cluster=True
์์ cluster_selection_epsilon=x
#$๋ฅผ ํจ๊ป ์ฌ์ฉํ๋ฉด HDBSCAN์ด ์ถฉ๋ํฉ๋๋ค.
์ ๋ ์ด ๋ ๊ฐ์ง ์ต์
์ ํจ๊ป ์ฌ์ฉํ์ฌ no_structure
์ฅ๋๊ฐ ๋ฐ์ดํฐ ์ธํธ (ํ๋จ ํ, ์ ์ฌ๊ฐํ)๋ฅผ ์ ์ ํ๊ฒ ํด๋ฌ์คํฐ๋งํ๋ ค๊ณ ์๋ํ๊ณ ์์ต๋๋ค. DBSCAN์ด ํ๋ ๊ฒ์ฒ๋ผ ์ฌ๊ฐํ์ด ์์ ํ ํ๋์์ด ๋๊ธฐ๋ฅผ ๋ฐ๋๋๋ค. cluster_selection_epsilon
๋ง ์ฌ์ฉํ๋ฉด ํด๋น ์ฌ๊ฐํ์ ์ฌ๋ฌ ํด๋ฌ์คํฐ๊ฐ ๋ํ๋ฉ๋๋ค. allow_single_cluster=True
๋ง ์ฌ์ฉํ๋ฉด ํด๋น ์ฌ๊ฐํ์ ์ผ๋ถ๊ฐ ํ์์
๋๋ค. ๋ ์ธ์๋ฅผ ๋ชจ๋ ์ฌ์ฉํ์ฌ ์ํ๋ ๊ฒฐ๊ณผ๋ง ์ป์ ์ ์๋ค๊ณ ์๊ฐํ์ง๋ง ๊ทธ๋ ๊ฒ ํ ๋ HDBSCAN์ด ์ถฉ๋ํฉ๋๋ค.
import numpy as np
import hdbscan
if __name__ == '__main__':
no_structure = np.random.rand(1500, 2)
clusterer = hdbscan.HDBSCAN(min_cluster_size=15, cluster_selection_epsilon=3, allow_single_cluster=True)
clusterer.fit(no_structure)
HDBSCAN์ ์ถฉ๋ ์์ด ๋ฐ์ดํฐ๋ฅผ ํด๋ฌ์คํฐ๋งํฉ๋๋ค. ๋ฐ๋์งํ๊ฒ๋ ์ค๋ช ๋ ๋๋ก ๋ชจ๋ ์ ์ ์ ์ฌ๊ฐํ ํ๋์์ผ๋ก ํ์ธํ ํฉ๋๋ค.
HDBSCAN์ ๋ค์ ์ญ์ถ์ ๊ณผ ํจ๊ป ์ถฉ๋ํฉ๋๋ค.
Traceback (most recent call last):
File "/home/home/PycharmProjects/sandbox/crash_example.py", line 7, in <module>
clusterer.fit(no_structure)
File "/home/home/PycharmProjects/sandbox/venv/lib/python3.8/site-packages/hdbscan/hdbscan_.py", line 919, in fit
self._min_spanning_tree) = hdbscan(X, **kwargs)
File "/home/home/PycharmProjects/sandbox/venv/lib/python3.8/site-packages/hdbscan/hdbscan_.py", line 632, in hdbscan
return _tree_to_labels(X,
File "/home/home/PycharmProjects/sandbox/venv/lib/python3.8/site-packages/hdbscan/hdbscan_.py", line 59, in _tree_to_labels
labels, probabilities, stabilities = get_clusters(condensed_tree,
File "hdbscan/_hdbscan_tree.pyx", line 645, in hdbscan._hdbscan_tree.get_clusters
File "hdbscan/_hdbscan_tree.pyx", line 733, in hdbscan._hdbscan_tree.get_clusters
File "hdbscan/_hdbscan_tree.pyx", line 631, in hdbscan._hdbscan_tree.epsilon_search
IndexError: index 0 is out of bounds for axis 0 with size 0
์ ๋ ์ด ๋ฌธ์ ๋ฅผ ๊ฒช๊ณ ์์ต๋๋ค. cluster_selection_method='eom'
๋ฅผ ์ฌ์ฉํ ๋๋ง ๋ฐ์ํฉ๋๋ค.
._hdbscan_tree.pyx์์ root ๋ ธ๋์ ๋ชจ๋ ์์์ด ์์ ํฌ๊ธฐ == 1์ด๊ธฐ ๋๋ฌธ์ cluster_tree๋ ๋น์ด ์์ต๋๋ค. 'leaf' ๋ฉ์๋๋ 'leaf'๋ ๋น์ด ์๋๋ก ์ค์ ๋์ด ์๊ธฐ ๋๋ฌธ์ ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์์ต๋๋ค.
๊ทธ๋ฌ๋ 'eom_clusters'๋ ๋ฃจํธ ๋ ธ๋๋ฅผ ํฌํจํ๊ณ epsilon_search()๋ ์์ ๋ ธ๋๋ฅผ ๊ฒ์ํ๋ cluster_tree์๋ง ์์กดํฉ๋๋ค. ๋ฃจํธ ๋ ธ๋๊ฐ 'eom_clusters' ๋ชฉ๋ก์ ํฌํจ๋์ง ์๋๋ก ํ๊ณ ์ถ์ต๋๊น?
@danielzgtg ๋ฌธ์ ์ ๋ํ ๊ฐ๋ฅํ ์์ ์ฌํญ์ ๋ํ ๋ด ํ ๋ฆฌํ์คํธ๋ฅผ ์ฐธ์กฐํ์ญ์์ค. ๋๋ ๊ทธ๊ฒ์ด ๊ธฐ๋ณธ ์๊ณ ๋ฆฌ์ฆ์ ์ํฅ์ ๋ฏธ์น์ง ์๋๋ค๊ณ ํ์ ํ์ง ์์ง๋ง ํด๊ฒฐ์ฑ ์ ์ฐพ๊ธฐ ์ํด ์ต์ ์ ๋คํ๊ณ ๊ฝค ์์ ํฉ๋๋ค.
๋ฐ๋ผ์ Malzer์ Baum์ HDBSCAN* ๋ ผ๋ฌธ์ ์ฝ์ ํ 370์ ๋ํ ์์ ์ฌํญ์ด ์๊ณ ๋ฆฌ์ฆ์ ์ผ๋ก ์ ํํ์ง ์์ ๊ฒ ๊ฐ์ต๋๋ค. ์์ ์ฌํญ์ ํตํด ํด๋น ๋งค๊ฐ๋ณ์๋ก ์คํํ ์ ์์ง๋ง ์ค์ ๋ก ์๊ณ ๋ฆฌ์ฆ์ด ์ ํํ ํ๋์ ๋จ์ผ ํด๋ฌ์คํฐ๋ฅผ ๋ฐํํ๋ ๊ฒ์ ํ์ฉํ์ง ์์ต๋๋ค.
์น์ 4.3์ ์ค๋ช ์ "๋ฃจํธ์ ๋๋ฌํ๊ธฐ ์ ์ ํ๋๋ฅผ ์ฐพ์ผ๋ฉด ํด๋ฌ์คํฐ๋ก ์ ํํ๊ณ ๋ชจ๋ ํ์ ํญ๋ชฉ์ ์ ํ ์ทจ์"ํ๋ ์ก์ค๋ก ๊ธฐ๋ฐ ๋ณํฉ์ ์ค๋ช ํฉ๋๋ค.
@lmcinnes ๋๋ @cmalzer ๊ฐ "allow_single_cluster"์ "cluster_selection_epsilon"์ ์กฐํฉ์ ๋ํด ๊ณ์ธต์ ์ํฅ ์ํ ๋์ _๋ฃจํธ ๋ ธ๋์ ๋๋ฌํ๋ ๊ฒ์ ํ์ฉํ๊ธฐ๋ฅผ ์ํ๋ค๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๊น? ๋๋ ์๋ํ์ง ์์ ๊ฒฐ๊ณผ๊ฐ ๋ฐ์ํ๋ค๋ฉด? ์ด๊ฒ์ ์ฌ๋ฐ๋ฅธ ์์ง์์ฒ๋ผ ๋ณด์ ๋๋ค.
๋๋ ๊ทธ๊ฒ์ ์ฌ๋ฐ๋ฅด๊ฒ ๊ตฌํํ๊ณ ๋ด๊ฐ๋ณด๊ณ ์๋ ๋ช ๊ฐ์ง ์๋ชป๋ ๋์์ ์์ ํ๋ค๊ณ ์๊ฐํ๋ ์๋ก์ด ์ปค๋ฐ์ ๋ง๋ค์์ต๋๋ค. ์ฒซ ์ปค๋ฐ์ ํ๊ธฐ ์ ์ ๋ ์ฒ ์ ํ ์กฐ์ฌ๋ฅผ ํ์ง ๋ชปํ ์ ์ ๋ํด ์ฌ๊ณผ๋๋ฆฝ๋๋ค.
์๋ ํ์ธ์, ์ง์ ํด ์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค. "allow_single_cluster" ์ต์ ์ ๋ํด cluster_selection_epsilon์ ๊ตฌํํ๋ ๊ฒ์ ์์์ต๋๋ค. (๋ ผ๋ฌธ์ ์ค๋ช ๋ ์๊ณ ๋ฆฌ์ฆ์ ๋ฐ์ดํฐ ์ธํธ๋ฅผ ๋ถํ ํ๋ ค๋ ๊ฒฝ์ฐ์๋ง ๊ณ ๋ ค๋จ)
๊ทธ๋ฌ๋ ์ต์ ์ปค๋ฐ์ ์ดํด๋ณด๊ณ allow_single_cluster๊ฐ True๋ก ์ค์ ๋ ๊ฒฝ์ฐ "traverse_upwards"์์ ๋ฃจํธ๋ฅผ ๋ฐํํ๋ฉด ์ค์ ๋ก ๋ฌธ์ ๊ฐ ํด๊ฒฐ๋ฉ๋๋ค. ๊ฐ์ฌํฉ๋๋ค!
๋น ๋ฅธ ๋ต๋ณ๊ณผ ์ด ์๊ณ ๋ฆฌ์ฆ ์ค๊ณ ์์ ์ ๊ฐ์ฌ๋๋ฆฝ๋๋ค!
@lmcinnes ์ํ๊น๊ฒ๋ ์ด ๋ฌธ์ ๋ ํด๊ฒฐ๋์ง ์์์ต๋๋ค.
๋ด๊ฐ ๋ง๋ ๋ ๋ฒ์งธ pull ์์ฒญ์ ๋ด๊ฐ ์ฌ์ฉํ๋ ๋จ์ ํ ์คํธ๋ฅผ ํต๊ณผํ๊ณ ๋๋ถ๋ถ์ ๊ฒฝ์ฐ ๋จ์ผ ํด๋ฌ์คํฐ๋ฅผ ํ์ฉํ์ง๋ง @danielzgtg์์ ์ ๊ณตํ ์ฝ๋์๋ ์์ธ์ ์ธ ๊ฒฝ์ฐ๊ฐ ์์ต๋๋ค.
๋๋ฒ๊น
์ ์๋ํ๋๋ฐ _hdbscan_tree์์ cluster_tree['lambda_val]
๊ฐ ๋น์ด ์์ ๋ ๋ฌธ์ ๊ฐ ์๋ ๊ฒ์ผ๋ก ๋ณด์
๋๋ค. pyx:638 . ๊ทธ๋๋ cython์ ๋๋ฒ๊น
ํ๋ ๋ฐ ๋ฌธ์ ๊ฐ ์์ต๋๋ค(์ฌ์ ํ cython์ ๋งค์ฐ ์ต์ํ์ง ์์). ๋๊ตฐ๊ฐ ์ด ์กฐ์ฌ๋ฅผ ๋์ธ ์๊ฐ์ด ์๋ค๋ฉด ๋๋จํ ๊ฐ์ฌํ๊ฒ ์ต๋๋ค.
๋๋ ๋ฐฉ๊ธ ๊ทธ๊ฒ์ ๋ณด์๋ค. ๋ฌธ์ ๋ ์ค์ ๋ก ์ด ์์ ์์ cluster_tree๊ฐ ๋น์ด ์๋ค๋ ๊ฒ์ ๋๋ค. ๋ฐ๋ผ์ cluster_tree['lambda_val'][cluster_tree['child'] == leaf] ๋ ๋น ๋ฐฐ์ด์ด๋ฉฐ ์ฒซ ๋ฒ์งธ ์์([0])์ ์ก์ธ์คํ๋ ค๊ณ ํ๋ฉด IndexError๊ฐ ๋ฐ์ํฉ๋๋ค.
cluster_tree๊ฐ ๋น์ด ์๋ ์ด์ ๋ "cluster_tree = tree[tree['child_size'] > 1]" ํ ๋๋ฌธ์ ๋๋ค. ํธ๋ฆฌ์๋ ํฌ๊ธฐ๊ฐ 1์ธ ์์์ด ์๋ ๋ถ๋ชจ ๋ ธ๋๋ง ํฌํจ๋ฉ๋๋ค.
์ด ์ฝ๋๋ ๋ฆฌํ ๋ชฉ๋ก์ด ๋น์ด ์๊ธฐ ๋๋ฌธ์ cluster_selection_method="leaf"์ ๋ํด ์๋ํฉ๋๋ค. "eom"์ ๊ฒฝ์ฐ ์์ ํด๋ฌ์คํฐ๋ "๋ฆฌํ"๋ก ์ ๋ฌ๋ฉ๋๋ค.
์ด๊ฒ์ epsilon_search๋ฅผ ์ํํ๊ธฐ ์ ์ cluster_tree๊ฐ ๋น์ด ์๋์ง ํ์ธํ์ฌ ํด๊ฒฐํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, "๋ง์ฝ cluster_selection_epsilon != 0.0 and cluster_tree.shape[0] > 0: ... "
์กฐ๊ฑด๋ถ cluster_tree.shape[0] > 0
์ ์์ผ๋ก ํ
์คํธํ์ง๋ง ์คํจํ์ต๋๋ค. ์ฝ๊ฐ์ printf ๋๋ฒ๊น
์ ์ํํ ํ cluster_tree์๋ ~26๊ฐ์ ์์๊ฐ ์์๊ณ eom_clusters์๋ ๋ฃจํธ ๋
ธ๋์ธ ์ ํํ ํ๋์ ์์๊ฐ ์์ต๋๋ค. ๋ฐ๋ผ์ 637ํ์์ cluster_tree['child'] == leaf
๋ฅผ ํ๊ฐํ ๋ ๋ฆฌํ๋ ๋ฃจํธ ๋
ธ๋์ด๊ณ ํ์ ํญ๋ชฉ๊ณผ ์ผ์นํ์ง ์์ต๋๋ค.
์ด๊ฑด ์ด๋์?
cpdef set epsilon_search(set leaves, np.ndarray cluster_tree, np.double_t cluster_sel\
ection_epsilon, np.intp_t allow_single_cluster):
selected_clusters = list()
processed = list()
root_node = cluster_tree['parent'].min()
for leaf in leaves:
if leaf == root_node:
if allow_single_cluster:
selected_clusters.append(leaf)
break
continue
eps = 1/cluster_tree['lambda_val'][cluster_tree['child'] == leaf][0]
if eps < cluster_selection_epsilon:
if leaf not in processed:
์์งํ ์ข ํท๊ฐ๋ฆฝ๋๋ค. ๋จ์ผ ํด๋ฌ์คํฐ๋ฅผ ํ์ฉํ๋ ค๋ ๊ฒฝ์ฐ ๋ ธ์ด์ฆ ํด๋ฌ์คํฐ๋ง ํ์ฉํด์ผ ํฉ๋๊น? ์ด ๊ฒฝ์ฐ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
cpdef set epsilon_search(set leaves, np.ndarray cluster_tree, np.double_t cluster_sel\
ection_epsilon, np.intp_t allow_single_cluster):
selected_clusters = list()
processed = list()
root_node = cluster_tree['parent'].min()
for leaf in leaves:
if leaf == root_node:
if not allow_single_cluster:
selected_clusters.append(leaf)
break
eps = 1/cluster_tree['lambda_val'][cluster_tree['child'] == leaf][0]
if eps < cluster_selection_epsilon:
์๋ชป๋ ์์ ์์ ๋ฐ์ดํฐ ํฌ์ธํธ๋ก ํ
์คํธํ ๋ค์ ๋์ผํ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ง๋ง ์์ธ์ ๋ค๋ฅธ ๊ฒ ๊ฐ์ต๋๋ค.
์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ์๋ ์ฌ๋ฌ ๊ฐ์ง๊ฐ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด ๋ค์๊ณผ ๊ฐ์ด epsilon_search ์ธ๋ถ์์ ๊ฒ์ฌ๋ฅผ ์ํํ๋ ๊ฒ์ด ์ค์ ๋ก ์กฐ๊ธ ๋ ๋น ๋ฅผ ์ ์์ต๋๋ค.
if cluster_selection_epsilon != 0.0 and cluster_tree.shape[0] > 0:
eom_clusters = [c for c in is_cluster if is_cluster[c]]
if not (len(eom_clusters) == 1 and eom_clusters[0] == cluster_tree['parent'].min()):
selected_clusters = epsilon_search(set(eom_clusters), cluster_tree, cluster_selection_epsilon, allow_single_cluster)
(์์ ์ด ๋ง์ ์ฃ์กํฉ๋๋ค)
๋น ๋ฅธ ์๋ต์ ๊ฐ์ฌ๋๋ฆฝ๋๋ค! ๊ทธ๊ฑฐ ์ข์ ์๊ฐ์ด์ผ. ์ค๋ ๋ฐค์ ํ ๋ฆฌํ์คํธ๋ฅผ ํ ๊ฒ์.
@cmalzer
๋๋ ๋น์ ์ ์ ์์ผ๋ก ํ ๋ฆฌํ์คํธ๋ฅผ ์ฌ๋ ธ์ต๋๋ค.
epsilon_search()
๋ฉ์๋ ๋ด๋ถ์์ ํ์ธ์ ํ ์ด์ ๋ ๋ฃจํธ ๋
ธ๋๊ฐ eom_clusters
์ ์์ ๋ ํด๋น ๋ชฉ๋ก์ ์ ์ผํ ํญ๋ชฉ์ด๋ผ๋ ๊ฒ์ ๋ณด์ฅํ ์ ์๋์ง ํ์ ํ ์ ์์๊ธฐ ๋๋ฌธ์
๋๋ค. ๊ทธ๋ ์ง ์๊ณ ํ ๊ฐ์ง ๊ฒฝ์ฐ๋ง ํ์ธํ๋ฉด ๋ฃจํธ ๋
ธ๋๊ฐ eom_clusters
๋ชฉ๋ก์ ๋ค๋ฅธ ์์ด ํผํฉ๋์ด ์๋ ๊ฒฝ์ฐ epsilon_search()
์์ ์ฌ์ ํ ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค.
์ฐ๋ฆฌ๊ฐ ๊ทธ ๊ฐ์ ์ ํ ์ ์๋ค๋ฉด ๋๋ ์กฐ๊ฑด๋ถ๋ฅผ ๋ค์์ผ๋ก ๋ณ๊ฒฝํ ๊ฒ์ ๋๋ค.
if (cluster_tree['parent'].min() in eom_clusters)
๋ฃจํธ๊ฐ ๋ชฉ๋ก์ ์ ํ ์กด์ฌํ์ง ์๊ณ ์ด๋ฅผ ์ถ๋ ฅ์ผ๋ก ํ์ฉํ๋ฉด ๊ธฐ๋ณธ์ ์ผ๋ก ๊ฒ์์์ ๋ฐํ๋๋ ์ ์ผํ ์ก์ค๋ก ์์์ด ๋๊ธฐ ๋๋ฌธ์ ๋๋ค.
HDBSCAN์ด ํด๋ฌ์คํฐ๋ฅผ ์ ํํ๋ฉด ๋ชจ๋ ํ์ ํญ๋ชฉ๋ ์ ํ ์ทจ์ํฉ๋๋ค. ๋ฐ๋ผ์ ์ค์ ๋ก ๋ฃจํธ ํด๋ฌ์คํฐ์ ๋ค๋ฅธ ํด๋ฌ์คํฐ๊ฐ ๋ชฉ๋ก์ ์๋ ๊ฒฝ์ฐ๋ ๋ฐ์ํ์ง ์์์ผ ํฉ๋๋ค. ๊ทธ๋ฌ๋ "if .... in eom_clusters"์ ๋ํ ๊ทํ์ ์ ์์ ์ ๋ฒ์ ๋ณด๋ค ํจ์ฌ ์ฝ๊ธฐ ์ฝ๊ธฐ ๋๋ฌธ์ ์ค์ ๋ก ํด๋น ์ฝ๋ ํ์ ์ ํธํฉ๋๋ค. ๋๋ฌธ ๊ฒฝ์ฐ์ง๋ง ์ก์ค๋ก ๋งค๊ฐ๋ณ์๋ฅผ ์ฌ์ฉํ ๋๋ง๋ค ๋ฃจํธ ๋ ธ๋์ ๋ํ ์ ์ฒด ํด๋ฌ์คํฐ ๋ชฉ๋ก์ ๋๋ค. ํ์ง๋ง ์ฑ๋ฅ ์ฐจ์ด๊ฐ ์ผ๋ง๋ ๋๋์ง ์ ๋ชจ๋ฅด๊ฒ ์ต๋๋ค... (์ด์จ๋ ๊ธธ์ด๋ฅผ ํ์ธํ๋ ๋ฐ ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆฌ๋์?) ์ ๋ ์ด๋ ์ชฝ์ด๋ ๊ด์ฐฎ์ต๋๋ค.
์ค๋ช ํด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค! ์๋ง๋ ์ค๊ฐ ์ง์ ์ผ๋ก ์ฑ๋ฅ์ ์ํด ๊ทธ๋๋ก๋๊ณ ์์ ์ฃผ์์ ๋ฌ ๊ฒ์ด๋ผ๊ณ ์๊ฐํฉ๋๋ค.