我正在绘制分类数据的二维热图直方图,以帮助显示和解释大型数据集中的模式,而且我不知道一种有效地沿每个轴对值进行合理排序的方法,以使相关值彼此相邻出现。
使用PyPI downloads logs中的示例,只有4个项目,我可以绘制项目名称与文件名称的直方图:
(注意,我停在60个最常见的不同值,然后将其他所有内容都集中到一个(其他)列中)
在这个极端的示例中,每个文件名都只与一个项目相关联,因此如果对x值进行重新排序以便所有numpy文件彼此相邻,则数据将更容易理解,然后是熊猫文件等...
在我的情况下,我不能保证x轴或y轴将包含更强的类别组,因此我希望找到可以沿两个轴排序的解决方案。我还要处理最大60x60的直方图,进行这种排序的时间预算最好是<500ms。
我认为答案在于:
我不清楚如何使用分层群集对每个轴重新排序。
使用相关矩阵的类似示例可以有效地处理一维数据,并将其转换为二维似乎很棘手。
我目前比较差的一种方法是为表定义一个能量函数,该函数将每一列与下一列之间的每个值的绝对差之和:
def get_energy(values):
np.abs(values[:, :-1] - values[:, 1:]).sum()
然后,对于每一行,我计算出必须交换哪些列以最终对该行中的值进行排序(相对于该行中的最高值)。然后,对于每个交换,我都会计算交换前后的表能量。如果能量较低,则使用交换的值更新表,然后继续。
最后,我转置桌子,并重复以上操作。
此方法存在严重缺陷,因为天真的掉期方法不会转移整个列,并且每个轴之间的订单之间也没有协作,因此最终您只能将我描述为零散的情况2碰巧破坏了列1中的值:
使用来自同一数据集的不同数据,比较发行版本名称与发行版本的示例(请注意,我在此处进行了一些轴标准化,否则Ubuntu将主导一切!)
未排序:
使用上述方法:
我当前拥有的代码如下:(使用一个熊猫表,对numpy ndarrays进行操作):
class Reorderer:
def __init__(self, table):
self.table = table.values
self.rows = table.index.values
self.cols = table.columns.values
def get_frame(self):
return pd.DataFrame(data=self.table, index=self.rows, columns=self.cols)
def _energy(self, values):
return np.abs(values[:, :-1] - values[:, 1:]).sum()
def _get_proposals(self, row_num):
row = self.table[row_num]
swaps = row.argsort()[::-1]
basis = swaps[0]
for order, from_index in enumerate(swaps[1:], 1):
if row[from_index] == 0:
break
to_index = basis + order
if to_index >= len(self.cols):
to_index = basis - order
if from_index != to_index:
yield (from_index, to_index)
def reorder(self):
initial_swaps = self.table[0].argsort()[::-1]
self.table = self.table[:, initial_swaps]
self.cols = self.cols[initial_swaps]
for row_num in range(1, len(self.rows)):
current_energy = self._energy(self.table[:row_num+1])
for proposed_from, proposed_to in self._get_proposals(row_num):
new_order = np.arange(len(self.cols))
new_order[proposed_from] = proposed_to
new_order[proposed_to] = proposed_from
reordered = self.table[:, new_order]
new_energy = self._energy(reordered[:row_num+1])
if new_energy < current_energy:
self.table = reordered
self.cols = self.cols[new_order]
current_energy = new_energy
return self.get_frame()
必须有更好的方法。