Image by AuthorRadar charts, also called spider or web charts, display multiple variables on axes that radiate from a central point. Each entity forms a polygon connecting its values across all variables, making it easy to compare patterns and identify strengths or weaknesses at a glance.
These charts are useful for performance evaluations, skill assessments, and multi-criteria comparisons. For example, you might compare programming languages across different attributes, evaluate business performance metrics, or analyze survey responses with multiple dimensions.
In this tutorial, we’ll build radar charts using two popular Python libraries: Plotly and Matplotlib. You’ll learn when to use each approach, how to handle data with different scales, and get hands-on experience with working code examples.
Prerequisites and Setup
We’ll use several Python libraries for this tutorial. Install them using pip:
pip install numpy pandas matplotlib plotly scikit-learn
Now let’s import the required packages:
import numpy as np import pandas as pd import matplotlib.pyplot as plt import plotly.graph_objects as go from sklearn.preprocessing import MinMaxScaler from math import pi
This imports the core libraries we need for data manipulation, visualization, and scaling.
Sample Dataset Preparation
We’ll compare four programming languages across six categories. Each language receives a score from 1-10 for different attributes:
categories = ['Learning Curve', 'Performance', 'Community Support',
'Job Market', 'Versatility', 'Documentation']
languages = {
'Python': [9, 7, 9, 9, 8, 9],
'JavaScript': [8, 6, 8, 10, 9, 7],
'Java': [6, 8, 8, 8, 7, 8],
'C++': [4, 10, 6, 7, 8, 7]
}
df = pd.DataFrame(languages, index=categories)
This creates our comparison dataset with consistent scoring across all categories.
Method 1: Creating Interactive Radar Charts with Plotly
Plotly excels at creating interactive visualizations with minimal code. Each programming language becomes a separate trace on the radar chart:
fig = go.Figure()
for language in languages.keys():
fig.add_trace(go.Scatterpolar(
r=languages[language],
theta=categories,
fill='toself',
name=language,
opacity=0.7
))
The fill=’toself’ parameter creates the filled polygon shape, while opacity=0.7 makes overlapping areas visible.
Now we customize the chart layout and display settings:
fig.update_layout(
polar=dict(
radialaxis=dict(
visible=True,
range=[0, 10]
)),
showlegend=True,
title={
'text': "Programming Languages Skill Comparison",
'x': 0.5,
'xanchor': 'center'
},
width=600,
height=600
)
fig.show()
This produces an interactive chart where users can hover over data points, toggle languages on and off, and zoom in on specific areas.

Plotly Advantages
Plotly offers several benefits for radar charts. The interactive features let users explore data dynamically, making it ideal for dashboards and presentations. The default styling looks professional with minimal customization, and the charts work well in web applications and Jupyter notebooks.
Plotly Limitations
Interactive features require JavaScript rendering, which may not work in all environments. The library adds dependencies to your project, and the charts aren’t suitable for static publications that need precise formatting control.
Method 2: Creating Static Radar Charts with Matplotlib
Matplotlib requires more setup but provides complete control over every visual element. We start by calculating the angles for each category:
fig, ax = plt.subplots(figsize=(8, 8), subplot_kw=dict(projection='polar')) angles = [n / float(len(categories)) * 2 * pi for n in range(len(categories))] angles += angles[:1]
The angles list contains the radial positions for each category. We add the first angle to the end to close the polygon.
Next, we plot each programming language as a separate line and filled area:
colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4']
for i, (language, values) in enumerate(languages.items()):
values_closed = values + values[:1]
ax.plot(angles, values_closed, 'o-', linewidth=2,
label=language, color=colors[i])
ax.fill(angles, values_closed, alpha=0.25, color=colors[i])
Each language gets its own color and styling. The values + values[:1] closes the polygon by duplicating the first value.
Finally, we customize the chart appearance:
ax.set_xticks(angles[:-1])
ax.set_xticklabels(categories)
ax.set_ylim(0, 10)
ax.set_yticks(range(0, 11, 2))
ax.set_title('Programming Languages Comparison', size=16, pad=20)
ax.legend(loc='upper right', bbox_to_anchor=(1.3, 1.0))
plt.tight_layout()
plt.show()
This creates a publication-ready static chart with full control over fonts, colors, and positioning.

Matplotlib Advantages
Matplotlib works everywhere Python runs and creates high-quality static images perfect for reports and publications. You control every aspect of the visualization, from line thickness to label positioning. The library is lightweight compared to Plotly.
Matplotlib Limitations
Creating radar charts requires more code and manual calculations. There’s no built-in interactivity, and achieving professional styling often needs additional customization work.
Handling Mixed-Scale Data
Oftentimes you will deal with data that comes in different units and scales. Revenue figures in millions will dominate satisfaction scores on a 1-5 scale, distorting the radar chart. Here’s how to solve this problem:
business_metrics = {
'Revenue': [2500000, 1800000, 3200000],
'Employee Count': [45, 55, 78],
'Customer Satisfaction': [4.2, 4.7, 4.5],
'Market Share': [0.14, 0.15, 0.10],
'Years in Business': [12, 8, 20]
}
companies = ['Company A', 'Company B', 'Company C']
raw_df = pd.DataFrame(business_metrics, index=companies)
The original data shows the scale problem clearly, with revenue values in millions while satisfaction scores range from 4-5. We’ll use MinMaxScaler to transform all variables to a 0-10 scale:
scaler = MinMaxScaler(feature_range=(0, 10))
scaled_data = scaler.fit_transform(raw_df)
scaled_df = pd.DataFrame(scaled_data,
index=companies,
columns=business_metrics.keys())
Now all metrics use the same scale, making fair comparisons possible. Let’s create a radar chart with the scaled data:
fig = go.Figure()
metrics = list(business_metrics.keys())
colors_scaled = ['#FF6B6B', '#4ECDC4', '#45B7D1']
for i, company in enumerate(companies):
fig.add_trace(go.Scatterpolar(
r=scaled_df.loc[company].values,
theta=metrics,
fill='toself',
name=company,
line=dict(color=colors_scaled[i])
))
fig.update_layout(
polar=dict(radialaxis=dict(visible=True, range=[0, 10])),
title={
'text': "Business Performance Comparison (Scaled Data)",
'x': 0.5,
'xanchor': 'center'
},
showlegend=True,
legend=dict(
x=0.8,
y=0.5,
xanchor='left',
yanchor='middle'
)
)
fig.show()

The scaled chart reveals meaningful patterns that were hidden when revenue dominated the visualization. We can create a similar scaled version using Matplotlib:
fig, ax = plt.subplots(figsize=(7, 7), subplot_kw=dict(projection='polar'))
# Categories (metrics) and angles
metrics = list(business_metrics.keys())
angles = [n / float(len(metrics)) * 2 * pi for n in range(len(metrics))]
angles += angles[:1] # Complete the circle
# Plot each company
colors = ['#FF6B6B', '#4ECDC4', '#45B7D1']
for i, company in enumerate(companies):
values = scaled_df.loc[company].values.tolist()
values_closed = values + values[:1]
ax.plot(angles, values_closed, 'o-', linewidth=2, label=company, color=colors[i])
ax.fill(angles, values_closed, alpha=0.25, color=colors[i])
# Customize the chart
ax.set_xticks(angles[:-1])
ax.set_xticklabels(metrics)
ax.set_ylim(0, 10)
ax.set_yticks(range(0, 11, 2))
ax.set_title('Business Performance Comparison (Scaled)', size=16, y=1.1, x=0.5)
ax.legend(loc='upper right', bbox_to_anchor=(1.3, 1.0))
ax.grid(True)
plt.tight_layout()
plt.show()

Scaling Considerations
Scaling changes interpretation. A company dominating in raw revenue might show modest advantages on the radar chart, while another company might appear to perform better overall due to more balanced scores across all scaled dimensions. The choice of scaling method impacts your radar chart’s appearance and interpretation.
MinMaxScaler transforms data to a fixed range, commonly (0-1) for simplicity or (0-10) for intuitive scoring. However, it’s sensitive to outliers, which can compress most values into a narrow range.
For better outlier handling, QuantileTransformer offers an alternative that scales data based on percentile ranks, providing more balanced visual distribution when your data contains extreme values.
Choosing the Right Approach
Select Plotly when building interactive dashboards, teaching materials, or web applications where users benefit from exploring the data. The hover tooltips and toggle features enhance understanding.
Choose Matplotlib for academic papers, printed reports, or any context requiring static images with precise formatting. You’ll have complete control over the final appearance.
Apply scaling whenever your variables use different units or ranges. This is especially important for business metrics, survey data, or any analysis combining measurements from different domains.
Conclusion
Radar charts provide a useful way to visualize multi-dimensional comparisons in Python. Plotly offers interactive features perfect for exploration and presentation, while Matplotlib delivers precise control for publication-quality static charts. When working with mixed-scale data, choosing an appropriate scaling method ensures fair comparisons across all dimensions while preserving the analytical insights most relevant to your specific use case.
Both approaches have their place in data visualization. By understanding their strengths and limitations, you can choose the right tool for your specific needs and audience.
