目录
[向量化的 Radviz(vectorized Radviz,简称 VRV)](#向量化的 Radviz(vectorized Radviz,简称 VRV))
多维数据可视化技术
多维和高维数据普遍存在于我们的日常生活和科学研究中 . 比如 , 手机就包括品牌、型号、尺寸、重量、 生产日期、屏幕尺寸和电池容量等几十个属性; 又如 , 生物医学领域中的基因表达数据经常会产生成百上千个 属性. 鉴于人类对于数据的理解集中在 2D 或 3D 的低维空间 , 因此 , 采用信息可视化技术将多维数据绘制到低维 屏幕空间是实现人与多维数据交互分析的一种解决思路. 多维和高维数据的可视化一直以来都是一个热门的研究领域. 以主成分分析 (principle component analysis, 简称 PCA) 和多维尺度分析 (multi-dimensional scaling, 简称 MDS) 为代表的传统高维数据降维方法 [6] , 是通过数学方法将高维数据降维到 2D 或 3D 的低维空间中 , 并尽量保留高维空间中的原有特性和聚类关系 , 但这类方法 会损失数据在原始维度上的细节信息, 而且无法表现维度之间的关系 . 以散点图矩阵 (scatterplot matrix) [7] 和平行坐标(parallel coordinates) [8] 为代表的经典可视化方法虽然可以保留多维数据在每个维度上的信息 , 并突出维度间关系, 但是当维度数量增多时 , 有限的屏幕空间难以容纳大量维度 , 而且还会存在过度绘制问题 . 以 Radviz [1]和星型坐标(star coordinates) [9,10] 为代表的高维数据径向型投影方法 , 可以大量节约屏幕空间 , 但维度的排列顺序对数据投影结果影响很大, 同时还存在数据重叠问题 . 此外 , 还有一部分研究提出先基于用户经验或算法准则提取高维空间中的部分维度, 然后在维度子集中结合上述技术完成高维数据分析 , 这类方法的代表是特征选择 与子空间分析
Radviz可视化原理
Radviz可视化原理是将一系列多维空间的点通过非线性方法映射到二维空间的可视化技术,是基于圆形平行坐标系的设计思想而提出的多维可视化方法。圆形的m条半径表示m维空间,使用坐标系中的一点代表多为信息对象,其实现原理参照物理学中物体受力平衡定理。
每个弹簧作用在小圆上的力取决于该弹簧的弹簧拉伸和弹性系数,当小圆静止不动,则表明其受到所有弹簧的合力为0,由此可得到如下方程:
Radviz 数据投影原理如图 1 所示,数据集各维度作为维度锚点分布在圆环上,圆内的点代表数据记录,其位置由来自各维度锚点的弹簧拉力共同决定,每个弹簧拉力的大小正比于数据点在相应维度上的取值,这些数据点在所有弹簧拉力的共同作用下稳定在合力为 0 的位置.图 1 中 A 点和 B 点是一个四维数据集中两个数据点在Radviz 中的映射,4 个维度被均匀分布在圆环上,记录 A 在维度 1 和维度 2 上取值较大,因此受到来自这两个维度锚点的弹簧拉力较大,从而定位在靠近 DA1 和 DA2 的附近;同理,记录 B 在维度 3 和维度 4 上取值较大,所以其位置在这两个维度锚点附近.在 Radviz 的数据投影机制下,具有相似特征的数据点将被映射到圆内相近位置.
因此,圆内聚集的点簇可以被人们直观地观察到,从而形成可视化的聚类效果.
Radviz 方法本身也存在一些缺点:
(1) 由于 Radviz 映射为多对一映射,这导致圆内数据点会出现遮挡和重合的现象
(2) Radviz圆环上维度错点摆放的位置和顺序对数据投影结果影响很大,如图1所示,随机维度错点布局的可视化聚类效果可能并不理想.针对以上问题。
学者们对 Radviz 进行了多方面的改进.针对 Radviz 方法将多维数据映射到低维空间时会产生数据点互相遮挡和重合的问题,学者们采用的主要改进思路是将Radviz 扩展到三维空间,Atero 等人8设计了 Viz3D,该方法在Radviz 垂直方向上增加一个标轴 2.各数据点在: 轴的取值为其有属性的平均值.Viz3D 能够有效保证原始空间内很近的数据在投影空间内也很近,但原始空间中距离较远的记录在投影空间中距离可能变近,随后,Novakova 等人9设计了 RadvizS.与Viz3D 不同的是,轴坐标记录了原始空间中数据点到坐标原点的距离,从而在三维空间中保留原始空间中数据点间的距离信息.他们进一步采用镜面投影法让原来重叠的记录点得到有效分离.在一定程度上解决了 Radviz中数据点重合、遮挡的混乱现象.
python
def radviz(frame, class_column, ax=None, color=None, colormap=None, **kwds):
"""RadViz - a multivariate data visualization algorithm
Parameters:
-----------
frame: DataFrame
class_column: str
Column name containing class names
ax: Matplotlib axis object, optional
color: list or tuple, optional
Colors to use for the different classes
colormap : str or matplotlib colormap object, default None
Colormap to select colors from. If string, load colormap with that name
from matplotlib.
kwds: keywords
Options to pass to matplotlib scatter plotting method
Returns:
--------
ax: Matplotlib axis object
"""
import matplotlib.patches as patches
'''
数据归一化
'''
def normalize(series):
a = min(series)
b = max(series)
return (series - a) / (b - a)
n = len(frame)
'''
type(classes)=pandas.core.series.Series
classes表示共有几个类别
class_col表示每一个样本对应的类别
'''
classes = frame[class_column].drop_duplicates()
class_col = frame[class_column]
'''
(1)pandas.DataFrame.drop
表明删除数据,axis=0表示删除行,axis=1表示删除列,这里表明去掉最后一列表示类别的列
(2)pandas.DataFrame.apply
调用函数,但输入必须是DataFrame
(3)df为150x7的二维矩阵,即原数据
'''
df = frame.drop(class_column, axis=1).apply(normalize)
'''
设定横轴、纵轴的取值范围
'''
if ax is None:
ax = plt.gca(xlim=[-1, 1], ylim=[-1, 1])
'''
定义一个字典
'''
to_plot = {}
colors = _get_standard_colors(num_colors=len(classes), colormap=colormap,
color_type='random', color=color)
for kls in classes:
to_plot[kls] = [[], []]
'''
to_plot= {'Breakout': [[], []], 'FalseAlarm': [[], []]}
'''
m = len(frame.columns) - 1
'''
当 i=0,1,2,3,4,5,6 时,
t=2π*0/7、2π*1/7、2π*2/7、2π*3/7、2π*4/7、2π*5/7、2π*6/7
上述t值对应的余弦、正弦即s为
[[ 1. 0. ]
[ 0.6234898 0.78183148]
[-0.22252093 0.97492791]
[-0.90096887 0.43388374]
[-0.90096887 -0.43388374]
[-0.22252093 -0.97492791]
[ 0.6234898 -0.78183148]]
'''
s = np.array([(np.cos(t), np.sin(t))
for t in [2.0 * np.pi * (i / float(m))
for i in range(m)]])
for i in range(n):
row = df.iloc[i].values
'''
(1)np.expand_dims(row, axis=1)
扩展维数,当axis=0时,扩展列,即在行上增加数据;[1,2]变为[[1,2]]
当axis=1时,扩展行,即在列上增加数据;[1,2]变为[[1],[2]]
(2)np.repeat(row, 2, axis=1)
幅值数组元素,2表示每个元素的复制次数
当axis=0时,列不变,在行上复制元素;[[1],[2]]变为[[1],[1],[2],[2]]
当axis=1时,行不变,在列上复制元素;[[1],[2]]变为[[1 1],[2 2]]
'''
row_ = np.repeat(np.expand_dims(row, axis=1), 2, axis=1)
'''
s * row_相当于两个数组相对应的位置相乘
(s * row_).sum(axis=0)相当于对相应行上的数值求和,即求每一列的和
此处对应公式(2)的分子,得到的是一个1X2的列表或数组
row.sum()相当于求数组所有元素的和
此处对应公式(2)的分母
'''
y = (s * row_).sum(axis=0) / row.sum()
if i==0:
print(row.sum())
'''
iat用于访问元素,访问150次,即样本个数
查看当前样本i所对应的kls是A还是B,A和B表示类别
然后再把相应的y值加入到以A或B为键的值里面
'''
kls = class_col.iat[i]
'''
to_plot= {'Breakout': [[y[0]], [y[1]]], 'FalseAlarm': [[y[0]], [y[1]]]}
将y的第一个值加入到字典中键为kls的第一个数组中,如上所示
'''
'''
将y的第二个值加入到字典中键为kls的第二个数组中,如上所示
'''
to_plot[kls][0].append(y[0])
to_plot[kls][1].append(y[1])
'''
enumerate(sequence, [start=0])返回枚举对象
sequence :序列、start=0 :下表从0开始
'''
for i, kls in enumerate(classes):
'''
一共循环两次
第一次循环画出A的值,即'A': [[y[0]], [y[1]]]
第二次循环画出B的值,即'B': [[y[0]], [y[1]]]
此时[y[0]]对应于to_plot[kls][0],[y[1]]对应于to_plot[kls][1]
'''
ax.scatter(to_plot[kls][0], to_plot[kls][1], color=colors[i],
label=pprint_thing(kls), **kwds)
ax.legend()
'''
在坐标轴中画圆,圆心(0.0,0.0),半径1.0,圆域无色
'''
ax.add_patch(patches.Circle((0.0, 0.0), radius=1.0, facecolor='none'))
'''
将属性注释到二维坐标图中
'''
for xy, name in zip(s, df.columns):
'''
画属性位置,位置坐标是数组s的每一行的两个值
'''
ax.add_patch(patches.Circle(xy, radius=0.025, facecolor='gray'))
'''
注释属性名称
'''
if xy[0] < 0.0 and xy[1] < 0.0:
ax.text(xy[0] - 0.025, xy[1] - 0.025, name,
ha='right', va='top', size='small')
elif xy[0] < 0.0 and xy[1] >= 0.0:
ax.text(xy[0] - 0.025, xy[1] + 0.025, name,
ha='right', va='bottom', size='small')
elif xy[0] >= 0.0 and xy[1] < 0.0:
ax.text(xy[0] + 0.025, xy[1] - 0.025, name,
ha='left', va='top', size='small')
elif xy[0] >= 0.0 and xy[1] >= 0.0:
ax.text(xy[0] + 0.025, xy[1] + 0.025, name,
ha='left', va='bottom', size='small')
ax.axis('equal')
return ax
向量化的 Radviz(vectorized Radviz,简称 VRV)
它通过扩展维度个数 ( 可简称为升维 ) 来
获得更大、更灵活的维度锚点排序空间 , 并从中搜索更好的数据投影结果 .VRV 以类别型多维数据为研究对象 ,
首先枚举每个维度的可取值 , 然后按照枚举值的数量将一个维度划分为多个新维度 . 例如 , 某武器类型维度的取
值可枚举为 ( 刀、枪、剑、棒 ), 维度扩展后该维度变为刀、枪、剑、棒 4 个新维度 , 原数据在 4 个新维度上的取
值只能为 0 或 1, 因此原武器类型维度取值为 " 刀 " 的数据在升维后取值变成了向量 (1,0,0,0).