Skip to content

Commit 8d1d1b5

Browse files
add sample operations section, reference bench/spatial_bench in benchmarks
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent c09bc02 commit 8d1d1b5

1 file changed

Lines changed: 59 additions & 3 deletions

File tree

README.md

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,17 +56,73 @@ The only spatial engine with a Polars-native API, cost-model-driven index select
5656

5757
---
5858

59-
## API Reference Docs
59+
## Example Operations
6060

61-
The full API reference, operation catalogue, and technical deep dive into the planner and cost model live at **[pranav-walimbe.github.io/PyCanopy](https://pranav-walimbe.github.io/PyCanopy)**.
61+
### Inspecting the query plan
62+
63+
```python
64+
lf = (
65+
sf.lazy()
66+
.range_query(min_x=-10.0, min_y=35.0, max_x=40.0, max_y=70.0)
67+
.filter(pl.col("population") > 100_000)
68+
)
69+
print(lf.explain())
70+
# RANGE_QUERY [(-10, 35) → (40, 70)]
71+
# FROM
72+
# FILTER [(col("population")) > (dyn int: 100000)]
73+
# FROM
74+
# DF [N=100,000; path: EXPR]
75+
```
76+
77+
The optimizer moved the scalar filter below the range query. It runs first on all rows, then the spatial index is probed on the smaller survivor set.
78+
79+
### kNN join
80+
81+
```python
82+
query_df = pl.DataFrame({"qx": [2.35, 13.4], "qy": [48.85, 52.5]})
83+
84+
result = sf.lazy().knn_join(query_df, x_col="qx", y_col="qy", k=3).collect()
85+
```
86+
87+
For each row in `query_df`, returns the 3 nearest rows in the `SpatialFrame`. Large probes are streamed in morsels automatically.
88+
89+
### Proximity join with aggregation
90+
91+
```python
92+
import pycanopy as pc
93+
94+
stats = (
95+
sf.lazy()
96+
.within_distance_join(landmarks, x_col="lon", y_col="lat", distance=0.5)
97+
.group_by(["landmark"])
98+
.agg(count=pc.agg.count(), avg_fare=pc.agg.mean("fare"))
99+
)
100+
```
101+
102+
The full pair frame is never materialised. Each probe morsel folds into per-group partials and combines at the end.
103+
104+
### Polygon intersects self-join
105+
106+
```python
107+
from shapely.geometry import box
108+
109+
polygons = [box(i, 0, i + 1.5, 1.0) for i in range(10_000)]
110+
sf = SpatialFrame.from_polygons(pl.DataFrame({"id": range(10_000), "geom": polygons}), geometry_col="geom")
111+
112+
overlaps = sf.intersects_pairs(key_col="id")
113+
```
114+
115+
Returns all intersecting polygon pairs with overlap area and IoU. `key_col` replaces positional indices with values from that column.
116+
117+
For the full operation catalogue, index modes, streaming joins, and API reference see the **[docs site](https://pranav-walimbe.github.io/PyCanopy)**.
62118

63119
---
64120

65121
## Benchmarks
66122

67123
### Apache SpatialBench
68124

69-
Run on a single `m7i.2xlarge` (8 vCPU, 32 GB), the same hardware used by [Apache SpatialBench](https://github.com/apache/sedona-spatialbench). PyCanopy is measured live with `index_mode="auto"`.
125+
Run on a single `m7i.2xlarge` (8 vCPU, 32 GB), the same hardware used by [Apache SpatialBench](https://github.com/apache/sedona-spatialbench). PyCanopy is measured live with `index_mode="auto"`. Results were produced using the benchmark harness in `bench/spatial_bench`.
70126

71127
**SF1** (~6M trips). PyCanopy wins 7/12 testcases.
72128

0 commit comments

Comments
 (0)