core-lens 0.1.dev197__tar.gz → 0.1.dev198__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/PKG-INFO +6 -33
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/benchmarks/bench_aoi.py +1 -1
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/benchmarks/bench_entity.py +5 -5
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/benchmarks/bench_spatial.py +1 -1
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/benchmarks/bench_view.py +4 -4
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/pyproject.toml +4 -3
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/src/core_lens/_version.py +2 -2
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/src/core_lens/aoi.py +17 -7
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/src/core_lens/base/entity.py +15 -8
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/src/core_lens/base/namespaces/plot.py +23 -10
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/src/core_lens/base/namespaces/stats.py +31 -15
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/src/core_lens/base/result.py +13 -2
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/src/core_lens/base/view.py +17 -9
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/src/core_lens/schema/detection.py +29 -2
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/src/core_lens/utils/paths.py +13 -6
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/src/core_lens/utils/polars_utils.py +46 -6
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/src/core_lens/utils/spatial.py +25 -10
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/tests/unit/conftest.py +12 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/tests/unit/test_entity.py +46 -6
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/tests/unit/test_polars_utils.py +44 -7
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/tests/unit/test_result.py +1 -1
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/tests/unit/test_schema_detection.py +26 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/tests/unit/test_season.py +1 -1
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/tests/unit/test_view.py +2 -2
- core_lens-0.1.dev198/uv.lock +4285 -0
- core_lens-0.1.dev197/uv.lock +0 -4418
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/.github/ISSUE_TEMPLATE/blank-proposal.yaml +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/.github/ISSUE_TEMPLATE/bug-report.yaml +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/.github/ISSUE_TEMPLATE/feature-request.yaml +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/.github/pull_request_template.md +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/.github/workflows/ci.yml +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/.github/workflows/gh-pages.yml +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/.github/workflows/pre-release.yml +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/.github/workflows/release.yml +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/.gitignore +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/.gitmessage +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/.pre-commit-config.yaml +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/.python-version +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/CONTRIBUTING.md +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/LICENSE +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/README.md +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/SKILLS.md +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/benchmarks/README.md +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/benchmarks/bench_export.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/benchmarks/bench_polars_utils.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/benchmarks/bench_result.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/benchmarks/bench_schema.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/benchmarks/bench_season.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/benchmarks/run_all.sh +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/docs/Makefile +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/docs/make.bat +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/docs/source/concepts.md +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/docs/source/conf.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/docs/source/export.md +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/docs/source/index.rst +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/docs/source/intro.md +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/docs/source/logging.md +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/docs/source/plots.md +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/docs/source/plugins.md +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/docs/source/queries.md +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/docs/source/quickstart.md +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/docs/source/stats.md +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/examples/demo_mws.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/examples/demo_tehsil.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/hooks/mypy.sh +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/hooks/no-parquet-outside-fixtures.sh +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/hooks/pytest.sh +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/src/core_lens/__init__.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/src/core_lens/__main__.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/src/core_lens/base/__init__.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/src/core_lens/base/namespaces/__init__.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/src/core_lens/entities/__init__.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/src/core_lens/entities/farms.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/src/core_lens/entities/mws.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/src/core_lens/entities/tehsil.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/src/core_lens/entities/waterbody.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/src/core_lens/export/__init__.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/src/core_lens/export/formats.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/src/core_lens/py.typed +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/src/core_lens/schema/__init__.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/src/core_lens/schema/profile.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/src/core_lens/utils/__init__.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/src/core_lens/utils/season.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/tests/fixtures/generate_fixtures.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/tests/unit/test_aoi.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/tests/unit/test_entities.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/tests/unit/test_export.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/tests/unit/test_main.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/tests/unit/test_plot.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/tests/unit/test_profile.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/tests/unit/test_schema_profile.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/tests/unit/test_season_config.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/tests/unit/test_spatial.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/tests/unit/test_stats.py +0 -0
- {core_lens-0.1.dev197 → core_lens-0.1.dev198}/usage.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: core-lens
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.dev198
|
|
4
4
|
Summary: Query, analyse, and visualise CoreStack's microwatershed and Earth science data through a clean, composable Python API.
|
|
5
5
|
Project-URL: Homepage, https://github.com/ApoorvaKashyap/core-lens
|
|
6
6
|
Project-URL: Issues, https://github.com/ApoorvaKashyap/core-lens/issues
|
|
@@ -21,7 +21,7 @@ Requires-Dist: core-lens[spatial]
|
|
|
21
21
|
Provides-Extra: core
|
|
22
22
|
Requires-Dist: awscrt>=0.23.0; extra == 'core'
|
|
23
23
|
Requires-Dist: loguru>=0.7.3; extra == 'core'
|
|
24
|
-
Requires-Dist: polars<2
|
|
24
|
+
Requires-Dist: polars<3,>=2.0.0; extra == 'core'
|
|
25
25
|
Requires-Dist: pyarrow<25,>=23.0.0; extra == 'core'
|
|
26
26
|
Requires-Dist: pydantic<3,>=2.13.3; extra == 'core'
|
|
27
27
|
Provides-Extra: full
|
|
@@ -33,7 +33,7 @@ Requires-Dist: geopandas<2,>=1.1.3; extra == 'full'
|
|
|
33
33
|
Requires-Dist: loguru>=0.7.3; extra == 'full'
|
|
34
34
|
Requires-Dist: lonboard<1,>=0.16.0; extra == 'full'
|
|
35
35
|
Requires-Dist: matplotlib<4,>=3.10.9; extra == 'full'
|
|
36
|
-
Requires-Dist: polars<2
|
|
36
|
+
Requires-Dist: polars<3,>=2.0.0; extra == 'full'
|
|
37
37
|
Requires-Dist: pyarrow<25,>=23.0.0; extra == 'full'
|
|
38
38
|
Requires-Dist: pydantic<3,>=2.13.3; extra == 'full'
|
|
39
39
|
Requires-Dist: pyogrio<1,>=0.10.0; extra == 'full'
|
|
@@ -41,39 +41,12 @@ Requires-Dist: pyproj<4,>=3.7.2; extra == 'full'
|
|
|
41
41
|
Requires-Dist: scipy<2,>=1.17.1; extra == 'full'
|
|
42
42
|
Requires-Dist: shapely<3,>=2.1.2; extra == 'full'
|
|
43
43
|
Requires-Dist: statsmodels<1,>=0.14.6; extra == 'full'
|
|
44
|
-
Provides-Extra: full-gpu
|
|
45
|
-
Requires-Dist: awscrt>=0.23.0; extra == 'full-gpu'
|
|
46
|
-
Requires-Dist: bokeh<4,>=3.6.0; extra == 'full-gpu'
|
|
47
|
-
Requires-Dist: cudf-cu13==26.6.*; extra == 'full-gpu'
|
|
48
|
-
Requires-Dist: cudf-polars-cu13==26.6.*; extra == 'full-gpu'
|
|
49
|
-
Requires-Dist: duckdb<2,>=1.5.3; extra == 'full-gpu'
|
|
50
|
-
Requires-Dist: geoarrow-rust-core>=0.3.0; extra == 'full-gpu'
|
|
51
|
-
Requires-Dist: geopandas<2,>=1.1.3; extra == 'full-gpu'
|
|
52
|
-
Requires-Dist: loguru>=0.7.3; extra == 'full-gpu'
|
|
53
|
-
Requires-Dist: lonboard<1,>=0.16.0; extra == 'full-gpu'
|
|
54
|
-
Requires-Dist: matplotlib<4,>=3.10.9; extra == 'full-gpu'
|
|
55
|
-
Requires-Dist: polars<2,>=1.39.0; extra == 'full-gpu'
|
|
56
|
-
Requires-Dist: pyarrow<25,>=23.0.0; extra == 'full-gpu'
|
|
57
|
-
Requires-Dist: pydantic<3,>=2.13.3; extra == 'full-gpu'
|
|
58
|
-
Requires-Dist: pyogrio<1,>=0.10.0; extra == 'full-gpu'
|
|
59
|
-
Requires-Dist: pyproj<4,>=3.7.2; extra == 'full-gpu'
|
|
60
|
-
Requires-Dist: scipy<2,>=1.17.1; extra == 'full-gpu'
|
|
61
|
-
Requires-Dist: shapely<3,>=2.1.2; extra == 'full-gpu'
|
|
62
|
-
Requires-Dist: statsmodels<1,>=0.14.6; extra == 'full-gpu'
|
|
63
|
-
Provides-Extra: gpu
|
|
64
|
-
Requires-Dist: awscrt>=0.23.0; extra == 'gpu'
|
|
65
|
-
Requires-Dist: cudf-cu13==26.6.*; extra == 'gpu'
|
|
66
|
-
Requires-Dist: cudf-polars-cu13==26.6.*; extra == 'gpu'
|
|
67
|
-
Requires-Dist: loguru>=0.7.3; extra == 'gpu'
|
|
68
|
-
Requires-Dist: polars<2,>=1.39.0; extra == 'gpu'
|
|
69
|
-
Requires-Dist: pyarrow<25,>=23.0.0; extra == 'gpu'
|
|
70
|
-
Requires-Dist: pydantic<3,>=2.13.3; extra == 'gpu'
|
|
71
44
|
Provides-Extra: spatial
|
|
72
45
|
Requires-Dist: awscrt>=0.23.0; extra == 'spatial'
|
|
73
46
|
Requires-Dist: duckdb<2,>=1.5.3; extra == 'spatial'
|
|
74
47
|
Requires-Dist: geopandas<2,>=1.1.3; extra == 'spatial'
|
|
75
48
|
Requires-Dist: loguru>=0.7.3; extra == 'spatial'
|
|
76
|
-
Requires-Dist: polars<2
|
|
49
|
+
Requires-Dist: polars<3,>=2.0.0; extra == 'spatial'
|
|
77
50
|
Requires-Dist: pyarrow<25,>=23.0.0; extra == 'spatial'
|
|
78
51
|
Requires-Dist: pydantic<3,>=2.13.3; extra == 'spatial'
|
|
79
52
|
Requires-Dist: pyogrio<1,>=0.10.0; extra == 'spatial'
|
|
@@ -82,7 +55,7 @@ Requires-Dist: shapely<3,>=2.1.2; extra == 'spatial'
|
|
|
82
55
|
Provides-Extra: stats
|
|
83
56
|
Requires-Dist: awscrt>=0.23.0; extra == 'stats'
|
|
84
57
|
Requires-Dist: loguru>=0.7.3; extra == 'stats'
|
|
85
|
-
Requires-Dist: polars<2
|
|
58
|
+
Requires-Dist: polars<3,>=2.0.0; extra == 'stats'
|
|
86
59
|
Requires-Dist: pyarrow<25,>=23.0.0; extra == 'stats'
|
|
87
60
|
Requires-Dist: pydantic<3,>=2.13.3; extra == 'stats'
|
|
88
61
|
Requires-Dist: scipy<2,>=1.17.1; extra == 'stats'
|
|
@@ -94,7 +67,7 @@ Requires-Dist: geoarrow-rust-core>=0.3.0; extra == 'viz'
|
|
|
94
67
|
Requires-Dist: loguru>=0.7.3; extra == 'viz'
|
|
95
68
|
Requires-Dist: lonboard<1,>=0.16.0; extra == 'viz'
|
|
96
69
|
Requires-Dist: matplotlib<4,>=3.10.9; extra == 'viz'
|
|
97
|
-
Requires-Dist: polars<2
|
|
70
|
+
Requires-Dist: polars<3,>=2.0.0; extra == 'viz'
|
|
98
71
|
Requires-Dist: pyarrow<25,>=23.0.0; extra == 'viz'
|
|
99
72
|
Requires-Dist: pydantic<3,>=2.13.3; extra == 'viz'
|
|
100
73
|
Description-Content-Type: text/markdown
|
|
@@ -116,7 +116,7 @@ view = aoi_bbox.mws
|
|
|
116
116
|
|
|
117
117
|
t1 = time.perf_counter()
|
|
118
118
|
print(f"First access : {(t1 - t0) * 1000:.2f} ms")
|
|
119
|
-
print(f"Keys shape : {view.keys.collect().shape}")
|
|
119
|
+
print(f"Keys shape : {view.keys.collect(engine='in-memory').shape}")
|
|
120
120
|
|
|
121
121
|
# Second access — should hit _scoped cache (near-zero cost).
|
|
122
122
|
t0 = time.perf_counter()
|
|
@@ -216,7 +216,7 @@ t0 = time.perf_counter()
|
|
|
216
216
|
view_attr = entity.where(state="Bihar")
|
|
217
217
|
t1 = time.perf_counter()
|
|
218
218
|
print(f"where(state) : {(t1 - t0) * 1000:.2f} ms")
|
|
219
|
-
print(f"Bihar keys : {view_attr.keys.collect().shape}")
|
|
219
|
+
print(f"Bihar keys : {view_attr.keys.collect(engine='in-memory').shape}")
|
|
220
220
|
|
|
221
221
|
|
|
222
222
|
# ── 9. BaseEntity.where() — composite attribute filter ───────────────────────
|
|
@@ -225,7 +225,7 @@ t0 = time.perf_counter()
|
|
|
225
225
|
view_composite = entity.where(state="Bihar", district="Banka", tehsil="Banka")
|
|
226
226
|
t1 = time.perf_counter()
|
|
227
227
|
print(f"where(3 cols) : {(t1 - t0) * 1000:.2f} ms")
|
|
228
|
-
print(f"Banka keys : {view_composite.keys.collect().shape}")
|
|
228
|
+
print(f"Banka keys : {view_composite.keys.collect(engine='in-memory').shape}")
|
|
229
229
|
|
|
230
230
|
|
|
231
231
|
# ── 10. BaseEntity.where() — list value (is_in) ──────────────────────────────
|
|
@@ -234,7 +234,7 @@ t0 = time.perf_counter()
|
|
|
234
234
|
view_list = entity.where(state=["Bihar", "Jharkhand"])
|
|
235
235
|
t1 = time.perf_counter()
|
|
236
236
|
print(f"where(list) : {(t1 - t0) * 1000:.2f} ms")
|
|
237
|
-
print(f"Bihar+Jharkhand keys : {view_list.keys.collect().shape}")
|
|
237
|
+
print(f"Bihar+Jharkhand keys : {view_list.keys.collect(engine='in-memory').shape}")
|
|
238
238
|
|
|
239
239
|
|
|
240
240
|
# ── 11. BaseEntity.where() — unknown kwarg error ─────────────────────────────
|
|
@@ -253,7 +253,7 @@ t0 = time.perf_counter()
|
|
|
253
253
|
view_sf = entity.spatial_filter(bbox=SMALL_BBOX)
|
|
254
254
|
t1 = time.perf_counter()
|
|
255
255
|
print(f"spatial_filter (bbox): {(t1 - t0) * 1000:.2f} ms")
|
|
256
|
-
print(f"Keys : {view_sf.keys.collect().shape}")
|
|
256
|
+
print(f"Keys : {view_sf.keys.collect(engine='in-memory').shape}")
|
|
257
257
|
|
|
258
258
|
# geometry mode
|
|
259
259
|
small_poly = sgeom.box(*SMALL_BBOX)
|
|
@@ -269,7 +269,7 @@ view_sf_area = entity.spatial_filter(
|
|
|
269
269
|
)
|
|
270
270
|
t1 = time.perf_counter()
|
|
271
271
|
print(
|
|
272
|
-
f"spatial_filter (area): {(t1 - t0) * 1000:.2f} ms keys={view_sf_area.keys.collect().shape}"
|
|
272
|
+
f"spatial_filter (area): {(t1 - t0) * 1000:.2f} ms keys={view_sf_area.keys.collect(engine='in-memory').shape}"
|
|
273
273
|
)
|
|
274
274
|
|
|
275
275
|
|
|
@@ -183,7 +183,7 @@ view = entity.spatial_filter(bbox=SMALL_BBOX)
|
|
|
183
183
|
|
|
184
184
|
t1 = time.perf_counter()
|
|
185
185
|
print(f"spatial_filter (bbox) : {(t1 - t0) * 1000:.2f} ms")
|
|
186
|
-
print(f"View keys : {view.keys.collect().shape}")
|
|
186
|
+
print(f"View keys : {view.keys.collect(engine='in-memory').shape}")
|
|
187
187
|
|
|
188
188
|
t0 = time.perf_counter()
|
|
189
189
|
view2 = entity.spatial_filter(geometry=small_poly)
|
|
@@ -54,7 +54,7 @@ t0 = time.perf_counter()
|
|
|
54
54
|
view_all = aoi.mws
|
|
55
55
|
t1 = time.perf_counter()
|
|
56
56
|
print(f"aoi.mws : {(t1 - t0) * 1000:.2f} ms")
|
|
57
|
-
print(f"Keys : {view_all.keys.collect().shape}")
|
|
57
|
+
print(f"Keys : {view_all.keys.collect(engine='in-memory').shape}")
|
|
58
58
|
|
|
59
59
|
|
|
60
60
|
# ── 2. View.where() — single filter ──────────────────────────────────────────
|
|
@@ -63,7 +63,7 @@ t0 = time.perf_counter()
|
|
|
63
63
|
view_bihar = view_all.where(state="Bihar")
|
|
64
64
|
t1 = time.perf_counter()
|
|
65
65
|
print(f"where(state) : {(t1 - t0) * 1000:.2f} ms")
|
|
66
|
-
print(f"Bihar keys : {view_bihar.keys.collect().shape}")
|
|
66
|
+
print(f"Bihar keys : {view_bihar.keys.collect(engine='in-memory').shape}")
|
|
67
67
|
|
|
68
68
|
|
|
69
69
|
# ── 3. View.where() — composite AND filter ────────────────────────────────────
|
|
@@ -72,7 +72,7 @@ t0 = time.perf_counter()
|
|
|
72
72
|
view_banka = view_all.where(state="Bihar", district="Banka", tehsil="Banka")
|
|
73
73
|
t1 = time.perf_counter()
|
|
74
74
|
print(f"where(3 cols) : {(t1 - t0) * 1000:.2f} ms")
|
|
75
|
-
print(f"Banka keys : {view_banka.keys.collect().shape}")
|
|
75
|
+
print(f"Banka keys : {view_banka.keys.collect(engine='in-memory').shape}")
|
|
76
76
|
|
|
77
77
|
|
|
78
78
|
# ── 4. View.spatial_filter() — further narrow ─────────────────────────────────
|
|
@@ -82,7 +82,7 @@ t0 = time.perf_counter()
|
|
|
82
82
|
view_spatial = view_all.spatial_filter(bbox=SMALL_BBOX)
|
|
83
83
|
t1 = time.perf_counter()
|
|
84
84
|
print(f"spatial_filter : {(t1 - t0) * 1000:.2f} ms")
|
|
85
|
-
print(f"Spatial keys : {view_spatial.keys.collect().shape}")
|
|
85
|
+
print(f"Spatial keys : {view_spatial.keys.collect(engine='in-memory').shape}")
|
|
86
86
|
|
|
87
87
|
|
|
88
88
|
# ── 5. View.between() — date range (no I/O) ───────────────────────────────────
|
|
@@ -47,7 +47,7 @@ venv = ".venv"
|
|
|
47
47
|
|
|
48
48
|
[project.optional-dependencies]
|
|
49
49
|
core = [
|
|
50
|
-
"polars>=
|
|
50
|
+
"polars>=2.0.0,<3",
|
|
51
51
|
"pyarrow>=23.0.0,<25",
|
|
52
52
|
"pydantic>=2.13.3,<3",
|
|
53
53
|
"loguru>=0.7.3",
|
|
@@ -69,9 +69,10 @@ viz = [
|
|
|
69
69
|
"bokeh>=3.6.0,<4",
|
|
70
70
|
]
|
|
71
71
|
stats = ["core-lens[core]", "scipy>=1.17.1,<2", "statsmodels>=0.14.6,<1"]
|
|
72
|
-
|
|
72
|
+
# GPU dependencies commented out until NVIDIA RAPIDS releases cudf-polars for Polars 2.0+
|
|
73
|
+
# gpu = ["core-lens[core]", "cudf-cu13==26.6.*", "cudf-polars-cu13==26.6.*"]
|
|
73
74
|
full = ["core-lens[spatial,viz,stats]"]
|
|
74
|
-
full-gpu = ["core-lens[spatial,viz,stats,gpu]"]
|
|
75
|
+
# full-gpu = ["core-lens[spatial,viz,stats,gpu]"]
|
|
75
76
|
|
|
76
77
|
[dependency-groups]
|
|
77
78
|
dev = [
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.1.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 1, '
|
|
21
|
+
__version__ = version = '0.1.dev198'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 'dev198')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -20,7 +20,11 @@ from loguru import logger
|
|
|
20
20
|
|
|
21
21
|
from core_lens.base.entity import BaseEntity, EntityValidationError
|
|
22
22
|
from core_lens.utils.paths import is_cloud_uri
|
|
23
|
-
from core_lens.utils.polars_utils import
|
|
23
|
+
from core_lens.utils.polars_utils import (
|
|
24
|
+
cached_read_schema,
|
|
25
|
+
cast_filter_values,
|
|
26
|
+
parquet_scan_path,
|
|
27
|
+
)
|
|
24
28
|
|
|
25
29
|
if TYPE_CHECKING:
|
|
26
30
|
from core_lens.base.view import View
|
|
@@ -232,22 +236,28 @@ def _cached_resolve_boundary(
|
|
|
232
236
|
schema_types = cached_read_schema(static_path, storage_options)
|
|
233
237
|
for col, val in entity_kwargs.items():
|
|
234
238
|
if col in schema.key_cols or col in schema.extra_static_cols:
|
|
235
|
-
|
|
239
|
+
col_dtype = schema_types.get(col)
|
|
240
|
+
is_list_col = isinstance(col_dtype, pl.List)
|
|
241
|
+
cast_val = cast_filter_values(val, col_dtype)
|
|
236
242
|
if isinstance(val, list):
|
|
237
243
|
if is_list_col:
|
|
238
244
|
filter_expr = (
|
|
239
245
|
filter_expr
|
|
240
|
-
& pl.col(col).list.eval(pl.element().is_in(
|
|
246
|
+
& pl.col(col).list.eval(pl.element().is_in(cast_val)).list.any()
|
|
241
247
|
)
|
|
242
248
|
else:
|
|
243
|
-
filter_expr = filter_expr & pl.col(col).is_in(
|
|
249
|
+
filter_expr = filter_expr & pl.col(col).is_in(cast_val)
|
|
244
250
|
else:
|
|
245
251
|
if is_list_col:
|
|
246
|
-
filter_expr = filter_expr & pl.col(col).list.contains(
|
|
252
|
+
filter_expr = filter_expr & pl.col(col).list.contains(cast_val)
|
|
247
253
|
else:
|
|
248
|
-
filter_expr = filter_expr & (pl.col(col) ==
|
|
254
|
+
filter_expr = filter_expr & (pl.col(col) == cast_val)
|
|
249
255
|
|
|
250
|
-
df =
|
|
256
|
+
df = (
|
|
257
|
+
lf.filter(filter_expr)
|
|
258
|
+
.select(candidate.key_cols + [geom_col])
|
|
259
|
+
.collect(engine="in-memory")
|
|
260
|
+
)
|
|
251
261
|
|
|
252
262
|
if df.is_empty():
|
|
253
263
|
raise ValueError(
|
|
@@ -29,7 +29,11 @@ from core_lens.utils.spatial import (
|
|
|
29
29
|
build_bbox_index,
|
|
30
30
|
exact_spatial_filter,
|
|
31
31
|
)
|
|
32
|
-
from core_lens.utils.polars_utils import
|
|
32
|
+
from core_lens.utils.polars_utils import (
|
|
33
|
+
cached_read_schema,
|
|
34
|
+
cast_filter_values,
|
|
35
|
+
parquet_scan_path,
|
|
36
|
+
)
|
|
33
37
|
|
|
34
38
|
if TYPE_CHECKING:
|
|
35
39
|
import shapely
|
|
@@ -487,23 +491,25 @@ class BaseEntity(ABC):
|
|
|
487
491
|
dtype = schema[col]
|
|
488
492
|
is_list_col = isinstance(dtype, pl.List) or dtype == pl.List
|
|
489
493
|
is_val_list = isinstance(val, (list, tuple, set))
|
|
494
|
+
cast_val = cast_filter_values(val, dtype)
|
|
490
495
|
|
|
491
496
|
if is_list_col:
|
|
492
497
|
if is_val_list:
|
|
493
498
|
filter_expr = filter_expr & (
|
|
494
|
-
pl.col(col).list.set_intersection(list(
|
|
499
|
+
pl.col(col).list.set_intersection(list(cast_val)).list.len()
|
|
500
|
+
> 0
|
|
495
501
|
)
|
|
496
502
|
else:
|
|
497
|
-
filter_expr = filter_expr & pl.col(col).list.contains(
|
|
503
|
+
filter_expr = filter_expr & pl.col(col).list.contains(cast_val)
|
|
498
504
|
else:
|
|
499
505
|
if is_val_list:
|
|
500
|
-
filter_expr = filter_expr & pl.col(col).is_in(list(
|
|
506
|
+
filter_expr = filter_expr & pl.col(col).is_in(list(cast_val))
|
|
501
507
|
else:
|
|
502
|
-
filter_expr = filter_expr & (pl.col(col) ==
|
|
508
|
+
filter_expr = filter_expr & (pl.col(col) == cast_val)
|
|
503
509
|
lf = lf.filter(filter_expr)
|
|
504
510
|
|
|
505
511
|
if not entity_kwargs:
|
|
506
|
-
keys = lf.select(self.key_cols).collect()
|
|
512
|
+
keys = lf.select(self.key_cols).collect(engine="in-memory")
|
|
507
513
|
else:
|
|
508
514
|
import shapely
|
|
509
515
|
import shapely.wkb as swkb
|
|
@@ -538,7 +544,7 @@ class BaseEntity(ABC):
|
|
|
538
544
|
matched = (
|
|
539
545
|
other_lf.filter(match_expr)
|
|
540
546
|
.select([other_profile.geometry_col])
|
|
541
|
-
.collect()
|
|
547
|
+
.collect(engine="in-memory")
|
|
542
548
|
)
|
|
543
549
|
|
|
544
550
|
if matched.is_empty():
|
|
@@ -572,8 +578,9 @@ class BaseEntity(ABC):
|
|
|
572
578
|
candidates.select(self.key_cols).lazy(),
|
|
573
579
|
on=self.key_cols,
|
|
574
580
|
how="semi",
|
|
581
|
+
maintain_order="left",
|
|
575
582
|
)
|
|
576
|
-
df = lf.collect()
|
|
583
|
+
df = lf.collect(engine="in-memory")
|
|
577
584
|
|
|
578
585
|
if df.is_empty():
|
|
579
586
|
keys = df.select(self.key_cols)
|
|
@@ -91,7 +91,7 @@ def _wkb_to_arrow_table(
|
|
|
91
91
|
)
|
|
92
92
|
|
|
93
93
|
# --- Fast path: pure Polygon / MultiPolygon rows (no Shapely) ---
|
|
94
|
-
poly_mask = geom_type_series.is_in([3, 6])
|
|
94
|
+
poly_mask = geom_type_series.is_in(pl.Series([3, 6], dtype=pl.UInt8).to_list())
|
|
95
95
|
df_poly = df.filter(poly_mask)
|
|
96
96
|
|
|
97
97
|
# --- Slow path: GeometryCollection rows — extract polygon parts via Shapely ---
|
|
@@ -545,7 +545,7 @@ class PlotNamespace:
|
|
|
545
545
|
def _make_fig(pdf: Any, title: str) -> "BokehFigure":
|
|
546
546
|
hover_cols = [c for c in pdf.columns if c not in ("geometry", "geom")]
|
|
547
547
|
tooltips = [(c, f"@{{{c}}}") for c in hover_cols]
|
|
548
|
-
fig = figure(
|
|
548
|
+
fig = figure(
|
|
549
549
|
width=900,
|
|
550
550
|
height=400,
|
|
551
551
|
x_axis_label=x,
|
|
@@ -658,7 +658,10 @@ class PlotNamespace:
|
|
|
658
658
|
if len(unique_entities) > top_n:
|
|
659
659
|
entities = unique_entities.limit(top_n).to_list()
|
|
660
660
|
entity_df = entity_df.join(
|
|
661
|
-
pl.DataFrame({key_col: entities}),
|
|
661
|
+
pl.DataFrame({key_col: entities}),
|
|
662
|
+
on=key_col,
|
|
663
|
+
how="inner",
|
|
664
|
+
maintain_order="left",
|
|
662
665
|
)
|
|
663
666
|
entity_pdf = entity_df.to_pandas()
|
|
664
667
|
|
|
@@ -716,12 +719,17 @@ class PlotNamespace:
|
|
|
716
719
|
unique_entities = df[key_col].unique()
|
|
717
720
|
if len(unique_entities) > top_n:
|
|
718
721
|
entities = unique_entities.limit(top_n).to_list()
|
|
719
|
-
df = df.join(
|
|
722
|
+
df = df.join(
|
|
723
|
+
pl.DataFrame({key_col: entities}),
|
|
724
|
+
on=key_col,
|
|
725
|
+
how="inner",
|
|
726
|
+
maintain_order="left",
|
|
727
|
+
)
|
|
720
728
|
|
|
721
729
|
def _make_scatter(y_col: str) -> "BokehFigure":
|
|
722
730
|
from bokeh.models import ColumnDataSource
|
|
723
731
|
|
|
724
|
-
fig = figure(
|
|
732
|
+
fig = figure(
|
|
725
733
|
width=800,
|
|
726
734
|
height=450,
|
|
727
735
|
x_axis_label=x,
|
|
@@ -815,12 +823,17 @@ class PlotNamespace:
|
|
|
815
823
|
unique_entities = df[key_col].unique()
|
|
816
824
|
if len(unique_entities) > top_n:
|
|
817
825
|
entities = unique_entities.limit(top_n).to_list()
|
|
818
|
-
df = df.join(
|
|
826
|
+
df = df.join(
|
|
827
|
+
pl.DataFrame({key_col: entities}),
|
|
828
|
+
on=key_col,
|
|
829
|
+
how="inner",
|
|
830
|
+
maintain_order="left",
|
|
831
|
+
)
|
|
819
832
|
|
|
820
833
|
df_pd = df.to_pandas()
|
|
821
834
|
|
|
822
835
|
def _make_hist(x_col: str) -> "BokehFigure":
|
|
823
|
-
fig = figure(
|
|
836
|
+
fig = figure(
|
|
824
837
|
width=800,
|
|
825
838
|
height=400,
|
|
826
839
|
x_axis_label=x_col,
|
|
@@ -902,7 +915,7 @@ class PlotNamespace:
|
|
|
902
915
|
|
|
903
916
|
from bokeh.models import FactorRange
|
|
904
917
|
|
|
905
|
-
fig = figure(
|
|
918
|
+
fig = figure(
|
|
906
919
|
width=600,
|
|
907
920
|
height=550,
|
|
908
921
|
x_range=FactorRange(factors=cols),
|
|
@@ -993,7 +1006,7 @@ class PlotNamespace:
|
|
|
993
1006
|
|
|
994
1007
|
mapper = LinearColorMapper(palette="Viridis256", low=low, high=high)
|
|
995
1008
|
|
|
996
|
-
fig = figure(
|
|
1009
|
+
fig = figure(
|
|
997
1010
|
width=900,
|
|
998
1011
|
height=500,
|
|
999
1012
|
x_range=FactorRange(factors=x_vals),
|
|
@@ -1046,7 +1059,7 @@ class PlotNamespace:
|
|
|
1046
1059
|
for row_col in num_cols:
|
|
1047
1060
|
row: list[Any] = []
|
|
1048
1061
|
for col_col in num_cols:
|
|
1049
|
-
fig = figure(
|
|
1062
|
+
fig = figure(
|
|
1050
1063
|
width=200,
|
|
1051
1064
|
height=200,
|
|
1052
1065
|
x_axis_label=col_col if row_col == num_cols[-1] else "",
|
|
@@ -293,7 +293,7 @@ class StatsNamespace:
|
|
|
293
293
|
columns=["dw_precipitation", "ci_cropping_intensity"],
|
|
294
294
|
method=CorrelateMethod.PEARSON,
|
|
295
295
|
)
|
|
296
|
-
print(result.data.collect())
|
|
296
|
+
print(result.data.collect(engine="in-memory"))
|
|
297
297
|
|
|
298
298
|
Per-MWS correlation, ranked by strength of dependence::
|
|
299
299
|
|
|
@@ -310,7 +310,7 @@ class StatsNamespace:
|
|
|
310
310
|
.sort("abs_corr", descending=True)
|
|
311
311
|
.select("mws_id", "correlation", "p_value", "n_obs")
|
|
312
312
|
.limit(5)
|
|
313
|
-
.collect()
|
|
313
|
+
.collect(engine="in-memory")
|
|
314
314
|
)
|
|
315
315
|
print(top5)
|
|
316
316
|
"""
|
|
@@ -378,18 +378,20 @@ class StatsNamespace:
|
|
|
378
378
|
)
|
|
379
379
|
|
|
380
380
|
# --- single collect boundary: scipy.t.sf needs eager numpy ---
|
|
381
|
-
result = lazy_result.collect()
|
|
381
|
+
result = lazy_result.collect(engine="in-memory")
|
|
382
382
|
t_vals = result["t_stat"].to_numpy()
|
|
383
383
|
df_vals = result["n_obs"].to_numpy() - 2
|
|
384
384
|
p_vals = 2.0 * sp.t.sf(np.abs(t_vals), df_vals)
|
|
385
|
-
result = result.with_columns(
|
|
385
|
+
result = result.with_columns(
|
|
386
|
+
pl.Series("p_value", np.atleast_1d(p_vals))
|
|
387
|
+
)
|
|
386
388
|
|
|
387
389
|
data = result.select([group_by, "correlation", "p_value", "n_obs"])
|
|
388
390
|
|
|
389
391
|
else:
|
|
390
392
|
# Kendall: scipy has no batched/lazy equivalent, so this branch
|
|
391
393
|
# is inherently eager. Collect ONCE here — not scattered filters.
|
|
392
|
-
df = lf.collect()
|
|
394
|
+
df = lf.collect(engine="in-memory")
|
|
393
395
|
rows: list[dict[str, Any]] = []
|
|
394
396
|
for sub_full in df.partition_by(group_by):
|
|
395
397
|
key = sub_full[group_by][0]
|
|
@@ -422,7 +424,7 @@ class StatsNamespace:
|
|
|
422
424
|
|
|
423
425
|
# Pooled mode — eager needed regardless (scipy.pearsonr/spearmanr/
|
|
424
426
|
# kendalltau all take raw numpy arrays). Collect ONCE here.
|
|
425
|
-
df = lf.collect()
|
|
427
|
+
df = lf.collect(engine="in-memory")
|
|
426
428
|
n_obs = len(df)
|
|
427
429
|
|
|
428
430
|
rows = []
|
|
@@ -674,7 +676,9 @@ class StatsNamespace:
|
|
|
674
676
|
.select(join_keys + [column])
|
|
675
677
|
.rename({column: "value_to"})
|
|
676
678
|
)
|
|
677
|
-
joined = from_df.join(
|
|
679
|
+
joined = from_df.join(
|
|
680
|
+
to_df, on=join_keys, how="inner", maintain_order="left"
|
|
681
|
+
).with_columns(
|
|
678
682
|
[
|
|
679
683
|
pl.lit(from_period).alias("year_from").cast(pl.Int32),
|
|
680
684
|
pl.lit(to_period).alias("year_to").cast(pl.Int32),
|
|
@@ -800,7 +804,7 @@ class StatsNamespace:
|
|
|
800
804
|
|
|
801
805
|
df = self._r.df()
|
|
802
806
|
if isinstance(df, pl.LazyFrame):
|
|
803
|
-
df = df.collect()
|
|
807
|
+
df = df.collect(engine="in-memory")
|
|
804
808
|
key = self._r.key_cols[0]
|
|
805
809
|
|
|
806
810
|
if mode == "cross_sectional":
|
|
@@ -945,20 +949,22 @@ class StatsNamespace:
|
|
|
945
949
|
n_dropped = counts.height - valid_keys.height
|
|
946
950
|
|
|
947
951
|
med_df = (
|
|
948
|
-
base_df.join(valid_keys, on=key)
|
|
952
|
+
base_df.join(valid_keys, on=key, maintain_order="left")
|
|
949
953
|
.group_by(key)
|
|
950
954
|
.agg(pl.col(column).median().alias("_med"))
|
|
951
955
|
)
|
|
952
956
|
mad_df = (
|
|
953
|
-
base_df.join(valid_keys, on=key)
|
|
954
|
-
.join(med_df, on=key)
|
|
957
|
+
base_df.join(valid_keys, on=key, maintain_order="left")
|
|
958
|
+
.join(med_df, on=key, maintain_order="left")
|
|
955
959
|
.with_columns(
|
|
956
960
|
(pl.col(column) - pl.col("_med")).abs().alias("_absdev")
|
|
957
961
|
)
|
|
958
962
|
.group_by(key)
|
|
959
963
|
.agg(pl.col("_absdev").median().alias("_mad"))
|
|
960
964
|
)
|
|
961
|
-
stats_df = med_df.join(
|
|
965
|
+
stats_df = med_df.join(
|
|
966
|
+
mad_df, on=key, maintain_order="left"
|
|
967
|
+
).with_columns(
|
|
962
968
|
(pl.col("_mad") * 1.4826).clip(lower_bound=1e-12).alias("_scale")
|
|
963
969
|
)
|
|
964
970
|
|
|
@@ -971,7 +977,7 @@ class StatsNamespace:
|
|
|
971
977
|
)
|
|
972
978
|
|
|
973
979
|
data = (
|
|
974
|
-
eval_df.join(stats_df, on=key, how="inner")
|
|
980
|
+
eval_df.join(stats_df, on=key, how="inner", maintain_order="left")
|
|
975
981
|
.with_columns(
|
|
976
982
|
((pl.col(column) - pl.col("_med")) / pl.col("_scale")).alias(
|
|
977
983
|
"anomaly_score"
|
|
@@ -1203,7 +1209,12 @@ class StatsNamespace:
|
|
|
1203
1209
|
if spec is None:
|
|
1204
1210
|
# Pull from result.data directly.
|
|
1205
1211
|
if col_name in df.columns:
|
|
1206
|
-
feat = feat.join(
|
|
1212
|
+
feat = feat.join(
|
|
1213
|
+
df.select([key, col_name]),
|
|
1214
|
+
on=key,
|
|
1215
|
+
how="left",
|
|
1216
|
+
maintain_order="left",
|
|
1217
|
+
)
|
|
1207
1218
|
# else silently skip — not in data and no spec provided
|
|
1208
1219
|
continue
|
|
1209
1220
|
|
|
@@ -1286,7 +1297,12 @@ class StatsNamespace:
|
|
|
1286
1297
|
else:
|
|
1287
1298
|
fetched = collect_lf(col_lf.group_by(key).agg(agg_expr))
|
|
1288
1299
|
|
|
1289
|
-
feat = feat.join(
|
|
1300
|
+
feat = feat.join(
|
|
1301
|
+
fetched.select([key, col_name]),
|
|
1302
|
+
on=key,
|
|
1303
|
+
how="left",
|
|
1304
|
+
maintain_order="left",
|
|
1305
|
+
)
|
|
1290
1306
|
|
|
1291
1307
|
feature_cols = [c for c in feat.columns if c != key]
|
|
1292
1308
|
|
|
@@ -184,13 +184,24 @@ class Result:
|
|
|
184
184
|
# 2. Push down the geometry filter into the Parquet reader using is_in
|
|
185
185
|
# so we don't scan the entire static geometry file (which takes ~0.4s).
|
|
186
186
|
geo_lf = self.entity.geometry_lazy
|
|
187
|
+
geo_schema = geo_lf.collect_schema()
|
|
188
|
+
cast_exprs = [
|
|
189
|
+
pl.col(k).cast(geo_schema[k])
|
|
190
|
+
for k in key_cols
|
|
191
|
+
if k in geo_schema and keys_df[k].dtype != geo_schema[k]
|
|
192
|
+
]
|
|
193
|
+
if cast_exprs:
|
|
194
|
+
keys_df = keys_df.with_columns(cast_exprs)
|
|
195
|
+
|
|
187
196
|
if len(key_cols) == 1 and keys_df.height < 50000:
|
|
188
197
|
key = key_cols[0]
|
|
189
198
|
geo_lf = geo_lf.filter(pl.col(key).is_in(keys_df[key].to_list()))
|
|
190
199
|
else:
|
|
191
|
-
geo_lf = geo_lf.join(
|
|
200
|
+
geo_lf = geo_lf.join(
|
|
201
|
+
keys_df.lazy(), on=key_cols, how="semi", maintain_order="left"
|
|
202
|
+
)
|
|
192
203
|
|
|
193
|
-
joined = res.data.join(geo_lf, on=key_cols, how="left")
|
|
204
|
+
joined = res.data.join(geo_lf, on=key_cols, how="left", maintain_order="left")
|
|
194
205
|
|
|
195
206
|
# 3. Cache the joined geometry so multiple plots are instant.
|
|
196
207
|
return res._replace(data=joined, has_geometry=True).materialise()
|
|
@@ -14,6 +14,7 @@ from core_lens.utils.polars_utils import (
|
|
|
14
14
|
scan_with_key_filter,
|
|
15
15
|
collect_lf,
|
|
16
16
|
cached_read_schema,
|
|
17
|
+
cast_filter_values,
|
|
17
18
|
)
|
|
18
19
|
|
|
19
20
|
if TYPE_CHECKING:
|
|
@@ -129,20 +130,22 @@ class View:
|
|
|
129
130
|
|
|
130
131
|
filter_expr = pl.lit(True)
|
|
131
132
|
for col, val in kwargs.items():
|
|
132
|
-
|
|
133
|
+
col_dtype = schema_types.get(col)
|
|
134
|
+
is_list_col = isinstance(col_dtype, pl.List)
|
|
135
|
+
cast_val = cast_filter_values(val, col_dtype)
|
|
133
136
|
if isinstance(val, list):
|
|
134
137
|
if is_list_col:
|
|
135
138
|
filter_expr = (
|
|
136
139
|
filter_expr
|
|
137
|
-
& pl.col(col).list.eval(pl.element().is_in(
|
|
140
|
+
& pl.col(col).list.eval(pl.element().is_in(cast_val)).list.any()
|
|
138
141
|
)
|
|
139
142
|
else:
|
|
140
|
-
filter_expr = filter_expr & pl.col(col).is_in(
|
|
143
|
+
filter_expr = filter_expr & pl.col(col).is_in(cast_val)
|
|
141
144
|
else:
|
|
142
145
|
if is_list_col:
|
|
143
|
-
filter_expr = filter_expr & pl.col(col).list.contains(
|
|
146
|
+
filter_expr = filter_expr & pl.col(col).list.contains(cast_val)
|
|
144
147
|
else:
|
|
145
|
-
filter_expr = filter_expr & (pl.col(col) ==
|
|
148
|
+
filter_expr = filter_expr & (pl.col(col) == cast_val)
|
|
146
149
|
|
|
147
150
|
keys = lf.filter(filter_expr).select(self.entity.key_cols)
|
|
148
151
|
|
|
@@ -202,8 +205,10 @@ class View:
|
|
|
202
205
|
candidates = bbox_intersects_geometry(self.entity._index, geometry)
|
|
203
206
|
candidates = (
|
|
204
207
|
candidates.lazy()
|
|
205
|
-
.join(
|
|
206
|
-
|
|
208
|
+
.join(
|
|
209
|
+
self.keys, on=self.entity.key_cols, how="inner", maintain_order="left"
|
|
210
|
+
)
|
|
211
|
+
.collect(engine="in-memory")
|
|
207
212
|
)
|
|
208
213
|
|
|
209
214
|
keys_df = exact_spatial_filter(
|
|
@@ -521,8 +526,11 @@ class View:
|
|
|
521
526
|
data_df.select(self.entity.key_cols).lazy(),
|
|
522
527
|
on=self.entity.key_cols,
|
|
523
528
|
how="semi",
|
|
524
|
-
|
|
525
|
-
|
|
529
|
+
maintain_order="left",
|
|
530
|
+
).collect(engine="in-memory")
|
|
531
|
+
data_df = data_df.join(
|
|
532
|
+
geom_df, on=self.entity.key_cols, how="left", maintain_order="left"
|
|
533
|
+
)
|
|
526
534
|
|
|
527
535
|
from core_lens.utils.spatial import execute_spatial_join
|
|
528
536
|
|