【问题标题】:networkx.read_edgelist uses much more memory than the file it loadsnetworkx.read_edgelist 使用的内存比它加载的文件多得多
【发布时间】:2018-06-28 14:24:12
【问题描述】:

我有一个 3.7 GB 的边列表文件,它描述了 20k 个节点上的完整图,其中每条边都有一个浮点数 'weight'(全为 1.0)和一个整数 'length'(全为 0-1000)。

所以edgelist文件的头部是这样的:

0 1 1.0 76
0 2 1.0 85
0 3 1.0 118
0 4 1.0 94
0 5 1.0 71
...

我正在使用:

def load_graph(file_path: str) -> Graph:
    return read_edgelist(file_path, nodetype=int,
                         data=[('weight', float),
                               ('length', int)])

但是,当networkx.read_edgelist 运行时,我的计算机因内存使用量接近 100 GB 而停止运行。

什么给了?这是read_edgelist 特有的,还是networkx.Graph 只是 使用大量内存?无论哪种方式,任何人都可以推荐一个占用空间更小的替代图形库吗?

【问题讨论】:

  • 有什么理由需要它作为图表吗? D[(0,1)] = (1.0,76) 将是一种更有效的方式来存储边缘。由于它是一个完整的图,我不清楚将其实际存储为图而不是仅将其存储为每个边的条目的字典有多大优势。
  • 图表给我的好处是边是无向的。所以 (0,1) 与 (1,0) 相同。有没有办法使用元组(或类似的东西)作为字典键,但不关心它们的条目顺序?这将允许我放弃 networkx。
  • 好的,这个问题的答案stackoverflow.com/q/41259493/2883198 说使用frozensets 作为键,所以我会试试看。
  • 另一种方法是有一个 getter 函数,它总是按索引的顺序检查元组键(frozenset 有一些开销,对于这个数据大小,值得优化)。此外,对于处理来说,使用内存更紧凑的存储对我来说是有意义的——例如NumPy 数组。
  • @sophros 感谢您的评论,但我并没有完全理解——您能再明确一点吗? freezeset 的开销是多少,我该如何解决?一个numpy数组会比一个frozenset-keyed dict的namedtuples(这是我现在使用的)更有效吗?我应该清楚该图表“几乎”完整,但我不能依赖这个事实,例如获取事件边的列表。现在我使用理解 n -> set(e for e in self.edges if n in edge) 其中self.edges 是我提到的字典。我会问一个新的 SO 问题,但我不确定要问什么!

标签: python-3.x memory networkx


【解决方案1】:

鉴于讨论从networkx 的性能到存储“几乎”完整图形的最佳方式不同,我将集中总结其背后的基本原理,而不是使用元组而不是 frozenset 类型作为字典键.

我试图找到一个确认,但考虑到更多的方法frozenset 可能比元组占用更多的内存。从this question 我了解到散列算法已被重新实现,这有助于提高字典插入和查找的性能(在途中获取键的散列),但另一方面,Python 针对元组、列表和各种字符串进行了高度优化长度,这让我想知道 2 元组是否仍然不比 frozenset 快,如果仅仅是因为这个原因。

现在,当我们考虑 NumPy 数组时 - 它们可能更适合这项任务的原因是多方面的:

  1. 内存是连续的,这对cache locality 有很大帮助(在遍历整个内存时很重要) 数组)。
  2. NumPy 比普通列表更适合更大规模的数据(比如数万个值)。
  3. 单个值的存储效率更高(请参阅下面的说明)。

在您的情况下,您似乎需要存储 2 个值 - 一个 float,一个 int。您可以分配 2 个 2-dim ndarrays - int 之一和 float32 类型之一。您可以对角填充数组并创建一个特殊的访问器方法(这将检查两个索引的顺序 - 这可能会更慢)或填充两个索引(例如:1,2 和 2,1)。

我假设您并不总是需要这两个值,因此解耦 intfloat32 值实际上有利于使用各自值的算法的性能。 ndarrays 消耗的内存应该更小,并且索引的连续处理比在内存中随机跳转的字典要快得多。

【讨论】:

  • @Cai - 对你有帮助吗?如果是这样,你能接受答案吗?
  • 好的,我已经为此工作了很长时间。最后,就我的目的而言,它似乎是继承自 tuple 的边缘类效果最好。我认为使用 numpy 数组对于测试边缘中的节点成员资格不太有用(除非我做了一些非常聪明的事情),但我认为你是对的,tuples 是正确的,而不是frozensets。 frozensets 与元组相比使用了太多内存。
  • 如果它对其他人有帮助,我创建了class Edge(tuple): def __new__(self, seq=()): return tuple.__new__(tuple, sorted(seq)),所以Edge((0, 1)) == Edge((1, 0))True。我还在__new__ 中添加了assert len(seq) == 2,但这可能是不必要的。
猜你喜欢
  • 2015-04-19
  • 1970-01-01
  • 2023-03-30
  • 1970-01-01
  • 1970-01-01
  • 2021-01-25
  • 1970-01-01
  • 1970-01-01
  • 2018-01-02
相关资源
最近更新 更多