|
|
Density-Based Spatial Clustering of Applications with Noise (DBSCAN) is an unsupervised learning method. It groups data points that are close to each other while also identifying those data points that are in sparse regions. This method does not require the number of clusters to be known in advance, unlike the k-means method. This method can identify clusters of varying shapes, and outliers can also be identified.
Complete step-by-step calculation using 10 customers, 4 independent variables, ε = 2.0, and MinPts = 3.
We use the same four independent variables as the K-Means example:
| Customer | Income | Spending | Purchases | Engagement |
|---|---|---|---|---|
| C1 | 2 | 2 | 2 | 2 |
| C2 | 3 | 2 | 2 | 3 |
| C3 | 2 | 3 | 2 | 2 |
| C4 | 3 | 3 | 3 | 3 |
| C5 | 7 | 7 | 7 | 7 |
| C6 | 8 | 7 | 8 | 7 |
| C7 | 7 | 8 | 7 | 8 |
| C8 | 8 | 8 | 8 | 8 |
| C9 | 4 | 9 | 4 | 9 |
| C10 | 5 | 8 | 5 | 9 |
A customer is considered a neighbor when its Euclidean distance is ≤ 2.0.
A point needs at least 3 points in its ε-neighborhood, including itself, to be a core point.
With four independent variables:
For DBSCAN, we are primarily interested in whether each distance is ≤ ε = 2.0.
| Pair | Distance | ≤ 2.0? |
|---|---|---|
| C1 – C2 | 1.414 | Yes |
| C1 – C3 | 1.000 | Yes |
| C1 – C4 | 2.000 | Yes |
| C1 – C5 | 10.000 | No |
| C1 – C6 | 11.045 | No |
| C1 – C7 | 11.045 | No |
| C1 – C8 | 12.000 | No |
| C1 – C9 | 10.296 | No |
| C1 – C10 | 10.149 | No |
| C2 – C3 | 1.414 | Yes |
| C2 – C4 | 1.414 | Yes |
| C3 – C4 | 1.414 | Yes |
| C5 – C6 | 1.414 | Yes |
| C5 – C7 | 1.414 | Yes |
| C5 – C8 | 2.000 | Yes |
| C6 – C7 | 2.000 | Yes |
| C6 – C8 | 1.414 | Yes |
| C7 – C8 | 1.414 | Yes |
| C9 – C10 | 1.732 | Yes |
The ε-neighborhood contains the customer itself plus every customer whose distance is ≤ 2.0.
| Customer | ε-Neighborhood | Number of Points | Core? |
|---|---|---|---|
| C1 | C1, C2, C3, C4 | 4 | Yes |
| C2 | C1, C2, C3, C4 | 4 | Yes |
| C3 | C1, C2, C3, C4 | 4 | Yes |
| C4 | C1, C2, C3, C4 | 4 | Yes |
| C5 | C5, C6, C7, C8 | 4 | Yes |
| C6 | C5, C6, C7, C8 | 4 | Yes |
| C7 | C5, C6, C7, C8 | 4 | Yes |
| C8 | C5, C6, C7, C8 | 4 | Yes |
| C9 | C9, C10 | 2 | No |
| C10 | C9, C10 | 2 | No |
DBSCAN defines a core point when:
Here:
C1–C8
Each has at least 3 points in its ε-neighborhood, including itself.
None
A border point is not core itself but lies within ε of a core point.
C9, C10
Neither is core, and neither lies within ε = 2.0 of any core point.
| Customer | Income | Spending | Purchases | Engagement | ε Neighbors | Type | Cluster |
|---|---|---|---|---|---|---|---|
| C1 | 2 | 2 | 2 | 2 | 4 | Core | 1 |
| C2 | 3 | 2 | 2 | 3 | 4 | Core | 1 |
| C3 | 2 | 3 | 2 | 2 | 4 | Core | 1 |
| C4 | 3 | 3 | 3 | 3 | 4 | Core | 1 |
| C5 | 7 | 7 | 7 | 7 | 4 | Core | 2 |
| C6 | 8 | 7 | 8 | 7 | 4 | Core | 2 |
| C7 | 7 | 8 | 7 | 8 | 4 | Core | 2 |
| C8 | 8 | 8 | 8 | 8 | 4 | Core | 2 |
| C9 | 4 | 9 | 4 | 9 | 2 | Noise | -1 |
| C10 | 5 | 8 | 5 | 9 | 2 | Noise | -1 |
C1–C4
These customers have relatively low income, low spending, few purchases, and low engagement.
Possible actions:
C5–C8
These customers have high income, high spending, frequent purchases, and high engagement.
Possible actions:
C9, C10
These customers spend heavily and are highly engaged, but they do not have enough nearby customers to form a dense DBSCAN cluster under the chosen parameters.
Possible actions:
| Feature | K-Means | DBSCAN |
|---|---|---|
| Number of clusters required? | Yes — K | No |
| Main parameters | K | ε and MinPts |
| Uses centroids? | Yes | No |
| Handles noise/outliers? | Poorly | Yes |
| Cluster shape | Generally spherical | Arbitrary shapes |
| Distance concept | Distance to centroid | Density/neighborhood |
Notice that DBSCAN produced a result that is different from the K-Means example.
With K-Means, C9 and C10 were placed into their own cluster because we explicitly requested K = 3.
With DBSCAN, we did not request three clusters. DBSCAN examined the density of the data and found that C9 and C10 did not have enough nearby points to satisfy MinPts = 3.
import pandas as pd from sklearn.preprocessing import StandardScaler from sklearn.cluster import DBSCAN import numpy as np # Load data # download from: # https://www.pythonplaza.com/healthcare_patient_dataset.html df = pd.read_csv("patients_data.csv") # Features used for clustering X = df[['Age', 'BMI', 'Blood_Pressure', 'Cholesterol', 'Hospital_Visits']] # Scale data scaler = StandardScaler() X_scaled = scaler.fit_transform(X) # Train DBSCAN model dbscan = DBSCAN(eps=1.5, min_samples=5) df['Cluster'] = dbscan.fit_predict(X_scaled) # Print cluster assignments print(df[['Cluster']].value_counts()) # New patient? new_patient = [[33, 28.0, 139, 210, 4]] # Apply SAME scaling new_patient_scaled = scaler.transform(new_patient) # ------------------------------ # DBSCAN prediction logic # ------------------------------ # DBSCAN has NO predict() method # We assign cluster using nearest core point (common approach) labels = df['Cluster'].values # Remove noise points (-1) valid_points = X_scaled[labels != -1] valid_labels = labels[labels != -1] # Compute distances from new patient to all points distances = np.linalg.norm(valid_points - new_patient_scaled, axis=1) # Find nearest point nearest_index = np.argmin(distances) cluster = valid_labels[nearest_index] print("Patient belongs to Cluster:", cluster)
USE CASE 2: Use DBSCAN for customer segmentation in Market Basket Analysis. Instead of finding which products are purchased together (like Apriori or FP-Growth), use DBSCAN to group customers based on their purchasing behavior. Once customers are clustered, you can create targeted promotions and personalized recommendations for each segment.
import pandas as pd
from sklearn.cluster import DBSCAN
from sklearn.preprocessing import StandardScaler
import numpy as np
# ----------------------------------
# Step 1: Sample Market Basket Data
# ----------------------------------
data = pd.DataFrame({
'Customer': ['C001','C002','C003','C004','C005','C006','C007','C008'],
'Bread': [12,10,11,1,0,2,6,5],
'Milk': [10,8,9,2,1,1,5,6],
'Eggs': [8,7,6,1,2,0,4,5],
'Beer': [0,1,0,10,12,9,4,5],
'Chips': [1,0,1,8,10,7,3,4]
})
# Load data (if using file instead)
# data = pd.read_csv("customer_shopping.csv")
print("Original Data")
print(data)
# ----------------------------------
# Step 2: Select Features
# ----------------------------------
X = data[['Bread', 'Milk', 'Eggs', 'Beer', 'Chips']]
# ----------------------------------
# Step 3: Scale Features
# ----------------------------------
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# ----------------------------------
# Step 4: Train DBSCAN Model
# ----------------------------------
dbscan = DBSCAN(eps=1.5, min_samples=2)
data['Cluster'] = dbscan.fit_predict(X_scaled)
print("\nCluster Assignments")
print(data[['Customer', 'Cluster']])
# ----------------------------------
# Step 5: Cluster Profiles
# ----------------------------------
print("\nCluster Profiles (Mean of Original Data)")
cluster_profiles = data.groupby('Cluster')[['Bread','Milk','Eggs','Beer','Chips']].mean()
print(cluster_profiles.round(2))
# ----------------------------------
# Step 6: Test New Customer
# ----------------------------------
new_customer = pd.DataFrame({
'Bread': [11],
'Milk': [9],
'Eggs': [7],
'Beer': [1],
'Chips': [1]
})
new_customer_scaled = scaler.transform(new_customer)
# ----------------------------------
# DBSCAN has NO predict()
# So we assign cluster by nearest neighbor
# ----------------------------------
X_scaled_arr = np.array(X_scaled)
labels = np.array(data['Cluster'])
# Remove noise points (-1)
valid_mask = labels != -1
valid_X = X_scaled_arr[valid_mask]
valid_labels = labels[valid_mask]
# Compute distances to all valid points
distances = np.linalg.norm(valid_X - new_customer_scaled, axis=1)
nearest_index = np.argmin(distances)
predicted_cluster = valid_labels[nearest_index]
print("\nNew Customer")
print(new_customer)
print(f"\nPredicted Cluster: {predicted_cluster}")
# ----------------------------------
# Step 7: Recommendation Logic
# ----------------------------------
if predicted_cluster == 0:
print("Recommendation: Bread, Milk, Eggs promotions")
elif predicted_cluster == 1:
print("Recommendation: Beer and Chips promotions")
else:
print("Recommendation: Mixed basket offers")