core-lens 0.1.dev154__tar.gz → 0.1.dev159__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.dev154 → core_lens-0.1.dev159}/PKG-INFO +1 -1
- core_lens-0.1.dev159/src/core_lens/__main__.py +36 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/src/core_lens/_version.py +2 -2
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/src/core_lens/base/namespaces/plot.py +102 -4
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/src/core_lens/base/namespaces/stats.py +29 -45
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/tests/unit/test_main.py +5 -1
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/tests/unit/test_plot.py +2 -2
- core_lens-0.1.dev154/src/core_lens/__main__.py +0 -14
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/.github/ISSUE_TEMPLATE/blank-proposal.yaml +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/.github/ISSUE_TEMPLATE/bug-report.yaml +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/.github/ISSUE_TEMPLATE/feature-request.yaml +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/.github/pull_request_template.md +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/.github/workflows/ci.yml +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/.github/workflows/gh-pages.yml +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/.github/workflows/pre-release.yml +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/.github/workflows/release.yml +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/.gitignore +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/.gitmessage +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/.pre-commit-config.yaml +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/.python-version +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/CONTRIBUTING.md +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/LICENSE +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/README.md +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/SKILLS.md +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/benchmarks/README.md +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/benchmarks/bench_aoi.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/benchmarks/bench_export.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/benchmarks/bench_polars_utils.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/benchmarks/bench_result.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/benchmarks/bench_schema.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/benchmarks/bench_season.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/benchmarks/bench_spatial.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/benchmarks/bench_view.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/benchmarks/run_all.sh +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/docs/Makefile +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/docs/make.bat +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/docs/source/concepts.md +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/docs/source/conf.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/docs/source/export.md +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/docs/source/index.rst +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/docs/source/intro.md +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/docs/source/logging.md +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/docs/source/plots.md +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/docs/source/plugins.md +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/docs/source/queries.md +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/docs/source/quickstart.md +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/docs/source/stats.md +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/examples/demo_mws.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/examples/demo_tehsil.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/hooks/mypy.sh +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/hooks/no-parquet-outside-fixtures.sh +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/hooks/pytest.sh +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/pyproject.toml +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/src/core_lens/__init__.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/src/core_lens/aoi.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/src/core_lens/base/__init__.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/src/core_lens/base/entity.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/src/core_lens/base/namespaces/__init__.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/src/core_lens/base/result.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/src/core_lens/base/view.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/src/core_lens/entities/__init__.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/src/core_lens/entities/mws.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/src/core_lens/entities/tehsil.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/src/core_lens/entities/waterbody.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/src/core_lens/export/__init__.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/src/core_lens/export/formats.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/src/core_lens/py.typed +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/src/core_lens/schema/__init__.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/src/core_lens/schema/detection.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/src/core_lens/schema/profile.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/src/core_lens/utils/__init__.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/src/core_lens/utils/paths.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/src/core_lens/utils/polars_utils.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/src/core_lens/utils/season.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/src/core_lens/utils/spatial.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/tests/fixtures/generate_fixtures.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/tests/unit/conftest.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/tests/unit/test_aoi.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/tests/unit/test_entities.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/tests/unit/test_entity.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/tests/unit/test_export.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/tests/unit/test_polars_utils.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/tests/unit/test_profile.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/tests/unit/test_result.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/tests/unit/test_schema_detection.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/tests/unit/test_schema_profile.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/tests/unit/test_season.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/tests/unit/test_season_config.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/tests/unit/test_spatial.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/tests/unit/test_stats.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/tests/unit/test_view.py +0 -0
- {core_lens-0.1.dev154 → core_lens-0.1.dev159}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: core-lens
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.dev159
|
|
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
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from . import __version__
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def hello() -> str:
|
|
5
|
+
hello_str = """Hello from core-lens!
|
|
6
|
+
|
|
7
|
+
The following primary capabilities are available:
|
|
8
|
+
- AoI: Construct Area of Interest queries (e.g., `AoI(district="Godda")`)
|
|
9
|
+
- Result: Lazy evaluation and computation graph materialisation
|
|
10
|
+
|
|
11
|
+
Analytics (via `result.stats`):
|
|
12
|
+
- anomaly(): Timeseries and cross-sectional anomaly detection
|
|
13
|
+
- change(): Absolute, percentage, and trend changes over time
|
|
14
|
+
- similarity(): Multi-feature similarity search across entities
|
|
15
|
+
- test(): Statistical hypothesis testing
|
|
16
|
+
- correlate(): Feature correlation analysis
|
|
17
|
+
|
|
18
|
+
Visualisation (via `result.plot`):
|
|
19
|
+
- choropleth(): Interactive Lonboard spatial mapping
|
|
20
|
+
- timeseries(): Temporal data visualisation
|
|
21
|
+
- scatter(): Scatter plots for variable comparison
|
|
22
|
+
- distribution(): Histograms and density plots
|
|
23
|
+
- correlation(): Correlation heatmaps between variables
|
|
24
|
+
- heatmap(): 2D density/binned heatmaps
|
|
25
|
+
- matrix(): Scatter matrix/pair plots
|
|
26
|
+
"""
|
|
27
|
+
return hello_str
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def version() -> str:
|
|
31
|
+
return str(__version__)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
if __name__ == "__main__":
|
|
35
|
+
print(hello())
|
|
36
|
+
print(f"Version: {version()}")
|
|
@@ -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.dev159'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 'dev159')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -27,7 +27,6 @@ class SubplotOn(Enum):
|
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
if TYPE_CHECKING:
|
|
30
|
-
import lonboard
|
|
31
30
|
from bokeh.plotting import figure as BokehFigure
|
|
32
31
|
from core_lens.base.result import Result
|
|
33
32
|
|
|
@@ -224,6 +223,102 @@ def _apply_theme(fig: Any, result: "Result", title: str) -> None:
|
|
|
224
223
|
fig.add_layout(subtitle)
|
|
225
224
|
|
|
226
225
|
|
|
226
|
+
class MapWithLegend:
|
|
227
|
+
"""Wrapper around lonboard.Map that adds a legend for Jupyter and HTML exports."""
|
|
228
|
+
|
|
229
|
+
def __init__(
|
|
230
|
+
self,
|
|
231
|
+
map_widget: Any,
|
|
232
|
+
cmap: Any,
|
|
233
|
+
v_min: float,
|
|
234
|
+
v_max: float,
|
|
235
|
+
column_name: str,
|
|
236
|
+
) -> None:
|
|
237
|
+
self._map = map_widget
|
|
238
|
+
self._cmap = cmap
|
|
239
|
+
self._v_min = v_min
|
|
240
|
+
self._v_max = v_max
|
|
241
|
+
self._column_name = column_name
|
|
242
|
+
|
|
243
|
+
def __getattr__(self, name: str) -> Any:
|
|
244
|
+
return getattr(self._map, name)
|
|
245
|
+
|
|
246
|
+
def _generate_legend_div(self, absolute: bool = True) -> str:
|
|
247
|
+
import matplotlib.colors as mcolors
|
|
248
|
+
import numpy as np
|
|
249
|
+
|
|
250
|
+
stops = 5
|
|
251
|
+
style = (
|
|
252
|
+
"position:absolute; bottom:35px; left:20px; z-index:999; "
|
|
253
|
+
if absolute
|
|
254
|
+
else ""
|
|
255
|
+
)
|
|
256
|
+
style += (
|
|
257
|
+
"background:white; padding:8px 12px; border-radius:4px; "
|
|
258
|
+
"box-shadow:0 1px 4px rgba(0,0,0,.3); font-family:sans-serif; font-size:12px;"
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
html = [
|
|
262
|
+
f'<div style="{style}">',
|
|
263
|
+
f'<b style="margin-bottom:4px;display:inline-block;">{self._column_name}</b><br>',
|
|
264
|
+
]
|
|
265
|
+
|
|
266
|
+
if np.isnan(self._v_min) or np.isnan(self._v_max) or self._v_min >= self._v_max:
|
|
267
|
+
html.append('<span style="color:gray;">No Data</span>')
|
|
268
|
+
else:
|
|
269
|
+
for i in range(stops):
|
|
270
|
+
frac = (stops - 1 - i) / (
|
|
271
|
+
stops - 1
|
|
272
|
+
) # Reverse order so highest is on top
|
|
273
|
+
val = self._v_min + frac * (self._v_max - self._v_min)
|
|
274
|
+
rgba = self._cmap(frac)
|
|
275
|
+
hex_color = mcolors.to_hex(rgba)
|
|
276
|
+
html.append(
|
|
277
|
+
f'<div style="margin-top:2px;">'
|
|
278
|
+
f'<span style="background:{hex_color};width:12px;height:12px;'
|
|
279
|
+
f"display:inline-block;margin-right:8px;vertical-align:middle;"
|
|
280
|
+
f'border:1px solid #ccc;"></span>'
|
|
281
|
+
f'<span style="vertical-align:middle;">{val:.2f}</span></div>'
|
|
282
|
+
)
|
|
283
|
+
html.append("</div>")
|
|
284
|
+
return "".join(html)
|
|
285
|
+
|
|
286
|
+
def _ipython_display_(self) -> None:
|
|
287
|
+
import ipywidgets # type: ignore[import-untyped]
|
|
288
|
+
from IPython.display import display
|
|
289
|
+
|
|
290
|
+
legend_div = self._generate_legend_div(absolute=False)
|
|
291
|
+
legend_html = ipywidgets.HTML(legend_div)
|
|
292
|
+
|
|
293
|
+
display(ipywidgets.VBox([legend_html, self._map])) # type: ignore[no-untyped-call]
|
|
294
|
+
|
|
295
|
+
def to_html(
|
|
296
|
+
self, filename: str | None = None, title: str | None = None
|
|
297
|
+
) -> str | None:
|
|
298
|
+
"""Export the map to a static HTML string or file, injecting the legend."""
|
|
299
|
+
# lonboard.Map to_html doesn't accept title in all versions,
|
|
300
|
+
# so we conditionally pass it if it's not None or we can just omit title if we aren't sure.
|
|
301
|
+
# But wait, we can just use `self._map.to_html()`
|
|
302
|
+
if title is not None:
|
|
303
|
+
try:
|
|
304
|
+
html_str = self._map.to_html(title=title)
|
|
305
|
+
except TypeError:
|
|
306
|
+
html_str = self._map.to_html()
|
|
307
|
+
else:
|
|
308
|
+
html_str = self._map.to_html()
|
|
309
|
+
|
|
310
|
+
legend_div = self._generate_legend_div(absolute=True)
|
|
311
|
+
|
|
312
|
+
# Inject right before closing </body> tag
|
|
313
|
+
html_str = html_str.replace("</body>", legend_div + "</body>")
|
|
314
|
+
|
|
315
|
+
if filename is not None:
|
|
316
|
+
with open(filename, "w", encoding="utf-8") as f:
|
|
317
|
+
f.write(html_str)
|
|
318
|
+
return None
|
|
319
|
+
return str(html_str)
|
|
320
|
+
|
|
321
|
+
|
|
227
322
|
class PlotNamespace:
|
|
228
323
|
"""Visualisation namespace for :class:`~core_lens.base.result.Result`.
|
|
229
324
|
|
|
@@ -239,7 +334,7 @@ class PlotNamespace:
|
|
|
239
334
|
|
|
240
335
|
def choropleth(
|
|
241
336
|
self, column: str, subplot_on: SubplotOn | None = None
|
|
242
|
-
) -> "
|
|
337
|
+
) -> "MapWithLegend":
|
|
243
338
|
"""Render an interactive choropleth map using Lonboard.
|
|
244
339
|
|
|
245
340
|
If the Result does not already have geometry, it will be attached
|
|
@@ -319,13 +414,16 @@ class PlotNamespace:
|
|
|
319
414
|
else:
|
|
320
415
|
norm_values = (values - v_min) / (v_max - v_min)
|
|
321
416
|
|
|
322
|
-
cmap = mpl.colormaps["
|
|
417
|
+
cmap = mpl.colormaps["plasma_r"]
|
|
323
418
|
|
|
324
419
|
layer = lonboard.PolygonLayer(
|
|
325
420
|
arrow_table,
|
|
326
421
|
get_fill_color=apply_continuous_cmap(norm_values, cmap),
|
|
422
|
+
get_line_color=[211, 211, 211, 255],
|
|
423
|
+
line_width_min_pixels=0.5,
|
|
327
424
|
)
|
|
328
|
-
|
|
425
|
+
map_widget = lonboard.Map(layers=[layer])
|
|
426
|
+
return MapWithLegend(map_widget, cmap, v_min, v_max, column)
|
|
329
427
|
|
|
330
428
|
def timeseries(
|
|
331
429
|
self,
|
|
@@ -489,18 +489,24 @@ class StatsNamespace:
|
|
|
489
489
|
)
|
|
490
490
|
|
|
491
491
|
if method in (ChangeMethod.ABSOLUTE, ChangeMethod.PERCENTAGE):
|
|
492
|
+
join_keys = [key]
|
|
493
|
+
if "season" in df.columns:
|
|
494
|
+
join_keys.append("season")
|
|
495
|
+
|
|
492
496
|
from_df = (
|
|
493
497
|
df.filter(pl.col(year_col) == from_period)
|
|
494
|
-
.select([
|
|
498
|
+
.select(join_keys + [column])
|
|
495
499
|
.rename({column: "value_from"})
|
|
496
500
|
)
|
|
497
501
|
to_df = (
|
|
498
502
|
df.filter(pl.col(year_col) == to_period)
|
|
499
|
-
.select([
|
|
503
|
+
.select(join_keys + [column])
|
|
500
504
|
.rename({column: "value_to"})
|
|
501
505
|
)
|
|
502
|
-
joined = from_df.join(to_df, on=
|
|
506
|
+
joined = from_df.join(to_df, on=join_keys, how="inner").with_columns(
|
|
503
507
|
[
|
|
508
|
+
pl.lit(from_period).alias("year_from").cast(pl.Int32),
|
|
509
|
+
pl.lit(to_period).alias("year_to").cast(pl.Int32),
|
|
504
510
|
(pl.col("value_to") - pl.col("value_from")).alias("change"),
|
|
505
511
|
(
|
|
506
512
|
(pl.col("value_to") - pl.col("value_from"))
|
|
@@ -509,7 +515,16 @@ class StatsNamespace:
|
|
|
509
515
|
).alias("pct_change"),
|
|
510
516
|
]
|
|
511
517
|
)
|
|
512
|
-
|
|
518
|
+
|
|
519
|
+
final_cols = join_keys + [
|
|
520
|
+
"year_from",
|
|
521
|
+
"year_to",
|
|
522
|
+
"value_from",
|
|
523
|
+
"value_to",
|
|
524
|
+
"change",
|
|
525
|
+
"pct_change",
|
|
526
|
+
]
|
|
527
|
+
data = joined.select(final_cols)
|
|
513
528
|
|
|
514
529
|
else: # trend
|
|
515
530
|
period_df = df.filter(
|
|
@@ -532,6 +547,8 @@ class StatsNamespace:
|
|
|
532
547
|
rows.append(
|
|
533
548
|
{
|
|
534
549
|
key: eid,
|
|
550
|
+
"year_from": int(from_period),
|
|
551
|
+
"year_to": int(to_period),
|
|
535
552
|
"slope": float(cast(float, slope)),
|
|
536
553
|
"r_squared": float(cast(float, r_val) ** 2),
|
|
537
554
|
"direction": (
|
|
@@ -859,12 +876,9 @@ class StatsNamespace:
|
|
|
859
876
|
df = self._r.df()
|
|
860
877
|
key = self._r.key_cols[0]
|
|
861
878
|
|
|
862
|
-
min_year: int | None = None
|
|
863
|
-
max_year: int | None = None
|
|
864
|
-
|
|
865
879
|
# --- Build feature DataFrame ------------------------------------------
|
|
866
880
|
# Universe of entity keys from result.data.
|
|
867
|
-
feat = df.select([key])
|
|
881
|
+
feat = df.select([key])
|
|
868
882
|
|
|
869
883
|
for col_name, spec in columns.items():
|
|
870
884
|
if spec is None:
|
|
@@ -884,23 +898,6 @@ class StatsNamespace:
|
|
|
884
898
|
resolution_str, filter_dict = spec
|
|
885
899
|
filter_dict = dict(filter_dict) if filter_dict else {}
|
|
886
900
|
|
|
887
|
-
if "year" in filter_dict:
|
|
888
|
-
yr = filter_dict["year"]
|
|
889
|
-
y0: int | None = None
|
|
890
|
-
y1: int | None = None
|
|
891
|
-
|
|
892
|
-
if isinstance(yr, (list, tuple)) and len(yr) >= 2:
|
|
893
|
-
y0, y1 = int(yr[0]), int(yr[1])
|
|
894
|
-
elif isinstance(yr, int):
|
|
895
|
-
y0, y1 = yr, yr
|
|
896
|
-
|
|
897
|
-
if y0 is not None:
|
|
898
|
-
if min_year is None or y0 < min_year:
|
|
899
|
-
min_year = y0
|
|
900
|
-
if y1 is not None:
|
|
901
|
-
if max_year is None or y1 > max_year:
|
|
902
|
-
max_year = y1
|
|
903
|
-
|
|
904
901
|
entity = self._r.entity
|
|
905
902
|
path: str | None
|
|
906
903
|
if resolution_str == "static":
|
|
@@ -1026,26 +1023,13 @@ class StatsNamespace:
|
|
|
1026
1023
|
dists[tidx] = float("inf") # exclude target itself
|
|
1027
1024
|
top_idx = np.argsort(dists)[:top_n]
|
|
1028
1025
|
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
max_year = int(cast(int, yr_max))
|
|
1037
|
-
|
|
1038
|
-
output_dict: dict[str, Any] = {
|
|
1039
|
-
key: [ids[i] for i in top_idx],
|
|
1040
|
-
"similarity_score": [float(1.0 / (1.0 + dists[i])) for i in top_idx],
|
|
1041
|
-
"rank": list(range(1, len(top_idx) + 1)),
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
if min_year is not None and max_year is not None:
|
|
1045
|
-
output_dict["year_from"] = [min_year] * len(top_idx)
|
|
1046
|
-
output_dict["year_to"] = [max_year] * len(top_idx)
|
|
1047
|
-
|
|
1048
|
-
data = pl.DataFrame(output_dict)
|
|
1026
|
+
data = pl.DataFrame(
|
|
1027
|
+
{
|
|
1028
|
+
key: [ids[i] for i in top_idx],
|
|
1029
|
+
"similarity_score": [float(1.0 / (1.0 + dists[i])) for i in top_idx],
|
|
1030
|
+
"rank": list(range(1, len(top_idx) + 1)),
|
|
1031
|
+
}
|
|
1032
|
+
)
|
|
1049
1033
|
metadata: dict[str, Any] = {
|
|
1050
1034
|
"method": method.value if method is not None else None,
|
|
1051
1035
|
"target": target,
|
|
@@ -8,7 +8,11 @@ def test_version() -> None:
|
|
|
8
8
|
def test_hello() -> None:
|
|
9
9
|
from core_lens.__main__ import hello
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
msg = hello()
|
|
12
|
+
assert msg.startswith("Hello from core-lens!")
|
|
13
|
+
assert "AoI: Construct Area of Interest queries" in msg
|
|
14
|
+
assert "Analytics (via `result.stats`):" in msg
|
|
15
|
+
assert "Visualisation (via `result.plot`):" in msg
|
|
12
16
|
|
|
13
17
|
|
|
14
18
|
def test_version_type() -> None:
|
|
@@ -9,7 +9,7 @@ from typing import Any
|
|
|
9
9
|
from core_lens.base.entity import BaseEntity
|
|
10
10
|
from core_lens.base.result import Result
|
|
11
11
|
from core_lens.schema.profile import Resolution, SchemaProfile
|
|
12
|
-
from core_lens.base.namespaces.plot import SubplotOn
|
|
12
|
+
from core_lens.base.namespaces.plot import SubplotOn, MapWithLegend
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class DummyEntity(BaseEntity):
|
|
@@ -79,7 +79,7 @@ def test_choropleth_basic(dummy_result: Result) -> None:
|
|
|
79
79
|
"""Test choropleth creates a lonboard Map correctly."""
|
|
80
80
|
plot_map = dummy_result.plot.choropleth("value")
|
|
81
81
|
|
|
82
|
-
assert isinstance(plot_map,
|
|
82
|
+
assert isinstance(plot_map, MapWithLegend)
|
|
83
83
|
assert len(plot_map.layers) == 1
|
|
84
84
|
layer = plot_map.layers[0]
|
|
85
85
|
assert isinstance(layer, lonboard.PolygonLayer)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|