0
votes

Matrice de distance précalculée dans DBSCAN

En lisant, je trouve qu'il est possible de passer une matrice de distance précalculée dans SKLearn DBSCAN . Malheureusement, je ne sais pas comment le transmettre pour le calcul.

Disons que j'ai un tableau 1D avec 100 éléments, avec juste les noms des nœuds. Ensuite, j'ai une matrice 2D, 100x100 avec la distance entre chaque élément (dans le même ordre).

Je sais que je dois l'appeler:

db = DBSCAN (eps = 2, min_samples = 5, metric = "précalculé")

Pour une distance entre les nœuds de 2 et un minimum de 5 clusters de nœuds. Utilisez également «précalculé» pour indiquer d'utiliser la matrice 2D. Mais comment transmettre les informations pour le calcul?

La même question pourrait s'appliquer si vous utilisez RAPIDS CUML DBScan fonction (accéléré par GPU).


0 commentaires

3 Réponses :


1
votes

documentation :

from sklearn.cluster import DBSCAN

clustering = DBSCAN(metric='precomputed')
clustering.fit(distance_matrix)
from sklearn.cluster import DBSCAN

clustering = DBSCAN()
DBSCAN.fit(X)

Donc, la façon dont vous appelez cela normalement est:

[...]
metricstring, or callable, default=’euclidean’
The metric to use when calculating distance between instances in a feature array. If 
metric is a string or callable, it must be one of the options allowed by 
sklearn.metrics.pairwise_distances for its metric parameter. If metric is 
“precomputed”, X is assumed to be a distance matrix and must be square. X may be a 
Glossary, in which case only “nonzero” elements may be considered neighbors for  
DBSCAN.
[...]

si vous avez une matrice de distance, vous faites:

class sklearn.cluster.DBSCAN(eps=0.5, *, min_samples=5, metric='euclidean', 
metric_params=None, algorithm='auto', leaf_size=30, p=None, n_jobs=None)
[...]


2 commentaires

Donc, X sera la matrice carrée avec les distances paires, non? Ne devrait pas être métrique = "précalculé" au lieu de métrique = 'fixe'?


pourriez-vous s'il vous plaît revoir les détails que je donne ci-dessous? Merci



0
votes

D'accord, j'ai essayé votre suggestion, mais cela ne fonctionne pas. Je suis cet article:

https://kanoki.org/2019/12/27/how-to-calculate-distance-in-python-and-pandas-using-scipy-spatial -and-distance-functions /

Voici mon état actuel du code. Lors du calcul de la distance dans le cadre de l'appel DBSCAN, cela fonctionne comme un champion. En essayant la même chose avec précalculé, j'obtiens une erreur.

           791        794        798        1124       1125
791    0.000000   6.091447  35.342980  42.952046  29.158508
794    6.091447   0.000000  29.394745  39.452365  23.151700
798   35.342980  29.394745   0.000000  41.346497  12.675131
1124  42.952046  39.452365  41.346497   0.000000  29.392357
1125  29.158508  23.151700  12.675131  29.392357   0.000000

Voici l'erreur que j'obtiens:

  File "./DBSCAN.py", line 41, in <module>
    db.fit(matrix)
  File "/Users/jnebrera/.pyenv/versions/3.8.3/lib/python3.8/site-packages/sklearn/cluster/_dbscan.py", line 330, in fit
    neighbors_model = NearestNeighbors(
  File "/Users/jnebrera/.pyenv/versions/3.8.3/lib/python3.8/site-packages/sklearn/utils/validation.py", line 73, in inner_f
    return f(**kwargs)
  File "/Users/jnebrera/.pyenv/versions/3.8.3/lib/python3.8/site-packages/sklearn/neighbors/_unsupervised.py", line 113, in __init__
    super().__init__(
  File "/Users/jnebrera/.pyenv/versions/3.8.3/lib/python3.8/site-packages/sklearn/neighbors/_base.py", line 305, in __init__
    self._check_algorithm_metric()
  File "/Users/jnebrera/.pyenv/versions/3.8.3/lib/python3.8/site-packages/sklearn/neighbors/_base.py", line 332, in _check_algorithm_metric
    raise ValueError("Metric '%s' not valid. Use "
ValueError: Metric 'precomputed' not valid. Use sorted(sklearn.neighbors.VALID_METRICS['ball_tree']) to get valid options. Metric can also be a callable function.

Ceci est la matrice de distance que j'ai:

#!/usr/bin/env python3

import pandas as pd, numpy as np
from numpy import load
from sklearn.neighbors import DistanceMetric
from sklearn.cluster import DBSCAN
from geopy.distance import great_circle
from shapely.geometry import MultiPoint
from numpy import save

kms_per_radian = 6371.0088

#Loading GPS coordinates array
cities_df = pd.read_csv('geo.csv',delimiter=';',header=0)

reducida = cities_df.copy()
coords = reducida.drop(columns=['ID'])

#Transforming to radians
cities_df['lat'] = np.radians(cities_df['lat'])
cities_df['lon'] = np.radians(cities_df['lon'])

#Function to compute distance. This is simulated, in reality we will use a different procedure
dist = DistanceMetric.get_metric('haversine')

cities_df[['lat','lon']].to_numpy()

matrix = pd.DataFrame(dist.pairwise(cities_df[['lat','lon']].to_numpy())*kms_per_radian,  columns=cities_df.ID.unique(), index=cities_df.ID.unique())

# Saving distance matrix to disk
# save('data.npy',matrix)


epsilon = 1 / kms_per_radian

# Load matrix
#matrix = load('data.npy')

# Computing clusters with precomputed matrix, DOESN'T WORK
db = DBSCAN(eps=epsilon, min_samples=1, algorithm='ball_tree', metric='precomputed')
db.fit(distancias)

# Instead if I use the embedded haversine function IT DOES WORK. Should have the same result
# db = DBSCAN(eps=epsilon, min_samples=1, algorithm='ball_tree', metric='haversine').fit(np.radians(coords))


cluster_labels = db.labels_

num_clusters = len(set(cluster_labels))

clusters = pd.Series([coords[cluster_labels == n] for n in range(num_clusters)])

print('Number of clusters: {}'.format(num_clusters))

Ceci est juste un sous-ensemble des données, j'ai en réalité une matrice beaucoup plus grande. Les nombres sont les ID des colonnes et des lignes (peut-être que l'erreur est là)


4 commentaires

vous ne définissez pas de distancias dans votre code. Pouvez-vous modifier votre question / réponse s.t. il est défini? Pouvez-vous également vérifier le type (distancias) ?


De plus, quelle version de sklearn avez-vous? Essayez: import sklearn puis sklearn .__ version__


Désolé, une erreur de copier-coller s'est produite. Il faudrait dire matrice au lieu de distances (en fait, l'erreur qu'elle produit est déjà bien copiée et dit db.fit (matrice)


La version de sklearn est la 0.23.1



0
votes

Voici ce que j'obtiens en exécutant:

pip install -U scikit-learn
Requirement already up-to-date: scikit-learn in /Users/jnebrera/.pyenv/versions/3.8.3/lib/python3.8/site-packages (0.23.1)
Requirement already satisfied, skipping upgrade: joblib>=0.11 in /Users/jnebrera/.pyenv/versions/3.8.3/lib/python3.8/site-packages (from scikit-learn) (0.15.1)
Requirement already satisfied, skipping upgrade: threadpoolctl>=2.0.0 in /Users/jnebrera/.pyenv/versions/3.8.3/lib/python3.8/site-packages (from scikit-learn) (2.1.0)
Requirement already satisfied, skipping upgrade: scipy>=0.19.1 in /Users/jnebrera/.pyenv/versions/3.8.3/lib/python3.8/site-packages (from scikit-learn) (1.5.0)
Requirement already satisfied, skipping upgrade: numpy>=1.13.3 in /Users/jnebrera/.pyenv/versions/3.8.3/lib/python3.8/site-packages (from scikit-learn) (1.19.0)


0 commentaires