Load data¶
An EdgeFrame needs two columns: the source and the destination of each edge. A NodeFrame needs one: the id. Everything else is up to you. This page shows each way to build a frame.
From Python values¶
The constructors take row dicts or a dict of columns. src= and dst= name the endpoint columns. They do not rename them.
import ursa as ur
edges = ur.EdgeFrame(
{"from": [0, 0, 1, 2], "to": [1, 2, 2, 0], "amount": [5.0, 1.0, 2.0, 4.0]},
src="from",
dst="to",
)
nodes = ur.NodeFrame(
[{"id": 0, "team": "red"}, {"id": 1, "team": "red"}, {"id": 2, "team": "blue"}],
id="id",
)
print(edges.src_col, edges.dst_col, nodes.id_col)
print(edges.collect())
from to id
shape: (4, 3)
┌───────┬───────┬────────┐
│ from ┆ to ┆ amount │
│ --- ┆ --- ┆ --- │
│ int64 ┆ int64 ┆ double │
╞═══════╪═══════╪════════╡
│ 0 ┆ 1 ┆ 5 │
│ 0 ┆ 2 ┆ 1 │
│ 1 ┆ 2 ┆ 2 │
│ 2 ┆ 0 ┆ 4 │
└───────┴───────┴────────┘
For a graph written by hand, from_edgelist takes tuples. A third element becomes the weight column.
shape: (3, 2)
┌────────┬────────┐
│ src ┆ dst │
│ --- ┆ --- │
│ string ┆ string │
╞════════╪════════╡
│ a ┆ b │
│ b ┆ c │
│ c ┆ a │
└────────┴────────┘
Node ids can be integers or strings. One frame uses one kind.
From a dataframe library¶
A frame built from a dataframe uses the same constructors. The data crosses as Arrow, with no copy.
Any object with an __arrow_c_stream__ method works the same way.
From a file¶
scan_edges and scan_nodes read Parquet and CSV. The read happens inside collect(), and a Parquet read gets only the columns the query needs.
edges = ur.scan_edges("edges.parquet", src="from_id", dst="to_id")
nodes = ur.scan_nodes("nodes.csv", id="node_id")
result = nodes.with_columns(pagerank=ur.pagerank(edges)).collect()
The format comes from the file extension. A CSV file needs a header row. A glob reads several files as one table: "edges/*.parquet".
read_edges and read_nodes are the eager forms. They return the rows at once.
Object storage¶
The same scans read from S3, Google Cloud Storage, and Azure. storage_options holds the backend settings. They layer over the backend's default credential chain, so a machine with credentials in its environment needs few of them.
edges = ur.scan_edges(
"s3://my-bucket/graph/edges/*.parquet",
src="from_id",
dst="to_id",
storage_options={"region": "us-east-1"},
)
A configured obstore store works too: pass it as store=.
HTTP¶
A plain https:// URL reads one hosted file. The URL must point at the file and must not have a query string.
From another graph library¶
Each function imports its library only when called.
Null endpoints¶
A row with a null src or dst is an error by default. Pass on_null="drop" to remove those rows. Ursa then emits a warning with the count.
edges = ur.EdgeFrame({"s": [1, 2, None], "d": [2, 3, 1]}, src="s", dst="d", on_null="drop")
print(edges.collect())
shape: (2, 2)
┌───────┬───────┐
│ s ┆ d │
│ --- ┆ --- │
│ int64 ┆ int64 │
╞═══════╪═══════╡
│ 1 ┆ 2 │
│ 2 ┆ 3 │
└───────┴───────┘
UserWarning: on_null='drop': dropped 1 edge row(s) with a null src or dst endpoint.
Bundled datasets¶
ur.datasets has four small graphs in the package and one larger graph that downloads on first use. See Datasets.