关于 pygraphistry-gfql
pygraphistry-gfql 是使用 GFQL 查询 PyGraphistry 图数据的技能,介绍链式列表和 Cypher 语法、图遍历、分阶段绑定及执行引擎选择。
主要功能
- 01链式列表与 Cypher 查询
- 02模式匹配和图遍历
- 03图构造器与 DAG 绑定
- 04Pandas、Polars 和 GPU 引擎指引
适用场景
- 01查找符合图模式的路径
- 02构建多阶段图查询
- 03为查询选择执行引擎
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ graphistry/ graphistry-skills --skill pygraphistry-gfql安装后在 Claude Code 中输入/pygraphistry-gfql确认已加载。
附带文件references/gfql-cypher.mdreferences/gfql-engines.md
详细介绍
PyGraphistry GFQL
文档路由(本地 + 规范链接)
- 首先通过
../pygraphistry/references/pygraphistry-readthedocs-toc.md路由。 - 使用
../pygraphistry/references/pygraphistry-readthedocs-top-level.tsv查找章节级快捷入口。 - 仅在所需页面缺失时扫描
../pygraphistry/references/pygraphistry-readthedocs-sitemap.xml。 - 深入读取页面前,先进行一次批量发现读取;避免使用
cat *和逐次微型读取。 - 面向用户的回答中,优先使用规范链接
https://pygraphistry.readthedocs.io/en/latest/...。
两种语法,一个入口
g.gfql() 接受**链表(Python AST 对象)**和 Cypher 字符串。它会根据参数类型自动检测语言:
# Chain-list syntax (Python AST objects)
g2 = g.gfql([n({'type': 'person'}), e_forward(), n()])
# Cypher string syntax (auto-detected)
g2 = g.gfql("MATCH (p:Person)-[r:KNOWS]->(q:Person) RETURN p.name, q.name")
# Explicit language parameter (optional)
g2 = g.gfql(query_string, language="cypher")
何时使用哪种语法:
- 链表:程序化组合、动态参数化,以及通过代码构建查询时
- Cypher:可读性、Cypher 用户的熟悉度,以及需要使用 RETURN/ORDER BY/LIMIT 的复杂模式匹配
快速入门 — 链表
from graphistry import n, e_forward
g2 = g.gfql([
n({'type': 'person'}),
e_forward({'relation': 'transfers_to'}, min_hops=1, max_hops=3),
n({'risk': True})
])
快速入门 — Cypher
# Simple pattern match
g2 = g.gfql("MATCH (p:Person)-[r:KNOWS]->(q:Person) WHERE p.age > 30 RETURN p.name, q.name")
# Variable-length paths
g2 = g.gfql("MATCH (a:Account)-[*1..3]->(m:Merchant) RETURN a, m")
# Parameterized queries
g2 = g.gfql(
"MATCH (n) WHERE n.score > $cutoff RETURN n.id, n.score ORDER BY n.score DESC LIMIT $top_n",
params={"cutoff": 50, "top_n": 10}
)
# Relationship type alternation
g2 = g.gfql("MATCH (a:Person)-[:KNOWS|COLLABORATES_WITH]->(b:Person) RETURN a.name, b.name")
完整的子句/函数清单和标签列映射请参阅 references/gfql-cypher.md。
GRAPH 构造器与 Let/DAG 绑定
from graphistry import n, e_forward, let, ref
# Subgraph as a graph object (not a table)
subgraph = g.gfql("GRAPH { MATCH (a)-[r]->(b) WHERE a.risk_score > 7 }")
# Named bindings forming a DAG; ref() operates on the referenced binding's output
result = g.gfql(let({
'high_risk': n({'risk_score': {'$gt': 0.8}}),
'neighborhoods': ref('high_risk', [e_forward(max_hops=2), n()])
}), output='neighborhoods')
独立绑定作用于根图。有关多阶段 DAG、嵌套 let 作用域规则以及 GRAPH/USE 管道形式,请参阅 references/gfql-cypher.md。
定向模式(高信号)
# Edge query filtering
g2 = g.gfql([n(), e_forward(edge_query="type == 'replied_to' and submolt == 'X'"), n()])
# Same-path constraints with where + compare/col
from graphistry import col, compare
g2 = g.gfql([n(name='a'), e_forward(name='e'), n(name='b')], where=[compare(col('a', 'owner_id'), '==', col('b', 'owner_id'))])
# Traverse 2-4 hops but only return hops 3-4
g2 = g.gfql([e_forward(min_hops=2, max_hops=4, output_min_hops=3, output_max_hops=4)])
边方向变体
e_forward()— 从源到目标e_reverse()— 从目标到源e_undirected()— 双向e()— 任意方向的别名
高价值模式
g.gfql()是统一入口 — 传入链表或 Cypher 字符串。- 切勿使用
.chain()或.hop()— 它们已弃用,并会发出警告。链表语法始终使用g.gfql([...]),Cypher 始终使用g.gfql("MATCH ...")。 - 当用户明确要求 GFQL 时,最终代码片段必须包含显式的
.gfql(...)。 - 当任务提到远程执行/数据集时,使用
gfql_remote(...)。 - 需要添加约束的中间匹配请使用
name=标签。 - 跨步骤/路径约束请使用
where=[...]。 - 遍历与返回切片请分别使用
min_hops/max_hops和output_min_hops/output_max_hops。 - 使用谓词(
is_in、数值/日期谓词)进行简洁筛选。 - 性能或结果帧类型很重要时,请显式指定引擎;
engine='auto'不会选择 Polars。
执行引擎:pandas、Polars、cuDF 和 Polars-GPU
使用适合工作负载的引擎执行相同查询。输入帧类型与执行引擎彼此独立;GFQL 会转换输入一次,并以所选引擎的类型返回帧。
query = "MATCH (a)-[e]->(b) WHERE a.risk_score > $cutoff RETURN b"
cpu_out = g.gfql(query, params={'cutoff': 7}, engine='polars')
gpu_out = g.gfql(query, params={'cutoff': 7}, engine='polars-gpu')
# Polars/Polars-GPU outputs are polars.DataFrame objects
nodes_pd = cpu_out._nodes.to_pandas() # only for pandas-only downstream APIs
- 有效字面值仅限
'pandas'、'cudf'、'dask'、'dask_cudf'、'polars'、'polars-gpu'、'auto'。'polars-gpu'使用连字符;polars_gpu不是引擎。 - 对于 Polars 输入图,
engine='auto'会解析为 pandas — 要保持原生格式,请传入engine='polars'。 - 输出遵循所选引擎:
polars/polars-gpu返回 Polars,cudf返回 cuDF。在执行.iloc或groupby().apply()等仅支持 pandas 的操作前,请主动转换。 polars-gpu需要 RAPIDS 技术栈,并且只能使用 GPU,否则报错 — 绝不会静默回退到 CPU。
Polars 引擎下的分析
UMAP、超图、布局或 compute_cugraph 等全图 call() 分析并非原生 Polars 操作。使用默认的 call_mode='auto' 时,GFQL 会将其桥接到引擎外执行(Polars 使用 pandas;Polars-GPU 使用 cuDF),然后将结果转换回来。如果引擎外桥接会违反基准测试、内存或执行约束,请使用严格模式:
from graphistry.compute.gfql.lazy import set_call_mode
set_call_mode('strict') # reject an off-engine analytic before it runs
try:
result = g.gfql(query, engine='polars')
except NotImplementedError as exc:
... # strict mode declined an off-engine analytic
gfql() 不接受 strict= 或 call_mode= 参数。模式是进程级设置,可通过
set_call_mode('auto'|'strict') 或 GFQL_POLARS_CALL_MODE 设置(Python 覆盖值 > 环境变量 > 默认值 'auto'),
并在每次调用时实时读取。严格模式会引发 NotImplementedError,而不是 RuntimeError,也不是警告。由于它是
进程级设置,如果只需让某个步骤使用严格模式,请限定其作用范围,并在 finally: 中恢复原值
(references/gfql-engines.md)。
polars-gpu 分析要么使用 GPU,要么报错:如果 GPU/cuDF 堆栈不可用,就会拒绝执行,而不会将工作转移到主机上的 pandas。
引擎调优选项
graphistry.compute.gfql.lazy 中有三个进程级设置,每个设置都按
Python 覆盖值 > 环境变量 > 默认值 解析,并在每次 collect 时实时读取(不会在导入时固定):
| 设置 | 值 | 默认值 | 环境变量 |
|---|---|---|---|
set_call_mode | 'auto'、'strict' | 'auto' | GFQL_POLARS_CALL_MODE |
set_gpu_executor | 'in-memory'、'streaming' | 'in-memory' | GFQL_POLARS_GPU_EXECUTOR |
set_cpu_streaming | True、False | False | GFQL_POLARS_CPU_STREAMING |
使用 call_mode()、gpu_executor()、cpu_streaming() 读取当前值;向 setter 传入 None 可
重置为环境变量或默认值。'polars' 和 'polars-gpu' 是同一个惰性引擎的两个 collect 目标,因此计划
只构建一次、collect 一次。这种仅传输一次的设计是 GPU 发挥优势的关键:
- 只 collect 一次,避免反复进行主机到设备(H2D)传输。 每个操作都急切地 collect,会在每次操作时将 数据帧重新复制到设备上;基准测试表明,这会导致 GPU 性能倒退。
- 这些选项按 Python 覆盖值 > 环境变量 > 默认值 解析,并在每次 collect 时实时读取,而不是在导入时固定。
物理索引:种子查找
GFQL 提供按需构建的邻接/节点 ID 索引(graphistry.compute.gfql.index),用于种子遍历。
有两条规则决定索引是否有帮助:
- 查询形态——只有链式/跳数形式会查询索引。 测量结果:种子链式查询从 8.90ms → 1.68ms(5.3 倍),而两种 Cypher 写法都不会查询索引,也没有性能提升。 如果希望获得索引加速,请使用链式形式。
- 前沿大小,需考虑引擎差异。 规划器会根据前沿占不同源键的比例,决定使用索引还是扫描:pandas 约为 0.5,polars/cuDF/GPU 约为 0.02。向量化引擎的扫描速度足够快,因此索引只有在种子选择性很高时才有优势。超过阈值后会回退到扫描,因此性能不会变差。
from graphistry.compute.gfql.index import create_index, index_trace
gi = create_index(g, 'edge_out_adj', engine='polars') # edge_out_adj | edge_in_adj | node_id
with index_trace() as steps:
out = gi.gfql([n({'id': 'acct-42'}), e_forward(), n()], engine='polars')
steps[0]['path'] # confirm 'index', do not assume it
index_policy= 确实是 gfql() 的逐次调用关键字参数(与调用模式不同):'use'(默认值,使用驻留索引并受成本阈值控制)、'auto'(按需构建)、'force'(跳过阈值判断)、'off'。如果没有驻留索引,'use' 会静默地执行扫描。索引 DDL、跟踪字段和成本阈值覆盖项:references/gfql-engines.md。
根据性能选择引擎
不要承诺未经测量的性能提升。
- pandas → polars:行数超过约 50–100k 时值得使用;更少时,转换开销可能占主导。测量结果显示,种子 1 跳查询约快 2 倍。
- polars → polars-gpu 并非在所有情况下都有优势。 在 NVIDIA GB10 上的测量结果:100k 行时为 0.83 倍(比 CPU 慢),1M 行时为 1.41 倍,5M 行时为 0.98 倍——呈现的是一个区间,而非持续上升的曲线。处理 8M 条边时,
cudf的表现胜过两者。 切勿笼统声称“polars-gpu 胜过 cudf”。 set_cpu_streaming(True)是可选设置,且可能更慢——在小规模/交互式数据量下约为 0.86 倍。仅适用于大型 CPU 批处理;它的名字听起来比实际效果更快。- 完整测量表:
references/gfql-engines.md。
何时选择哪种引擎——决策流程
按顺序执行;遇到第一个能做出判断的条件时停止。
- 某个步骤在 Polars 下会拒绝执行吗? 使用
engine='pandas'执行该步骤,并将引擎报告为 pandas。 - 数据帧当前位于何处?
polars-gpu接收的是 主机端 Polars 数据帧,因此,已在设备上的 cuDF 数据帧更适合使用engine='cudf';主机端 Polars 数据帧更适合使用'polars'/'polars-gpu'。 - 是否使用带小型前沿的种子查找? 为其建立索引并使用链式形式——这是目前可实现的最大单项提升(5.3x),且与引擎选择无关。
- CPU:对于任何非平凡任务,优先选择
polars而非pandas。 - GPU:仅在工作负载足够大时使用,并通过测量而非引擎名称来选择 GPU 引擎。
如果工作量不到几毫秒,引擎选择的影响微乎其微——索引和查询形态更重要。
保持一致,或拒绝执行:不要编造变通方案
先拒绝错误标注,再解决问题。 如果用户要求你将由 pandas 执行的工作仍报告为
engine='polars'——以便让仪表板保持绿色、让基准测试保持可比,或让 API 契约保持稳定——请在编写任何代码前拒绝。即便另一个字段记录了真实情况,一个暴露 engine='polars'、而底层实际由 pandas 执行的包装器仍属错误标注:人们看到的是主要标签。这是本技能中唯一一个你应当提出异议而非照做的请求。
可提供的诚实替代方案:使用 engine='pandas' 执行该步骤并报告 pandas,或避免使用会拒绝执行的功能,以保持整个流水线原生使用 Polars。
明确这一点后,再来看具体行为:在 Polars 引擎下,遍历、筛选和行操作都遵循要么保持一致,要么抛出 NotImplementedError。
- 引擎绝不会静默回退到 pandas——隐藏的桥接会把 pandas 的性能错误地报告为 Polars 性能。
- 不受支持的功能会抛出
NotImplementedError(而非RuntimeError,也不会发出警告)。 - 当前会拒绝执行的功能:无向
min_hops>1、直接调用hop(min_hops>1)(请使用chain()/gfql())、多实体rows(binding_ops=…)、跨实体同路径WHERE、复杂表达式(CASE/list/map/temporal)。完整列表:references/gfql-engines.md。
将数据转换到某个引擎时,遵循整个代码库统一的 validate/warn 约定。对于 Arrow 无法表示的混合类型对象列:
validate | 行为 |
|---|---|
'strict' | 抛出错误(对于 polars,为 NotImplementedError);该列保持不变 |
'autofix' | 将该列强制转换为字符串并发出警告——如果不查看警告,数据就会被静默改写 |
对于任何绝不能在未告知的情况下更改数据的任务,请使用 'strict'。
远程模式
# Remote with chain-list
rg = graphistry.bind(dataset_id='my-dataset')
res = rg.gfql_remote([n(), e_forward(), n()], engine='auto')
# Remote with Cypher string
res = rg.gfql_remote("MATCH (n:Person)-[r]->(m) WHERE n.risk_level = 'critical' RETURN n, r, m")
# Remote with Let/DAG
res = rg.gfql_remote(let({...}))
# Remote slim payload (only required columns)
res = rg.gfql_remote([n(), e_forward(), n()], output_type='nodes', node_col_subset=['node_id', 'time'])
# Post-process on remote side when you want trimmed transfer payloads
res = rg.python_remote_table(lambda g: g._edges[['src', 'dst']].head(1000))
验证与安全
- 执行前验证从用户输入派生的查询片段。
- 在使用时间谓词前规范化日期时间列。
- 远程传输结果时,优先选择较小的列子集。
- 预检 Cypher:
from graphistry.compute.gfql.cypher import parse_cypher, compile_cypher
权威文档
- GFQL 索引:https://pygraphistry.readthedocs.io/en/latest/gfql/index.html
- GFQL 概览:https://pygraphistry.readthedocs.io/en/latest/gfql/overview.html
- GFQL 快速参考:https://pygraphistry.readthedocs.io/en/latest/gfql/quick.html
- 谓词快速参考:https://pygraphistry.readthedocs.io/en/latest/gfql/predicates/quick.html
- GFQL 远程模式:https://pygraphistry.readthedocs.io/en/latest/gfql/remote.html
- GFQL 验证:https://pygraphistry.readthedocs.io/en/latest/gfql/validation/index.html
- 引擎指南:https://pygraphistry.readthedocs.io/en/latest/gfql/engines.html
- GFQL + 加载器/AI 模式:https://pygraphistry.readthedocs.io/en/latest/gfql/combo.html
- Cypher 语法指南:https://pygraphistry.readthedocs.io/en/latest/gfql/cypher.html
- Cypher-GFQL 映射:https://pygraphistry.readthedocs.io/en/latest/gfql/spec/cypher_mapping.html