Pytorch Geometric 将表格数据集(CSV 文件)转换为图形数据集

导 读

如今图数据集正在以惊人的速度出现,所有化学分子、社交网络和推荐系统主要以图数据结构的形式存储数据

有需要的朋友关注公众号【小Z的科研日常】,获取更多内容

01、如何转换CSV文件至图形数据结构

确定图形数据所需的基本信息

  • 节点(物品、人物、地点、汽车……)

  • 边缘(连接、交互、相似性……)

  • 节点特征(属性)

  • 标签(节点级、边级、图级)

以及可选:

  • 边权重(连接强度、交互次数……)

  • 边缘特征(描述边缘的附加(多维)属性)

检查是否有同质(相同类型)(节点,边)或异构(不同类型)(节点,边)

同质(节点、边)

对于用例,我们将使用FIFA 21 评级数据集(足球运动员的数据集)。

import pandas as pdas pd!wget -q https://raw.githubusercontent.com/batuhan-demirci/fifa21_dataset/master/data/tbl_player.csv
!wget -q https://raw.githubusercontent.com/batuhan-demirci/fifa21_dataset/master/data/tbl_player_skill.csv
!wget -q https://raw.githubusercontent.com/batuhan-demirci/fifa21_dataset/master/data/tbl_team.csv# 加载数据
player_df = pd.read_csv("tbl_player.csv")
skill_df = pd.read_csv("tbl_player_skill.csv")
team_df = pd.read_csv("tbl_team.csv")# 提取子集
player_df = player_df[["int_player_id", "str_player_name", "str_positions", "int_overall_rating", "int_team_id"]]
skill_df = skill_df[["int_player_id", "int_long_passing", "int_ball_control", "int_dribbling"]]
team_df = team_df[["int_team_id", "str_team_name", "int_overall"]]# 合并数据
player_df = player_df.merge(skill_df, on='int_player_id')
fifa_df = player_df.merge(team_df, on='int_team_id')# 数据帧排序
fifa_df = fifa_df.sort_values(by="int_overall_rating", ascending=False)
print("Players: ", fifa_df.shape[0])
fifa_df.head()

输出:

首先确定我们需要的特定于图的东西:

  • Nodes- 足球运动员(按 ID)

  • Edges- 如果他们为同一支球队或不同球队效力

  • Node Features- 足球运动员的位置、专长、控球……

  • Labels- 足球运动员的总体评分(节点级回归任务)

节点通常很容易识别——这里我们甚至有 ID。如果没有唯一标识符,则需要一个,因为我们需要知道哪些节点之间存在连接。

最具挑战性的任务通常是通过边以某种方式链接这些节点。在这里,我们根据团队分配定义边缘。

通过这个数据集,我们可以预测当一名球员转投新球队或观察到一名新球员时的预期评分。因此,我们期望通过团队分配产生关系效应。当然,还有许多其他的可能性来定义边缘,例如:

  • 两名玩家一起玩的次数(边缘权重)→ 协同作用

  • 一名玩家赢/输了多少次 1:1 决斗(边缘权重)

  • 在同一足球俱乐部开始职业生涯

  • 时间边缘:“过去两周一起玩过”

  • ……

关于如何组合数据框中的实例有很多选择。我们将继续采用最简单的方法,即根据他们的团队任务将他们联系起来。

max(fifa_df["int_player_id"].value_counts())

每个足球运动员 ID 在我们的数据集中仅出现一次。

如果单个节点 ID 在数据集中出现多次,则有不同的选项:

  • 有多个可以包含同一节点的图。在这种情况下,需要迭代数据框的每个子集(属于一个单独的图)并对该子集进行计算

  • 必须将多行聚合为一行。例如,如果有交易数据(如付款历史记录),则需要以某种方式将其汇总为一个特征向量,例如# payments、付款金额……

提取节点特征

节点特征通常以(num_nodes, node_feature_dim)形状的矩阵表示。

对于每个足球运动员,我们简单地提取他们的属性。因为每个玩家ID都是唯一的,所以我们可以根据原始数据框轻松做到这一点。

# 排序来定义节点的顺序来定义节点的顺序
sorted_df = fifa_df.sort_values(by="int_player_id")
#选择节点特征
node_features = sorted_df[["str_positions", "int_long_passing", "int_ball_control", "int_dribbling"]]
# 转换非数字列
pd.set_option('mode.chained_assignment', None)
positions = node_features["str_positions"].str.split(",", expand=True)
node_features["first_position"] = positions[0]
# One-hot 编码
node_features = pd.concat([node_features, pd.get_dummies(node_features["first_position"])], axis=1, join='inner')
node_features.drop(["str_positions", "first_position"], axis=1, inplace=True)
node_features.head() 

输出:

这已经是我们的节点特征矩阵。节点的数量和顺序由它们的形状隐式定义。每一行对应于最终图中的一个节点。

x = node_features.to_numpy()
x.shape # [num_nodes x num_features]

输出:

提取边缘

这可能是表格数据集最棘手的部分。我们需要考虑一种合理的方式来连接节点。如前所述,我们将在这里使用团队分配。

注意:有很多方法可以连接数据集中的实体,但这种方法非常简单。如果我想从这个数据集构建一个真实的模型,我可能会寻找一种更复杂的方式来连接玩家。对于我对边缘建模的方式来说,使用 GNN 有点大材小用。

我们现在需要找到分配到同一团队的成对球员。我们首先检查一下我们每队有多少名球员。

# 重新映射球员 ID
fifa_df["int_player_id"] = fifa_df.reset_index().index
# 这将告诉我们每队需要连接多少名球员
fifa_df["str_team_name"].value_counts()

我们现在需要在一支球队内构建这些球员的所有排列,这对应于每个球队子组内的完全连接图。我们使用 int_player_id 列作为边的索引。例如,如果边索引中有[0, 1],则意味着第一和第二节点(关于先前定义的节点特征矩阵)是连接的。

import itertools
import numpy as npteams = fifa_df["str_team_name"].unique()"str_team_name"].unique()
all_edges = np.array([], dtype=np.int32).reshape((0, 2))
for team in teams:team_df = fifa_df[fifa_df["str_team_name"] == team]players = team_df["int_player_id"].values# Build all combinations, as all players are connectedpermutations = list(itertools.combinations(players, 2))edges_source = [e[0] for e in permutations]edges_target = [e[1] for e in permutations]team_edges = np.column_stack([edges_source, edges_target])all_edges = np.vstack([all_edges, team_edges])
# Convert to Pytorch Geometric format
edge_index = all_edges.transpose()
edge_index # [2, num_edges]

array([[    0,     0,     0, ..., 18704, 18704, 18719],[[    0,     0,     0, ..., 18704, 18704, 18719],[    7,    32,    45, ..., 18719, 18751, 18751]])e = torch.tensor(edge_index, dtype=torch.long)
print(e)edge_index1 = e.t().clone().detach()
edge_index1
# output tensor([[    0,     7],[    0,    32],[    0,    45],...,[18704, 18719],[18704, 18751],[18719, 18751]])

建立图结构数据:

使用 networkx 来完成此任务:我们将 Pytorch 几何图转换为 NetworkX 图

from torch_geometric.data import Datadata import Data
data = Data(x=x, edge_index=edge_index1.t().contiguous(), y=y)from torch_geometric.data import Data
from torch_geometric.utils import to_networkx
networkX_graph = to_networkx(data)import networkx as nx
nx.draw(networkX_graph)

输出:

它看起来有点令人困惑,因为我们有很多节点和很多边。但是当我们尝试可视化该图的子图时,这对我们来说可能是有意义的。

异构(节点或边)

推荐系统是一个典型的例子,因此我选择了动漫推荐数据库(电影推荐数据集)。

加载数据集

import pandas as pd# Download data (quietly)
!wget -q https://raw.githubusercontent.com/Mayank-Bhatia/Anime-Recommender/master/data/anime.csv
!wget -q https://raw.githubusercontent.com/Mayank-Bhatia/Anime-Recommender/master/data/rating.csvanime = pd.read_csv("anime.csv")
rating = pd.read_csv("rating.csv")

输出:
 

就像之前一样,我们首先确定我们需要的图实体。

  • Nodes- 用户和动漫(具有不同特征的两种节点类型=异构)

  • Edges- 如果用户对电影进行了评分/评分(边缘权重)

  • Node Features- 电影属性和用户,我们没有明确的特征,所以我们必须稍后再弄清楚

  • Labels- 电影的评级(链接预测回归任务)

该数据集就像Example 1生成一个包含所有节点和边的图一样。给定一对节点和动漫电影,我们将能够预测用户是否/如何喜欢这部电影。

要将其建模为图表,我们必须支持两种节点类型:电影和用户。这是因为它们具有不同的节点特征(和形状),不适合一个联合节点特征矩阵。

提取节点特征

每部电影在动漫数据框中只出现一次,因此我们可以直接从中提取特征。如果数据框中的每个节点(电影 ID)有多个条目。

我们将提取具有特定属性的列并将它们转换为数字特征......

对于动漫电影...

  • 首先,我们需要重新映射 ID。这是因为它们不是以 0 开头,而且并非所有 ID 都存在。但这很重要,因为edge_index始终引用节点特征矩阵中的索引

  • 我们将存储此映射,稍后需要它

# 排序定义节点的顺序
sorted_df = anime.sort_values(by="anime_id").set_index("anime_id")# 映射 ID 从 0 开始
sorted_df = sorted_df.reset_index(drop=False)
movie_id_mapping = sorted_df["anime_id"]#挑选特征
node_features = sorted_df[["type", "genre", "episodes"]]
# Convert non-numeric columns
pd.set_option('mode.chained_assignment', None)# 为简单起见,我在这里只选择第一个流派,而忽略其他流派
genres = node_features["genre"].str.split(",", expand=True)
node_features["main_genre"] = genres[0]anime_node_features = pd.concat([node_features, pd.get_dummies(node_features["main_genre"])], axis=1, join='inner')
anime_node_features = pd.concat([anime_node_features, pd.get_dummies(anime_node_features["type"])], axis=1, join='inner')
anime_node_features.drop(["genre", "main_genre"], axis=1, inplace=True)
anime_node_features.head(10)

输出:

# 排序定义节点的顺序
x = anime_node_features.to_numpy()
x.shape # [num_movie_nodes x movie_node_feature_dim]

对于用户...

这里我们缺少一个描述每个用户属性的数据框。作为解决方法,我们有不同的选择:

  • 要么我们插入虚拟值(例如 0 到 1 之间的随机值,如 [0, 0.5, 0.1, 1]),然后通过消息传递进行更新

  • 或者我们计算一些有关用户的统计数据,例如平均评分、评分数量……(基于评分数据框)

  • 或者我们使用每个节点的典型特征作为特征(度、邻域、甚至 Node2Vec 嵌入)

我们将在这里选择第二个选项。

# 了解每个用户的平均评分和评分数
mean_rating = rating.groupby("user_id")["rating"].mean().rename("mean")
num_rating = rating.groupby("user_id")["rating"].count().rename("count")
user_node_features = pd.concat([mean_rating, num_rating], axis=1)# 重新映射用户 ID(从 0 开始)
user_node_features = user_node_features.reset_index(drop=False)
user_id_mapping = user_node_features["user_id"]# 只保留功能
user_node_features = user_node_features[["mean", "count"]]
user_node_features.head()

# Convert to numpy
x = user_node_features.to_numpy()
x.shape # [num_user_nodes x user_node_feature_dim]

这些已经是我们的节点特征矩阵。当然,我们也可以将值标准化为 (0,1) 范围内。

对于电影,我们有明确的属性来描述每个节点。对于用户,我们计算了一些基本属性,提供有关评级行为的信息。

节点的数量和顺序由它们的形状隐式定义。每一行对应于最终图中的一个节点。

提取标签

在这个例子中,我们有一个链接预测/回归问题,因此标签就是边缘。下图显示了评分的分布。稍后的任务将是预测用户和电影之间的评分。

与现在的标签不同的Example 1是,标签等于边的数量。

rating.head()# Outputuser_id anime_id rating
0 1 20 -1
1 1 24 -1
2 1 79 -1
3 1 226 -1
4 1 241 -1#-1 means the user watched but didn't assign a weight
rating["rating"].hist()

正如您所看到的(如下),我们并没有在评级表中包含所有电影(这很自然,因为我们通常没有所有项目的评级)。这意味着我们没有所有用户-项目对的标签,而只有一个子集。

为了在损失计算中考虑这一点,我们可以简单地存储可用索引的掩码。之前我也快速讨论过节点级预测情况下的掩模。这里完全相同——我们只想对有标签的实体进行预测。

由于我们这里有一个边缘预测问题,我们已经隐式地将这个掩码存储为edge_index。对于每条边,我们知道标签,因此我们只需根据我们知道的边计算损失。稍后在推理时,我们还可以预测其他节点对的边属性(标签)。

# Movies that are part of our rating matrix
rating["anime_id"].unique()# Output
array([   20,    24,    79, ..., 29481, 34412, 30738])# All movie IDs (e.g. no rating above for 1, 5, 6...)
anime["anime_id"].sort_values().unique()# Output
array([    1,     5,     6, ..., 34522, 34525, 34527])# We can also see that there are some movies in the rating matrix, for which we have no features (we will drop them here)
print(set(rating["anime_id"].unique()) - set(anime["anime_id"].unique()))
rating = rating[~rating["anime_id"].isin([30913, 30924, 20261])]# Output
{30913, 30924, 20261}

提取标签

labels = rating["rating"]
labels.tail()
y = labels.to_numpy()
y.shape

构建数据集

现在我们拥有为 Pytorch Geometric 或 DGL 等库构建图形所需的所有组件。我不会在这里安装这些库,因为这会使笔记本变得太大,但这里有一些最后步骤的代码片段。

对于异构图,我们需要将各个矩阵存储在HeteroData对象中,该对象可以保存多个节点/边矩阵。

from torch_geometric.data import HeteroDatadata import HeteroData
data = HeteroData()
data['user'].x = user_node_features
data['movie'].x = anime_node_features

如果节点之间有不同的边类型,也可以在此考虑。在上面的示例中,我们只有一种类型,因此 edge_index 是这样的(三连串):

data['user', 'rating', movie'].edge_index = edge_index

最后,我们可以像这样添加链接预测设置的标签。在异构图中,不同实体之间也可以有不同的标签,但这里我们只有一种类型。如果您专门构建推荐系统,那么您可能还会发现本关于二部图的教程很有帮助。

data['user', 'movie'].y = y

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/509371.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode 刷题 [C++] 第108题.将有序数组转换为二叉搜索树

题目描述 给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。 高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。 题目分析 由于二叉搜索树的中序遍历是升序的&…

性能问题分析排查思路之机器(3)

本文是性能问题分析排查思路的展开内容之一,第2篇,主要分为日志1期,机器4期、环境2期共7篇系列文章,本期是第三篇,讲机器(硬件)的网络方面的排查方法和最佳实践。 主要内容如图所示&#xff1a…

Python + Selenium —— 多窗口!

首先我们先理解下什么是窗口的概念&#xff1a;浏览器的 window 概念&#xff0c;一个tab就是一个window。 什么时候会产生多窗口&#xff1a; <a target"_blank" class"cate_menu_lk" href"//jiadian.jd.com">家用电器</a>target&…

巧【二叉搜索树的最近公共祖先】【二叉搜索树的性质】Leetcode 235. 二叉搜索树的最近公共祖先

【二叉搜索树的最近公共祖先】【二叉搜索树性质】Leetcode 235. 二叉搜索树的最近公共祖先 【巧】解法1 利用二叉搜索树有序的性质解法2 采用二叉树求最近公共祖先的方法——后序遍历 ---------------&#x1f388;&#x1f388;235. 二叉搜索树的最近公共祖先 题目链接&#x…

深度学习PyTorch 之 RNN-中文多分类【代码解析】

上篇文章给出了RNN-中文多分类的代码实现&#xff0c;本次主要是对RNN的架构进行一个详细的解析 1、主代码 class RNN(nn.Module):def __init__(self, vocab_size, embedding_dim, hidden_dim, output_dim, n_layers, bidirectional, dropout):super().__init__()# 初始化函数…

香杆箐骑行记,春回大地

2024年3月2日春回大地之际我们校长骑行群再次踏上征程前往香杆箐。这次骑行不仅是一次对身体的锻炼更是一次心灵的洗礼。 清晨的阳光洒满大地我们从郊野公园后门出发踏上了前往香杆箐的道路。沿途的风景如画绿树成荫鲜花盛开让人心旷神怡。我们沿着山路蜿蜒前行感受着大自然的韵…

UNIapp实现局域网内在线升级

首先是UNIapp 生成apk 用Hbuilder 进行打包 可以从网站https://www.yunedit.com/reg?gotocert 使用自有证书&#xff0c;目测比直接使用云证书要快一些。 发布apk 网站 用IIS发布即可 注意事项中记录如下内容 第一、需要在 iis 的MiMe 中添加apk 的格式&#xff0c;否则无法…

抖音视频批量采集软件|视频评论数据下载工具

特点一&#xff1a;关键词搜索&#xff0c;批量提取视频 工具不再受限于单个视频链接&#xff0c;支持通过关键词进行视频搜索&#xff0c;实现批量、有选择性的提取。只需简单的操作&#xff0c;输入想要搜索的视频关键词&#xff0c;点击开始抓取即可快速获取与关键词相关的视…

视频生成模型Sora的全面解析:从AI绘画、ViT到ViViT、DiT、VDT、NaViT、VideoPoet

视频生成模型Sora的全面解析&#xff1a;从AI绘画、ViT到ViViT、DiT、VDT、NaViT、VideoPoet 真没想到&#xff0c;举例视频生成上一轮的集中爆发才过去三个月&#xff0c;没想OpenAI一出手&#xff0c;该领域又直接变天了自打2.16日OpenAI发布sora以来&#xff0c;不但把同时…

揭秘8.4k星开发者的秘密武器:it-tools在线工具集,你不可不知!

在IT的世界里&#xff0c;为了更好地发挥自己的才能&#xff0c;必须善用优秀的工具。深入挖掘IT-Tools的神奇力量&#xff0c;让你的工作像魔法一般变得轻松高效&#xff01;无论是自动化、监控还是问题解决&#xff0c;这些工具是我们事业成功的关键利器。选择合适的IT工具&a…

力扣周赛387

第一题 代码 package Competition.The387Competitioin;public class Demo1 {public static void main(String[] args) {}public int[] resultArray(int[] nums) {int ans[]new int[nums.length];int arr1[]new int[nums.length];int arr2[]new int[nums.length];if(nums.leng…

数据库(易混知识点)

1.删除操作&#xff1a; DELETE&#xff1a;删除表中的行数据 DROP&#xff1a;删除视图 CLOSE&#xff1a;关闭游标 REVOKE&#xff1a;收回语句或对象许可 2.WHERE->GROUP BY->WHERE 筛选->分组->筛选 3.在视图上可以进行增删改查&#xff0c;也可以在视图…