【PYG】简单分析Planetoid()中存储Cora数据集边的数量

发布于:2024-07-03 ⋅ 阅读:(12) ⋅ 点赞:(0)

  • 分析一下代码生成的数据,edge_index=[2, 10556],10556/2=5278,实际上数据集节点间有5429条边,打印前十条边可以看到(1,2)和(2,1)实际上都存进来了
Data(x=[2708, 1433], edge_index=[2, 10556], y=[2708], train_mask=[2708], val_mask=[2708], test_mask=[2708])
Number of nodes: 2708
Number of edges (directed): 10556
Average node degree: 3.90
Is undirected: True
Edge index shape: torch.Size([2, 10556])
First 10 edges:
tensor([[   0,    0,    0,    1,    1,    1,    2,    2,    2,    2],
        [ 633, 1862, 2582,    2,  652,  654,    1,  332, 1454, 1666]])
  • 分析以下代码输出结果
from torch_geometric.datasets import Planetoid
from torch_geometric.transforms import NormalizeFeatures

# 加载并预处理Cora数据集
dataset = Planetoid(root='/tmp/Cora', name='Cora', transform=NormalizeFeatures())
data = dataset[0]

# 打印边的数量
print(f'Number of nodes: {data.num_nodes}')
print(f'Number of edges (directed): {data.num_edges}')

# 检查无向边数量
edge_index = data.edge_index.numpy()
edge_set = set()

print(f"edge_index shape {edge_index.shape}")

for i in range(edge_index.shape[1]):
    u, v = edge_index[:, i]
    print(f"u {u}, v {v}")
    if (v, u) not in edge_set:  # 如果反向边不存在,则添加当前边
        edge_set.add((u, v))
        print(f"add edge_set: {edge_set}")
    else:
        print(f"not add v {v}, u {u}")
    if (i == 9):
        break
print(f'Number of unique edges (undirected): {len(edge_set)}')
  • 统计独一无二的边的数目,(1,2)在就不添加(2,1),这样计算边数有5278,不存在self-loops的情况
Number of nodes: 2708
Number of edges (directed): 10556
edge_index shape (2, 10556)
u 0, v 633
add edge_set: {(0, 633)}
u 0, v 1862
add edge_set: {(0, 633), (0, 1862)}
u 0, v 2582
add edge_set: {(0, 633), (0, 2582), (0, 1862)}
u 1, v 2
add edge_set: {(1, 2), (0, 633), (0, 2582), (0, 1862)}
u 1, v 652
add edge_set: {(1, 2), (0, 2582), (0, 633), (0, 1862), (1, 652)}
u 1, v 654
add edge_set: {(1, 2), (0, 2582), (1, 654), (0, 633), (0, 1862), (1, 652)}
u 2, v 1
not add v 1, u 2
u 2, v 332
add edge_set: {(1, 2), (2, 332), (0, 2582), (1, 654), (0, 633), (0, 1862), (1, 652)}
u 2, v 1454
add edge_set: {(2, 1454), (1, 2), (2, 332), (0, 2582), (1, 654), (0, 633), (0, 1862), (1, 652)}
u 2, v 1666
add edge_set: {(2, 1454), (1, 2), (2, 332), (0, 2582), (2, 1666), (1, 654), (0, 633), (0, 1862), (1, 652)}
  • 不用那么麻烦打印前十条数据可以用下面代码
for i in range(10):
    print(f"edge_index {i}: {edge_index[:,i]}")

这得分析cora.cites原始文件

  • 以下代码输出结果可以看出原始文件中就是存在重复的边,打印具体重复边的代码可以看文章最后面
Total edges (directed): 5429
Unique edges (undirected): 5278
Self-loops: 0
Number of unique edges without self-loops: 5278

你提到的边数差异可能涉及到数据的具体细节和处理方式。为了更好地理解这些差异,我们可以通过以下几个步骤来详细检查和分析 Cora 数据集中的边数据。

步骤

  1. 读取和解析 cora.cites 文件:加载所有边并计算独特边的数量。
  2. 检查自环和重复边:确定是否存在自环和重复边,这些可能会影响总边数的计算。
  3. 计算双向边:确认每个无向边是否在数据集中都被存储为两条有向边。

示例代码

以下是一个示例代码,展示如何读取、解析和检查 cora.cites 文件中的边数据:

# 文件路径
cites_file = 'path_to_cora.cites'  # 替换为实际的文件路径

# 读取引用关系数据
with open(cites_file, 'r') as f:
    cites_lines = f.readlines()

# 初始化边集合和自环计数
edges = set()
self_loops = 0

# 解析边数据
for line in cites_lines:
    parts = line.strip().split()
    cited = parts[0]
    citing = parts[1]
    
    # 检查自环
    if cited == citing:
        self_loops += 1

    # 添加无向边(确保 (u, v) 和 (v, u) 都被计为一条边)
    edge = tuple(sorted((cited, citing)))
    edges.add(edge)

# 计算边数
num_edges = len(cites_lines)
unique_edges = len(edges)

# 打印结果
print(f'Total edges (directed): {num_edges}')
print(f'Unique edges (undirected): {unique_edges}')
print(f'Self-loops: {self_loops}')

# 验证无向边数量是否符合预期
print(f'Number of unique edges without self-loops: {unique_edges - self_loops}')

输出解释

  1. num_edges:原始数据集中有向边的总数。
  2. unique_edges:去重后的无向边总数。
  3. self_loops:自环数量。
  4. unique_edges - self_loops:去除自环后的无向边总数。

分析

  • 总边数 (num_edges):表示所有有向边的数量。由于每条无向边存储为两条有向边,所以这个数值应该是原始无向边数的两倍(减去自环的情况)。
  • 去重后无向边 (unique_edges):表示独特的无向边数量。
  • 自环 (self_loops):表示从一个节点指向自身的边数量。
  • 去除自环后的无向边数 (unique_edges - self_loops):应与无向边的实际数量接近。

通过这段代码,你可以更清楚地理解 Cora 数据集中边的数量差异。如果去重后无向边的数量与预期不符,那么可能需要进一步检查数据的具体情况,例如是否存在重复边或特定的边处理方式。

这段代码也可以帮助解释为什么 54295278 之间有差异,这可能与特定数据处理细节有关,例如去除了某些自环或重复边。


chagpt生成的第一个版本答案,打印数据集的部分可以看下

在 Cora 数据集中,边是无向的。然而,在许多图神经网络库(包括 PyTorch Geometric)中,边通常被存储为有向边的形式。这意味着每条无向边在边列表中会被存储为两条有向边(一个从节点 A 到节点 B,另一个从节点 B 到节点 A)。

因此,如果 Cora 数据集中有 5429 条无向边,当它们被存储为有向边时,总数会是 5429 * 2 = 10858 条边。这个解释可以解释为什么 num_edges 显示的数量是 10556 而不是 5429。

以下是一个示例,展示如何加载 Cora 数据集并确认无向边被存储为有向边:

from torch_geometric.datasets import Planetoid
from torch_geometric.transforms import NormalizeFeatures

# 加载并预处理Cora数据集
dataset = Planetoid(root='/tmp/Cora', name='Cora', transform=NormalizeFeatures())
data = dataset[0]

# 打印图结构和特征信息
print(data)
print(f'Number of nodes: {data.num_nodes}')
print(f'Number of edges (directed): {data.num_edges}')
print(f'Average node degree: {data.num_edges / data.num_nodes:.2f}')
print(f'Is undirected: {data.is_undirected()}')

输出解释

  • data.num_nodes: 节点的数量,应该是 2708。
  • data.num_edges: 边的数量,应该是 10556(因为每条无向边在存储时变成了两条有向边)。
  • data.is_undirected(): 检查图是否是无向的。这个方法会返回 True,表明原始图是无向的,尽管在内部存储时使用了有向边的表示。

进一步验证

你可以进一步验证存储的边确实是有向的,通过查看边的列表:

edge_index = data.edge_index
print(f'Edge index shape: {edge_index.shape}')
print(f'First 10 edges:\n{edge_index[:, :10]}')

这将打印出前 10 条边的索引,确认它们是有向边的形式。

总结

Cora 数据集原本有 5429 条无向边,在 PyTorch Geometric 中被存储为 10556 条有向边(每条无向边变成了两条有向边)。这就是为什么在加载数据集时,data.num_edges 显示的是 10556 而不是 5429。


chatgpt生成的第二个版本答案

你说得对,5429 * 2 确实应该等于 10858,而不是 10556。那么,为何 Cora 数据集中显示的边数是 10556 呢?

这主要是因为在实际数据集中,有一些自环(self-loops)或重复边没有存储为双向边。

检查 Cora 数据集的边

为了进一步验证这一点,我们可以实际检查边的数据并计算其无向边的数量。以下是一个示例代码,展示如何检查边的数据并验证其数量:

from torch_geometric.datasets import Planetoid
from torch_geometric.transforms import NormalizeFeatures

# 加载并预处理Cora数据集
dataset = Planetoid(root='/tmp/Cora', name='Cora', transform=NormalizeFeatures())
data = dataset[0]

# 打印边的数量
print(f'Number of nodes: {data.num_nodes}')
print(f'Number of edges (directed): {data.num_edges}')

# 检查无向边数量
edge_index = data.edge_index.numpy()
edge_set = set()

for i in range(edge_index.shape[1]):
    u, v = edge_index[:, i]
    if (v, u) not in edge_set:  # 如果反向边不存在,则添加当前边
        edge_set.add((u, v))

print(f'Number of unique edges (undirected): {len(edge_set)}')

输出解释

  • data.num_edges: 边的数量,应该是 10556。
  • len(edge_set): 计算无向边的唯一数量。

检查边的方向性

我们还可以检查每对边是否确实包含其反向边:

undirected_edges = 0
for i in range(edge_index.shape[1]):
    u, v = edge_index[:, i]
    if (v, u) in edge_set:  # 检查是否存在反向边
        undirected_edges += 1

print(f'Number of undirected edge pairs: {undirected_edges}')

解释

  1. 自环(Self-loops)
    自环指的是从一个节点指向自身的边,在无向图中,自环只计算一次。

  2. 重复边(Duplicate edges)
    如果图中存在重复边(多个相同的边),这些边在有向边表示时只会存储一次。

这些因素会导致实际存储的有向边数量比理论上双倍的无向边数量少。

进一步优化

让我们编写更完整的代码来检查这些情况:

import numpy as np
from torch_geometric.datasets import Planetoid
from torch_geometric.transforms import NormalizeFeatures

# 加载并预处理Cora数据集
dataset = Planetoid(root='/tmp/Cora', name='Cora', transform=NormalizeFeatures())
data = dataset[0]

# 打印边的数量
print(f'Number of nodes: {data.num_nodes}')
print(f'Number of edges (directed): {data.num_edges}')

# 检查无向边数量
edge_index = data.edge_index.numpy()
edge_set = set()
self_loops = 0

for i in range(edge_index.shape[1]):
    u, v = edge_index[:, i]
    if u == v:
        self_loops += 1
    if (v, u) not in edge_set:  # 如果反向边不存在,则添加当前边
        edge_set.add((u, v))

print(f'Number of unique edges (undirected): {len(edge_set)}')
print(f'Number of self-loops: {self_loops}')

# 检查是否有重复边
directed_edge_pairs = set()
for i in range(edge_index.shape[1]):
    u, v = edge_index[:, i]
    directed_edge_pairs.add((u, v))

print(f'Number of directed edge pairs: {len(directed_edge_pairs)}')

运行结果解释

  • Number of unique edges (undirected): 应该接近 5429。
  • Number of self-loops: 自环数量。
  • Number of directed edge pairs: 检查无向边的反向边是否存在。

通过这段代码,你可以更清楚地理解 Cora 数据集的边数为什么是 10556,而不是 5429 * 2 = 10858。这是因为在实际数据集中,自环和重复边的处理方式不同于简单的双倍计算。


chagpt生成的第三个版本答案,打印cora.cites中重复的边

  • 截取一部分代码运行结果,可以看出5429-5278=151
Total edges (directed): 5429
Unique edges (undirected): 5278
Self-loops: 0
Number of repeated edges: 151
Repeated edges:
Edge ('210871', '35') appears 2 times
Edge ('130', '39403') appears 2 times
Edge ('506', '89416') appears 2 times

为了检查 Cora 数据集中的重复边,我们可以编写代码来查找并列出所有重复的边。这可以帮助我们理解数据集中的边数量差异。

以下是详细的代码步骤:

  1. 读取并解析 cora.cites 文件
  2. 使用字典记录每条边的出现次数
  3. 查找并打印出现次数超过一次的边

示例代码

# 文件路径
cites_file = 'path_to_cora.cites'  # 替换为实际的文件路径

# 读取引用关系数据
with open(cites_file, 'r') as f:
    cites_lines = f.readlines()

# 初始化字典记录边的出现次数
edge_count = {}
self_loops = 0

# 解析边数据并统计出现次数
for line in cites_lines:
    parts = line.strip().split()
    cited = parts[0]
    citing = parts[1]

    # 检查自环
    if cited == citing:
        self_loops += 1

    # 创建无向边的标准形式 (sorted tuple)
    edge = tuple(sorted((cited, citing)))

    if edge in edge_count:
        edge_count[edge] += 1
    else:
        edge_count[edge] = 1

# 查找并打印重复边
repeated_edges = {edge: count for edge, count in edge_count.items() if count > 1}

print(f'Total edges (directed): {len(cites_lines)}')
print(f'Unique edges (undirected): {len(edge_count)}')
print(f'Self-loops: {self_loops}')
print(f'Number of repeated edges: {len(repeated_edges)}')

# 打印重复边及其出现次数
print('Repeated edges:')
for edge, count in repeated_edges.items():
    print(f'Edge {edge} appears {count} times')

输出解释

  1. len(cites_lines):原始数据集中有向边的总数。
  2. len(edge_count):去重后的无向边总数。
  3. self_loops:自环数量。
  4. len(repeated_edges):重复边的数量。
  5. repeated_edges:打印每条重复边及其出现次数。

分析

  • 原始有向边总数 (len(cites_lines)) 应该与数据集描述一致。
  • 去重后的无向边总数 (len(edge_count)) 应该接近 5429
  • 重复边 (repeated_edges):查找所有出现次数超过一次的边,帮助理解为什么无向边数目不是预期的 5429

通过这段代码,你可以更清楚地看到 Cora 数据集中是否存在重复边以及这些重复边的具体情况。如果去重后的无向边数量和预期不符,重复边可能是一个关键因素。