10

我在 MongoDB 中有一组文档,每个文档在列表中都有一个或多个类别。使用 map reduce,我可以获得每个类别的唯一组合有多少文档的详细信息:

['cat1']               = 523
['cat2']               = 231
['cat3']               = 102
['cat4']               = 72
['cat1','cat2']        = 710
['cat1','cat3']        = 891
['cat1','cat3','cat4'] = 621 ...

其中总数是类别精确组合的文档数量。

我正在寻找一种合理的方式来呈现这些数据,我认为具有比例区域的维恩图是一个好主意。使用上面的例子,cat1 的面积为 523+710+891+621,cat1 和 cat3 的重叠面积为 891+621,cat1、cat3、cat4 的重叠面积为 621,以此类推。

有没有人对我如何实施这个有任何提示?我最好用 Python (+Numpy/MatPlotLib) 或 MatLab 来做。

4

6 回答 6

10

问题

我们需要表示多个相互关联的对象类别的计数,而维恩图将无法表示超过微不足道的类别数量及其重叠。

一个解法

将每个类别及其组合视为图中的一个节点。绘制图形,使节点的大小代表每个类别中的计数,并且边连接相关类别。这种方法的优点是:可以轻松容纳多个类别,这成为一种连接的气泡图。

结果

网络布局

编码

建议的解决方案使用NetworkX创建数据结构并使用matplotlib绘制它。如果数据以正确的格式呈现,这将扩展到具有多个连接的大量类别。

import networkx as nx
import matplotlib.pyplot as plt

def load_nodes():
    text = '''  Node    Size
                1        523
                2        231
                3        102
                4         72
                1+2      710
                1+3      891
                1+3+4    621'''
    # load nodes into list, discard header
    # this may be replaced by some appropriate output 
    # from your program
    data = text.split('\n')[1:]
    data = [ d.split() for d in data ]
    data = [ tuple([ d[0], 
                    dict( size=int(d[1]) ) 
                    ]) for d in data]
    return data

def load_edges():
    text = '''  From   To
                1+2    1
                1+2    2
                1+3    1
                1+3    3
                1+3+4    1
                1+3+4    3
                1+3+4    4'''
    # load edges into list, discard header
    # this may be replaced by some appropriate output 
    # from your program
    data = text.split('\n')[1:]
    data = [ tuple( d.split() ) for d in data ]
    return data

if __name__ == '__main__':
    scale_factor = 5
    G = nx.Graph()
    nodes = load_nodes()
    node_sizes = [ n[1]['size']*scale_factor
                  for n in nodes ]

    edges = load_edges()
    G.add_edges_from( edges )

    nx.draw_networkx(G, 
                     pos=nx.spring_layout(G),
                     node_size = node_sizes)
    plt.axis('off')
    plt.show()

其他解决方案

其他解决方案可能包括:气泡图Voronoi 图弦图蜂巢图等。链接的示例都没有使用 Python;它们仅用于说明目的。

于 2012-05-30T10:29:10.773 回答
6

我相信 ninjagecko 是正确的,这通常不能表示为交叉点图,除非您不介意该图是 n 维的。但是,如果每个类别都有一个显示其所有交叉点的图表,则它可以用 2D 表示 - 这本身可以是一个图表。所以这可能是一种更合适的方式来表示您的数据。我制作了一个堆积条形图来说明:

在此处输入图像描述

编码:

cats = ['cat1','cat2','cat3','cat4']
data = {('cat1',): 523, ('cat2',): 231, ('cat3',): 102, ('cat4',): 72, ('cat1','cat2'): 710,('cat1','cat3'): 891,('cat1','cat3','cat4') : 621}

import matplotlib.pyplot as plt
import numpy as np
from random import random

colors = dict([(k,(random(),random(),random())) for k in data.keys()])
print colors
for i, cat in enumerate(sorted(cats)):
    y = 0
    for key, val in data.items():
        if cat in key:
            plt.bar(i, val, bottom=y, color=colors[key])
            plt.text(i,y,' '.join(key))
            y += val
plt.xticks(np.arange(len(cats))+0.4, cats )
plt.show()
于 2012-05-29T21:49:32.123 回答
4

这通常是不可能的,除非粗略地说,交叉点图是平面图 并且您没有 4 路交叉点。边长也有限制(除非你愿意画无定形的斑点来表示面积);所以如果你坚持画圆,那就受限制了。

在非常简单的情况下,您可以创建一个例程来绘制一个 3 向维恩图,然后在三元组的“另一侧”“添加”另一个圆圈。在上述情况下,1,3,4是那个三元组,并且2是奇一出。

如果可能因为您的数据满足上述条件(由于某种原因,您的图形是平面且极其复杂),并且您使用无定形斑点,则可以绘制平面图,并慢慢将每条边“膨胀”成椭圆体. 你可以用一种放松的方式来做到这一点:如果它们的交叉点低于应有的值,它们就会膨胀,如果它们的交叉点高于应有的值,它们就会收缩。(他们实际上有两个维度来做到这一点:增肥和拉长;根据需要选择。拉长将推动图表的其余部分,因此您必须检查这不会使事情变得不可能,例如通过使用基于物理弹簧的布局。)最终你可能会集中在一个答案上,你必须检查它的准确性。

于 2012-05-29T18:18:52.627 回答
2

高登答案的变化怎么样?每个类别都是一个节点,节点之间的加权边表示重叠度。重叠越多,边缘越厚。

于 2012-06-08T01:38:59.260 回答
1

请参阅高阶图的一些示例

不过,我不知道您将如何缩放比例区域。

也许如果您绘制了适当顺序的图表并对其进行了细分。然后您可以为每个三角形分配所需的区域并进行某种压力扩散,允许顶点移动,并可能允许一些压力从每个三角形“泄漏”到属于同一组的邻居?

于 2012-05-29T18:20:44.940 回答
1

您可能想尝试https://github.com/icetime/pyinfor/blob/master/venn.py但我也在 MatPlotLib 上找到了它https://github.com/icetime/matplotlib/blob/master/lib/ matplotlib/venn.py但我认为它没有被正式接受。

于 2012-05-29T18:40:46.257 回答