python-3.x - 使用 Graph-tool 读取 Pajek .net 文件
问题描述
我有一个Pajek 网络文件(带加权边缘的无向网络),此处提供了一个示例:
*Vertices 5
1 apple
2 cat
3 tree
4 nature
5 fire
*Edges
1 3 14
2 4 1
节点标签无需引用即可证明。边被指定为 node1、node2、边权重。
我需要在图形工具中将此文件读取为带有节点标签和边的“权重”属性的无向图。该功能还应保留隔离节点。
在 Python 中有没有一种有效的方法来做到这一点?到目前为止,我一直在使用 Networkx 读取 .net 文件,然后使用这样的转换函数。我正在寻找一种方法来加快这个过程。
解决方案
Pajek 文件的每个部分(顶点/边)似乎都可以解释为以空格分隔的 CSV 文件,这意味着您可以使用pandas.read_csv()
. 该函数比您在纯 python 答案中建议的逐行解析要快。
此外,一次初始化边缘列表和属性列表(作为 numpy 数组)比在 python 循环中单独设置每个元素更快。
我认为下面的实现应该有点接近最优,但我没有对它进行基准测试。
import re
from io import StringIO
import numpy as np
import pandas as pd
import graph_tool as gt
def pajek_to_gt(path, directed=False, remove_loops=False):
"""
Load a Pajek .NET file[1] as a graph_tool.Graph.
Supports files which specify their edges via node pairs.
Does not support files which specify their edges via the
'edgeslist' scheme (i.e. the neighbors-list scheme).
Note:
Vertices are renumbered to start with 0, per graph-tool
conventions (not Pajek conventions, which start with 1).
Author: Stuart Berg (github.com/stuarteberg)
License: MIT
[1]: https://gephi.org/users/supported-graph-formats/pajek-net-format/
"""
# Load into RAM
with open(path, 'r') as f:
full_text = f.read()
if '*edgeslist' in full_text:
raise RuntimeError("Neighbor list format not supported.")
# Erase comment lines
full_text = re.sub(r'^\s*%.*$', '', full_text, flags=re.MULTILINE)
# Erase blank lines (including those created by erasing comments)
full_text = re.sub(r'\n+', '\n', full_text)
# Ensure delimiter is a single space
full_text = re.sub(r'[ \t]+', ' ', full_text)
num_vertices = int(StringIO(full_text).readline().split()[-1])
# Split into vertex section and edges section
# (Vertex section might be empty)
vertex_text, edges_text = re.split(r'\*[^\n]+\n', full_text)[1:]
# Parse vertices (if present)
v_df = None
if vertex_text:
v_df = pd.read_csv(StringIO(vertex_text), delimiter=' ', engine='c', names=['id', 'label'], header=None)
assert (v_df['id'] == np.arange(1, 1+num_vertices)).all(), \
"File does not list all vertices, or lists them out of order."
# Parse edges
e_df = pd.read_csv(StringIO(edges_text), delimiter=' ', engine='c', header=None)
if len(e_df.columns) == 2:
e_df.columns = ['v1', 'v2']
elif len(e_df.columns) == 3:
e_df.columns = ['v1', 'v2', 'weight']
else:
raise RuntimeError("Can't understand edge list")
e_df[['v1', 'v2']] -= 1
# Free up some RAM
del full_text, vertex_text, edges_text
# Create graph
g = gt.Graph(directed=directed)
g.add_vertex(num_vertices)
g.add_edge_list(e_df[['v1', 'v2']].values)
# Add properties
if 'weight' in e_df.columns:
g.edge_properties["weight"] = g.new_edge_property("double", e_df['weight'].values)
if v_df is not None:
g.vertex_properties["label"] = g.new_vertex_property("string", v_df['label'].values)
if remove_loops:
gt.stats.remove_self_loops(g)
return g
这是它为您的示例文件返回的内容:
In [1]: from pajek_to_gt import pajek_to_gt
In [2]: g = pajek_to_gt('pajek-example.NET')
In [3]: g.get_vertices()
Out[3]: array([0, 1, 2, 3, 4])
In [4]: g.vertex_properties['label'].get_2d_array([0])
Out[4]: array([['apple', 'cat', 'tree', 'nature', 'fire']], dtype='<U6')
In [5]: g.get_edges()
Out[5]:
array([[0, 2],
[1, 3]])
In [6]: g.edge_properties['weight'].get_array()
Out[6]: PropertyArray([14., 1.])
注意:此函数执行一些预处理以将双空格转换为单空格,因为您上面的示例在条目之间使用了双空格。那是故意的吗?您链接到的 Pajek 文件规范使用单空格。
编辑:
在重新阅读您链接到的 Pajek 文件规范后,我注意到边缘部分有两种可能的格式。第二种格式在可变长度列表中列出每个节点的邻居:
*edgeslist
4941 386 395 451
1 3553 3586 3587 3637
2 3583
3 4930
4 88
5 13 120
显然,我上面的实现与该格式不兼容。如果文件中使用了该格式,我已经编辑了函数以引发异常。
推荐阅读
- c# - 管理直线频道 Web 小部件的会话
- python - Python Tkinter 滑块自定义
- php - 在类中使用 PDO - 在 null 上调用成员函数 prepare()
- python - 使用已使用 numpy save 保存的 numpy fromfile 恢复二进制数据
- sed - 如何使用 sed 将文件中某些行的间距加倍
- amazon-web-services - 如何找到只有 id 的 Amazon EC2 AMI?
- python - 用 Pandas 中行前后值的平均值替换给定列的异常值
- git - Git添加所有修改过的文件,但有一些例外
- windows - 为什么我会从 chrome 收到两种不同类型的通知?
- typo3 - 来自隐藏输入字段的 Powermail 7.3 接收器附件