雷达图 之 缺失值忽略
一、序言
在当今数据驱动的商业环境中,有效地展示和比较多维数据是数据分析和产品决策的关键。而雷达图(Radar Chart),也被称为蜘蛛图(Spider Chart)或极坐标图(Polar Chart),正是一种能够在二维平面上展示多维度数据的强大可视化工具。在实际应用中,雷达图被广泛用于产品性能比较、员工能力评估、游戏角色属性分析、市场竞争力分析等各种场景。
大家想必都清楚,关于绘制雷达图的 Python 代码,获取途径十分便捷。无论是在百度上搜索,还是向 AI(比如 DeepSeek)询问,瞬间就能得到相关代码,稍作数据修改便可套用。还有些朋友会选择将数据上传至可视化网站,无需编写代码,也能生成雷达图。如今,生成雷达图的方法众多。
在实际情况中,部分数据可能难以获取甚至缺失。而上述常见方法,大多默认将缺失值处理为 0。但在本文里,我采用的是截然不同的方式 —— 直接忽略缺失值,将现有数据点直接连接来绘制雷达图,并深入探讨这种情况下对缺失值的分析方法。希望本文内容能给大家带来启发与帮助。
二、缺失处理方法
在数据分析工作中,数据缺失的情况极为常见。面对这类问题,常规处理手段大致可分为以下三类:
① 最暴力的温柔 - 将缺失值默认设定为 0:这种方法简单直接,能够快速填充空缺数据,但容易忽视数据背后真实的特征差异,在某些场景下会严重干扰分析结果的准确性。
# 特征工程中的Zero Padding技巧
def zero_fill_missing(data_array):
"""零值填补函数"""
return np.nan_to_num(data_array, nan=0, copy=False)
# 数据
medical_records = np.array([72, np.nan, 68, np.nan])
print(zero_fill_missing(medical_records)) # 输出:[72. 0. 68. 0.]
② 数据重构艺术 - 运用拟合、统计分析等方法对缺失值进行填充:例如通过均值、中位数填充,或是采用更为复杂的回归分析、多重填补法等。这些方法旨在基于已有数据的分布特征,对缺失值进行合理估计。然而,它们对数据的整体结构和分布规律依赖程度较高,一旦数据存在异常或分布不均衡,填充结果可能出现偏差。
from sklearn.impute import KNNImputer
import numpy as np
# 构建金融产品评估矩阵
finance_data = np.array([
[8.5, 7.2, 6.7],
[np.nan, 6.8, 5.9],
[7.9, np.nan, 7.1],
[8.0, 7.5, np.nan]
])
# K最近邻插补算法
imputer = KNNImputer(n_neighbors=2)
filled_data = imputer.fit_transform(finance_data)
print("插补后矩阵:\n")
filled_data
③ 智慧留白哲学 - 直接忽视、跳过缺失值:此方法能保留数据的原始性,避免因不当填充引入额外误差。但在后续分析过程中,如果缺失值比例较大,可能会导致数据样本的代表性不足,进而影响分析结论的普适性 。
上述三种处理缺失值的方法各具特点,很难简单评判孰优孰劣,它们在实际应用中均存在一定的优势与局限性。具体选用哪种方法,需要结合数据的具体情况,经过严谨细致的分析后才能确定。在本文聚焦的雷达图分析场景下,由于雷达图的数据维度通常较为有限,前两种方法,即将缺失值默认设为 0 以及通过拟合等手段进行填充,在过往实践中颇为常见。基于此,接下来本文将着重阐述如何在绘制雷达图时,采用忽视缺失值的方式开展相关工作。
三、雷达跳过缺失
下面是对雷达必备需要的基本信息。要知道label维度的个数,根据不同的个数计算出维度位置的角度是多少,最后闭合成一个密闭的空间。
num_vars = len(labels) # 维度数统计
angles = np.linspace(0, 2 * np.pi, num_vars, endpoint=False).tolist() # 计算角度
angles += angles[:1] # 角度闭合
下面是处理雷达图跳过缺失值的核心代码内容:
# 处理产品 A 缺失值,只保留有值的部分
valid_indices_a = np.isfinite(product_a) # 判断数据是否有效;有效True、无效False
valid_angles_a = [angles[i] for i in range(num_vars) if valid_indices_a[i]] # 提取出有效数值的角度
valid_product_a = product_a[valid_indices_a] # 提取出有效的数值
valid_angles_a.append(valid_angles_a[0]) # 角度闭合
valid_product_a = np.concatenate((valid_product_a, [valid_product_a[0]])) # 数值闭合
① 首先,
valid_indices_a = np.isfinite(product_a)
这行代码使用np.isfinite
函数,判断product_a
里的每个数据是否为有限值。如果是,对应的valid_indices_a
位置就为True
;不是(像无穷大或NaN
这类无效值),对应位置就为False
。这个valid_indices_a
是个布尔数组,和product_a
长度一样,能清晰标记哪些数据有效。② 接着,
valid_angles_a = [angles[i] for i in range(num_vars) if valid_indices_a[i]]
利用列表推导式,遍历num_vars
范围的索引i
。只有valid_indices_a[i]
为True
,才把angles[i]
添加到valid_angles_a
列表里,这样就筛选出了有效数据对应的角度。③ 然后,
valid_product_a = product_a[valid_indices_a]
通过布尔索引,从product_a
里挑出所有有效数值,放进valid_product_a
数组。④ 最后,为了绘制闭合的雷达图,
valid_angles_a.append(valid_angles_a[0])
把valid_angles_a
列表的首个角度再添加到末尾,实现角度闭合;valid_product_a = np.concatenate((valid_product_a, [valid_product_a[0]]))
则用np.concatenate
函数,把valid_product_a
数组的首个数值添加到数组末尾,完成数值闭合。经过这些步骤,就处理好了产品 A 的缺失值,可用于绘制雷达图了 。
其实大致原理很简单啊哈哈哈,下面就附上一个案例分析实操:
def plot_radar_chart(product_a, product_b, labels, product_name):
num_vars = len(labels) # 维度数统计
angles = np.linspace(0, 2 * np.pi, num_vars, endpoint=False).tolist() # 计算角度
angles += angles[:1] # 角度闭合
# 假设产品 A 无缺失值情况
# product_a = np.concatenate((product_a, [product_a[0]])) # 数值闭合
# 处理产品 A 缺失值,只保留有值的部分
valid_indices_a = np.isfinite(product_a) # 判断数据是否有效;有效True、无效False
valid_angles_a = [angles[i] for i in range(num_vars) if valid_indices_a[i]] # 提取出有效数值的角度
valid_product_a = product_a[valid_indices_a] # 提取出有效的数值
valid_angles_a.append(valid_angles_a[0]) # 角度闭合
valid_product_a = np.concatenate((valid_product_a, [valid_product_a[0]])) # 数值闭合
# 处理产品 B 缺失值,只保留有值的部分 (原理同上)
valid_indices_b = np.isfinite(product_b)
valid_angles_b = [angles[i] for i in range(num_vars) if valid_indices_b[i]]
valid_product_b = product_b[valid_indices_b]
valid_angles_b.append(valid_angles_b[0])
valid_product_b = np.concatenate((valid_product_b, [valid_product_b[0]]))
# 创建雷达图画布 (极坐标图)
fig, ax = plt.subplots(figsize=(6, 6), subplot_kw=dict(polar=True))
# 绘制 A 产品的雷达图
ax.plot(valid_angles_a, valid_product_a,
color='#F25F40', linewidth=1.5, linestyle = '-', # 线条的颜色、宽度、类型
markeredgecolor = '#FC4747', markersize = 3, marker='o', # 标记点的颜色、宽度、类型
label= product_name[0] )
ax.fill(valid_angles_a, valid_product_a, color='#F25F40', alpha=0.15)
# 绘制 B 产品的雷达图
ax.plot(valid_angles_b, valid_product_b,
color='#46BBFE', linewidth=1.5, linestyle = '-',
markeredgecolor = '#E5F1FB', markersize = 3, marker='o',
label= product_name[1])
ax.fill(valid_angles_b, valid_product_b, color='b', alpha=0.25)
# 设置标签
ax.set_thetagrids(np.degrees(angles[:-1]), labels,fontsize=12)
# 设置标题
ax.set_title('Product Performance Comparison', size=12, y=1.05)
# 添加图例
ax.legend(loc='upper right', bbox_to_anchor=(1.3, 1.1))
plt.show()
# 数据
product_a = np.array([8, 7, 9, 6, 7])
product_b = np.array([6, np.nan, 7, np.nan, 6])
labels = ['Performance', 'Battery', 'Camera', 'Display', 'Storage']
product = ['A' ,'B']
# 绘制雷达图
plot_radar_chart(product_a, product_b, labels,product)
上图可以看到有些缺失值,在绘画时都是直接跳过了,这样的方法确保了
- 不影响整体图表结构
- 自动适应不完整数据
- 保持比较的公平性
四、结束语
不知道写啥啊哈哈哈!若有不足或者建议,欢迎指出!!不定时更新分享哈!