如何优化K-Means聚类结果的有序可视化实现方案?
Hey there! Your K-Means customer segmentation workflow is solid, but those nested conditionals and hardcoded legend logic can definitely be cleaned up to make the code more flexible and maintainable. Let's walk through optimizing each part step by step:
1. Simplify the order_cluster Function
Your original function works, but we can eliminate the merge step entirely by using a dictionary to map old cluster labels to their ordered ranks. This is faster and cleaner:
def order_cluster(cluster_field_name, target_field_name, df, ascending=True): # Calculate mean of the target metric for each cluster cluster_means = df.groupby(cluster_field_name)[target_field_name].mean() # Sort clusters by their mean target value and get the ordered cluster IDs ordered_clusters = cluster_means.sort_values(ascending=ascending).index # Create a mapping from original cluster ID to new ordered rank (0,1,2...) cluster_map = {old_cluster: new_rank for new_rank, old_cluster in enumerate(ordered_clusters)} # Apply the mapping to update the cluster field df[cluster_field_name] = df[cluster_field_name].map(cluster_map) return df
This does the same job as your original function but cuts out the intermediate dataframe merges and renames—much more efficient!
2. Dynamically Generate the Rank_ID Column
That nested if-else chain for creating Rank_ID is a maintenance nightmare when you change K. Instead, we can generate the group labels dynamically using string formatting and character manipulation. Since your Rank values are ordered from 0 (lowest TOTAL_SALES) to K-1 (highest), we can map them directly to Group_A (highest) down to Group_*:
k = 4 # Your chosen number of clusters data['Rank_ID'] = data['Rank'].apply(lambda x: f"Group_{chr(ord('A') + (k-1 - x))}")
For example, with K=4:
- Rank=3 (highest sales) → Group_A
- Rank=2 → Group_B
- Rank=1 → Group_C
- Rank=0 → Group_D
This works for any K up to 26 (since we're using letters A-Z), and you never have to touch the code when changing cluster counts.
3. Eliminate the build_legend Function
You don't need a hardcoded function to generate the legend order—we can create it dynamically based on K, matching the Rank_ID labels we just generated:
legend_order = [f"Group_{chr(ord('A') + i)}" for i in range(k)]
This will produce exactly the ordered list of group labels you need, no matter what K you pick.
Full Optimized Code
Putting it all together, here's the streamlined version of your workflow:
import pandas as pd from sklearn.cluster import KMeans import seaborn as sns import matplotlib.pyplot as plt # Simplified cluster ordering function def order_cluster(cluster_field_name, target_field_name, df, ascending=True): cluster_means = df.groupby(cluster_field_name)[target_field_name].mean() ordered_clusters = cluster_means.sort_values(ascending=ascending).index cluster_map = {old: new for new, old in enumerate(ordered_clusters)} df[cluster_field_name] = df[cluster_field_name].map(cluster_map) return df # Sample data (matching your example) data = pd.DataFrame({ 'CUSTOMER_ID': ['A','B','C','D','E','F','G','H'], 'ORDERS_PLACED': [2,106,13,43,53,1,3,1], 'UNITS_SOLD': [59,184,78,2034,582,6,60,1], 'AVG_UNIT_PRICE': [21553.9,6295.9,14290,245.38,760.92,15000,967,1807], 'TOTAL_SALES': [1271680,1158445.7,1114620,499102,442856,90000,58020,1807] }) # K-Means clustering k = 4 # Adjust this value as needed kmeans = KMeans(n_clusters=k, random_state=42) # Add random_state for reproducibility data['Rank'] = kmeans.fit_predict(data[['ORDERS_PLACED','UNITS_SOLD','AVG_UNIT_PRICE','TOTAL_SALES']]) # Order clusters by TOTAL_SALES (ascending=True means 0 = lowest, k-1 = highest) data = order_cluster('Rank','TOTAL_SALES',data,True) # Dynamically generate Rank_ID labels data['Rank_ID'] = data['Rank'].apply(lambda x: f"Group_{chr(ord('A') + (k-1 - x))}") # Plotting with dynamic legend order legend_order = [f"Group_{chr(ord('A') + i)}" for i in range(k)] fig, ax = plt.subplots(figsize=(12,5)) plot = sns.scatterplot( x='ORDERS_PLACED', y='TOTAL_SALES', hue='Rank_ID', size='Rank_ID', hue_order=legend_order, size_order=legend_order, data=data, ax=ax ) # Format y-axis ticks ytick = plot.get_yticks() plot.set_yticklabels(['{:,.0f}'.format(x) for x in ytick]) plot.set_title('80/20 Customer Segmentation Using K-Means Clustering, Plot on Orders Placed & Total Sales',fontsize=12) plt.legend(bbox_to_anchor=(1.05, 1), loc=2) plt.show()
Key Improvements
- Flexibility: Works for any number of clusters K without modifying hardcoded logic
- Maintainability: No nested conditionals or hardcoded legend lists to update
- Efficiency: Removed unnecessary dataframe merges in the cluster ordering step
- Reproducibility: Added
random_stateto KMeans so your clustering results are consistent
内容的提问来源于stack exchange,提问作者Dru

