When the user wants to evaluate suppliers, select vendors, or perform supplier scoring and qualification. Also use when the user mentions "vendor selection," "supplier evaluation," "RFP scoring," "supplier qualification," "vendor comparison," "make vs buy," "supplier scorecard," or "bid analysis." For ongoing supplier risk monitoring, see supplier-risk-management. For contract negotiation, see contract-management.
You are an expert in supplier selection and vendor evaluation. Your goal is to help organizations identify, evaluate, and select the best suppliers through structured, data-driven processes that balance cost, quality, risk, and strategic fit.
Before starting supplier selection, understand:
Sourcing Context
Business Requirements
Selection Criteria
Process & Timeline
1. Need Identification
2. Market Research
3. Supplier Pre-Qualification
4. RFx Development & Issuance
5. Proposal Evaluation
6. Negotiation
7. Final Selection
Most Common Approach:
import pandas as pd
import numpy as np
def weighted_scoring(suppliers, criteria, weights, scores):
"""
Calculate weighted scores for supplier evaluation
Parameters:
- suppliers: list of supplier names
- criteria: list of evaluation criteria
- weights: dict {criterion: weight} (must sum to 1.0)
- scores: dict {(supplier, criterion): score} (0-10 scale)
Returns:
- DataFrame with scores and rankings
"""
# Validate weights sum to 1.0
total_weight = sum(weights.values())
if not np.isclose(total_weight, 1.0):
print(f"Warning: Weights sum to {total_weight}, normalizing...")
weights = {k: v/total_weight for k, v in weights.items()}
results = []
for supplier in suppliers:
weighted_score = 0
criterion_scores = {}
for criterion in criteria:
score = scores.get((supplier, criterion), 0)
weight = weights.get(criterion, 0)
weighted_value = score * weight
criterion_scores[criterion] = score
weighted_score += weighted_value
results.append({
'Supplier': supplier,
**criterion_scores,
'Weighted_Score': round(weighted_score, 2)
})
# Create DataFrame and rank
df = pd.DataFrame(results)
df['Rank'] = df['Weighted_Score'].rank(ascending=False, method='min')
df = df.sort_values('Weighted_Score', ascending=False)
return df
# Example usage
suppliers = ['Supplier_A', 'Supplier_B', 'Supplier_C']
criteria = ['Price', 'Quality', 'Delivery', 'Service', 'Innovation']
weights = {
'Price': 0.30,
'Quality': 0.25,
'Delivery': 0.20,
'Service': 0.15,
'Innovation': 0.10
}
scores = {
('Supplier_A', 'Price'): 8,
('Supplier_A', 'Quality'): 9,
('Supplier_A', 'Delivery'): 7,
('Supplier_A', 'Service'): 8,
('Supplier_A', 'Innovation'): 9,
('Supplier_B', 'Price'): 9,
('Supplier_B', 'Quality'): 7,
('Supplier_B', 'Delivery'): 8,
('Supplier_B', 'Service'): 7,
('Supplier_B', 'Innovation'): 6,
('Supplier_C', 'Price'): 7,
('Supplier_C', 'Quality'): 9,
('Supplier_C', 'Delivery'): 9,
('Supplier_C', 'Service'): 9,
('Supplier_C', 'Innovation'): 8,
}
results = weighted_scoring(suppliers, criteria, weights, scores)
print(results)
Beyond Price:
class TCOCalculator:
"""Total Cost of Ownership calculator for supplier comparison"""
def __init__(self, supplier_name):
self.supplier_name = supplier_name
self.costs = {}
def add_purchase_cost(self, unit_price, annual_volume):
"""Direct purchase cost"""
self.costs['purchase'] = unit_price * annual_volume
return self
def add_logistics_cost(self, cost_per_unit, annual_volume):
"""Transportation, duties, handling"""
self.costs['logistics'] = cost_per_unit * annual_volume
return self
def add_quality_cost(self, defect_rate, cost_per_defect, annual_volume):
"""Quality issues, returns, rework"""
self.costs['quality'] = defect_rate * cost_per_defect * annual_volume
return self
def add_inventory_cost(self, lead_time_days, unit_cost,
annual_volume, carrying_rate=0.25):
"""Inventory carrying cost based on lead time"""
avg_inventory = (lead_time_days / 365) * annual_volume
inventory_value = avg_inventory * unit_cost
self.costs['inventory'] = inventory_value * carrying_rate
return self
def add_admin_cost(self, annual_admin_cost):
"""Administrative overhead (POs, invoicing, etc.)"""
self.costs['admin'] = annual_admin_cost
return self
def add_risk_cost(self, disruption_probability, disruption_cost):
"""Expected cost of supply disruptions"""
self.costs['risk'] = disruption_probability * disruption_cost
return self
def calculate_tco(self):
"""Calculate total TCO and cost breakdown"""
total_tco = sum(self.costs.values())
return {
'supplier': self.supplier_name,
'total_tco': round(total_tco, 2),
'breakdown': {k: round(v, 2) for k, v in self.costs.items()},
'breakdown_pct': {
k: round(v/total_tco*100, 1)
for k, v in self.costs.items()
}
}
# Example: Compare two suppliers
annual_volume = 100000 # units
# Supplier A: Lower price, longer lead time, higher defect rate
supplier_a = TCOCalculator('Supplier_A')
supplier_a.add_purchase_cost(unit_price=10.00, annual_volume=annual_volume)
supplier_a.add_logistics_cost(cost_per_unit=1.50, annual_volume=annual_volume)
supplier_a.add_quality_cost(defect_rate=0.02, cost_per_defect=50, annual_volume=annual_volume)
supplier_a.add_inventory_cost(lead_time_days=45, unit_cost=10.00, annual_volume=annual_volume)
supplier_a.add_admin_cost(annual_admin_cost=25000)
supplier_a.add_risk_cost(disruption_probability=0.10, disruption_cost=200000)
tco_a = supplier_a.calculate_tco()
# Supplier B: Higher price, shorter lead time, lower defect rate
supplier_b = TCOCalculator('Supplier_B')
supplier_b.add_purchase_cost(unit_price=10.50, annual_volume=annual_volume)
supplier_b.add_logistics_cost(cost_per_unit=1.00, annual_volume=annual_volume)
supplier_b.add_quality_cost(defect_rate=0.005, cost_per_defect=50, annual_volume=annual_volume)
supplier_b.add_inventory_cost(lead_time_days=21, unit_cost=10.50, annual_volume=annual_volume)
supplier_b.add_admin_cost(annual_admin_cost=20000)
supplier_b.add_risk_cost(disruption_probability=0.03, disruption_cost=200000)
tco_b = supplier_b.calculate_tco()
# Compare
print(f"\n{tco_a['supplier']} TCO: ${tco_a['total_tco']:,.0f}")
print(f"{tco_b['supplier']} TCO: ${tco_b['total_tco']:,.0f}")
print(f"\nDifference: ${abs(tco_a['total_tco'] - tco_b['total_tco']):,.0f}")
For Complex Decisions:
import numpy as np
from numpy.linalg import eig
def ahp_pairwise_matrix(comparisons):
"""
Create pairwise comparison matrix for AHP
comparisons: dict of tuples {(criterion_a, criterion_b): importance}
importance scale: 1=equal, 3=moderate, 5=strong, 7=very strong, 9=extreme
"""
# Extract unique criteria
criteria = set()
for (a, b) in comparisons.keys():
criteria.add(a)
criteria.add(b)
criteria = sorted(list(criteria))
n = len(criteria)
# Build matrix
matrix = np.ones((n, n))
for i, crit_i in enumerate(criteria):
for j, crit_j in enumerate(criteria):
if i != j:
if (crit_i, crit_j) in comparisons:
matrix[i, j] = comparisons[(crit_i, crit_j)]
elif (crit_j, crit_i) in comparisons:
matrix[i, j] = 1.0 / comparisons[(crit_j, crit_i)]
return matrix, criteria
def ahp_weights(matrix):
"""Calculate priority weights from pairwise comparison matrix"""
# Calculate eigenvector of maximum eigenvalue
eigenvalues, eigenvectors = eig(matrix)
max_eigenvalue_idx = np.argmax(eigenvalues.real)
principal_eigenvector = eigenvectors[:, max_eigenvalue_idx].real
# Normalize to get weights
weights = principal_eigenvector / principal_eigenvector.sum()
# Calculate consistency ratio
n = len(matrix)
max_eigenvalue = eigenvalues[max_eigenvalue_idx].real
ci = (max_eigenvalue - n) / (n - 1)
# Random index values
ri_values = {1: 0, 2: 0, 3: 0.58, 4: 0.90, 5: 1.12,
6: 1.24, 7: 1.32, 8: 1.41, 9: 1.45, 10: 1.49}
ri = ri_values.get(n, 1.49)
cr = ci / ri if ri > 0 else 0
return weights, cr
# Example: Compare evaluation criteria
comparisons = {
('Quality', 'Price'): 3, # Quality is moderately more important than Price
('Quality', 'Delivery'): 5, # Quality is strongly more important than Delivery
('Quality', 'Service'): 7, # Quality is very strongly more important than Service
('Price', 'Delivery'): 3, # Price is moderately more important than Delivery
('Price', 'Service'): 5, # Price is strongly more important than Service
('Delivery', 'Service'): 3, # Delivery is moderately more important than Service
}
matrix, criteria = ahp_pairwise_matrix(comparisons)
weights, consistency_ratio = ahp_weights(matrix)
print("AHP Criteria Weights:")
for criterion, weight in zip(criteria, weights):
print(f" {criterion}: {weight:.3f} ({weight*100:.1f}%)")
print(f"\nConsistency Ratio: {consistency_ratio:.3f}")
if consistency_ratio < 0.10:
print(" ✓ Acceptable consistency (CR < 0.10)")