scistackplotdb 0.1.53__tar.gz → 0.1.56__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.
- {scistackplotdb-0.1.53 → scistackplotdb-0.1.56}/PKG-INFO +5 -5
- {scistackplotdb-0.1.53 → scistackplotdb-0.1.56}/src/scistackplotdb/load.py +58 -8
- {scistackplotdb-0.1.53 → scistackplotdb-0.1.56}/src/scistackplotdb/source.py +24 -0
- {scistackplotdb-0.1.53 → scistackplotdb-0.1.56}/.gitignore +0 -0
- {scistackplotdb-0.1.53 → scistackplotdb-0.1.56}/README.md +0 -0
- {scistackplotdb-0.1.53 → scistackplotdb-0.1.56}/pyproject.toml +0 -0
- {scistackplotdb-0.1.53 → scistackplotdb-0.1.56}/src/scistackplotdb/__init__.py +0 -0
- {scistackplotdb-0.1.53 → scistackplotdb-0.1.56}/src/scistackplotdb/_versioned.py +0 -0
- {scistackplotdb-0.1.53 → scistackplotdb-0.1.56}/src/scistackplotdb/bundle_section.py +0 -0
- {scistackplotdb-0.1.53 → scistackplotdb-0.1.56}/src/scistackplotdb/endpoint.py +0 -0
- {scistackplotdb-0.1.53 → scistackplotdb-0.1.56}/src/scistackplotdb/hierarchy.py +0 -0
- {scistackplotdb-0.1.53 → scistackplotdb-0.1.56}/src/scistackplotdb/presets.py +0 -0
- {scistackplotdb-0.1.53 → scistackplotdb-0.1.56}/src/scistackplotdb/saved.py +0 -0
- {scistackplotdb-0.1.53 → scistackplotdb-0.1.56}/src/scistackplotdb/variants.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: scistackplotdb
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.56
|
|
4
4
|
Summary: scidb-backed plotting: load variables into long tables and plot them with scistackplot
|
|
5
5
|
Author: SciStack Contributors
|
|
6
6
|
License-Expression: MIT
|
|
@@ -17,10 +17,10 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
17
17
|
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
18
18
|
Classifier: Typing :: Typed
|
|
19
19
|
Requires-Python: >=3.10
|
|
20
|
-
Requires-Dist: sciduckdb==0.1.
|
|
21
|
-
Requires-Dist: scistack-db==0.1.
|
|
22
|
-
Requires-Dist: scistacklog==0.1.
|
|
23
|
-
Requires-Dist: scistackplot==0.1.
|
|
20
|
+
Requires-Dist: sciduckdb==0.1.56
|
|
21
|
+
Requires-Dist: scistack-db==0.1.56
|
|
22
|
+
Requires-Dist: scistacklog==0.1.56
|
|
23
|
+
Requires-Dist: scistackplot==0.1.56
|
|
24
24
|
Provides-Extra: dev
|
|
25
25
|
Requires-Dist: matplotlib>=3.6; extra == 'dev'
|
|
26
26
|
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
@@ -21,6 +21,7 @@ from typing import Any
|
|
|
21
21
|
|
|
22
22
|
import numpy as np
|
|
23
23
|
import pandas as pd
|
|
24
|
+
from sciduckdb import merge_tz_columns, stored_tz_companions, tz_column_name
|
|
24
25
|
from scistacklog import Log
|
|
25
26
|
from scistackplot import CODE_FACTOR_PREFIX, RUN_FACTOR_PREFIX
|
|
26
27
|
from scistackplot.framesize import format_extent, frame_extent
|
|
@@ -130,6 +131,41 @@ def data_columns_for(db, variable: str) -> list[str]:
|
|
|
130
131
|
return list(data_column_types_for(db, variable))
|
|
131
132
|
|
|
132
133
|
|
|
134
|
+
def _stored_column_types(db, variable: str) -> dict[str, str]:
|
|
135
|
+
"""``{column: DuckDB type}`` of the variable's table as stored (zone
|
|
136
|
+
companions included), in ordinal order."""
|
|
137
|
+
table = table_name_for(db, variable)
|
|
138
|
+
rows = db._duck._fetchall(
|
|
139
|
+
"SELECT column_name, data_type FROM information_schema.columns "
|
|
140
|
+
"WHERE table_name = ? AND column_name != 'record_id' "
|
|
141
|
+
"ORDER BY ordinal_position",
|
|
142
|
+
[table],
|
|
143
|
+
)
|
|
144
|
+
return {row[0]: str(row[1] or "") for row in rows}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def datetime_zone_meta(db, variable: str, columns) -> dict:
|
|
148
|
+
"""The dtype metadata ``sciduckdb.merge_tz_columns`` needs to fold the
|
|
149
|
+
``__tz`` zone column of each datetime column in ``columns`` back in:
|
|
150
|
+
``{"columns": {col: {"python_type": "datetime", "unit": ...}}}``. The
|
|
151
|
+
unit comes from the variable's stored metadata; the companion is found
|
|
152
|
+
in the table schema (owner ``sciduckdb.stored_tz_companions``)."""
|
|
153
|
+
zone_cols = stored_tz_companions(_stored_column_types(db, variable))
|
|
154
|
+
sources = [src for src in zone_cols.values() if src in set(columns)]
|
|
155
|
+
if not sources:
|
|
156
|
+
return {"columns": {}}
|
|
157
|
+
stored = (db.get_dtype_meta(variable) or {}).get("columns", {})
|
|
158
|
+
return {
|
|
159
|
+
"columns": {
|
|
160
|
+
src: {
|
|
161
|
+
**{"python_type": "datetime"},
|
|
162
|
+
**{k: v for k, v in (stored.get(src) or {}).items() if k == "unit"},
|
|
163
|
+
}
|
|
164
|
+
for src in sources
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
|
|
133
169
|
def data_column_types_for(db, variable: str) -> dict[str, str]:
|
|
134
170
|
"""``{column: declared DuckDB type}``, in ordinal order.
|
|
135
171
|
|
|
@@ -139,13 +175,7 @@ def data_column_types_for(db, variable: str) -> dict[str, str]:
|
|
|
139
175
|
(:func:`is_container_type`). It never accepts one; see that function.
|
|
140
176
|
"""
|
|
141
177
|
table = table_name_for(db, variable)
|
|
142
|
-
|
|
143
|
-
"SELECT column_name, data_type FROM information_schema.columns "
|
|
144
|
-
"WHERE table_name = ? AND column_name != 'record_id' "
|
|
145
|
-
"ORDER BY ordinal_position",
|
|
146
|
-
[table],
|
|
147
|
-
)
|
|
148
|
-
types = {row[0]: str(row[1] or "") for row in rows}
|
|
178
|
+
types = _stored_column_types(db, variable)
|
|
149
179
|
|
|
150
180
|
# A data column named after a schema key is an address stored as payload:
|
|
151
181
|
# a record saved before the spread/collision rules stripped such columns
|
|
@@ -171,6 +201,18 @@ def data_column_types_for(db, variable: str) -> dict[str, str]:
|
|
|
171
201
|
)
|
|
172
202
|
for name in shadowed:
|
|
173
203
|
del types[name]
|
|
204
|
+
|
|
205
|
+
# A datetime column's __tz zone companion is storage, not a column to
|
|
206
|
+
# plot (owner sciduckdb.datetimes). load_variable selects it anyway and
|
|
207
|
+
# folds it back into its datetime column (datetime_zone_meta).
|
|
208
|
+
zone_cols = stored_tz_companions(types)
|
|
209
|
+
if zone_cols:
|
|
210
|
+
Log.debug(
|
|
211
|
+
"variable %r: datetime zone column(s) %s left out of the data columns",
|
|
212
|
+
variable, sorted(zone_cols), layer=LAYER,
|
|
213
|
+
)
|
|
214
|
+
for name in zone_cols:
|
|
215
|
+
del types[name]
|
|
174
216
|
return types
|
|
175
217
|
|
|
176
218
|
|
|
@@ -442,7 +484,12 @@ def load_variable(
|
|
|
442
484
|
schema_select = "".join(
|
|
443
485
|
f', CAST(s."{key}" AS VARCHAR) AS "{key}"' for key in keys
|
|
444
486
|
)
|
|
445
|
-
|
|
487
|
+
# Datetime columns bring their __tz zone column, folded back right
|
|
488
|
+
# after the fetch (sciduckdb.merge_tz_columns): without it every
|
|
489
|
+
# aware value reached a plot as a naive UTC instant.
|
|
490
|
+
zone_meta = datetime_zone_meta(db, variable, columns) if columns else {"columns": {}}
|
|
491
|
+
zone_select = [tz_column_name(c) for c in zone_meta["columns"]]
|
|
492
|
+
data_select = "".join(f', t."{column}"' for column in [*columns, *zone_select])
|
|
446
493
|
query = (
|
|
447
494
|
f"SELECT t.record_id{data_select}{schema_select} "
|
|
448
495
|
f'FROM "{table}" t '
|
|
@@ -464,6 +511,9 @@ def load_variable(
|
|
|
464
511
|
# regression to per-sample boxing is visible in the log, not inferred.
|
|
465
512
|
with timer.phase("fetch"):
|
|
466
513
|
frame = db._duck._fetchdf(query, [variable])
|
|
514
|
+
if zone_meta["columns"]:
|
|
515
|
+
with timer.phase("datetime_zones"):
|
|
516
|
+
merge_tz_columns(frame, zone_meta, where=f"load_variable({variable})")
|
|
467
517
|
|
|
468
518
|
with timer.phase("dataframe"):
|
|
469
519
|
frame = frame[["record_id", *columns, *keys]].copy()
|
|
@@ -28,6 +28,7 @@ from scistackplot.dedup import SingleFlight
|
|
|
28
28
|
from scistackplot.framesize import format_extent, frame_extent
|
|
29
29
|
from scistackplot.sources import BaseSource, UnknownMeasureError
|
|
30
30
|
from scistackplot.variants import VARIABLE_COLUMN
|
|
31
|
+
from scistackplot.temporal import is_temporal_column
|
|
31
32
|
|
|
32
33
|
from .hierarchy import join_frames, joinable, joined_levels
|
|
33
34
|
from .load import (
|
|
@@ -75,7 +76,16 @@ def _as_levels(column):
|
|
|
75
76
|
synthetic-factors invariant 4 exists to prevent. The order matters:
|
|
76
77
|
``astype(str)`` first would turn every missing value into the string
|
|
77
78
|
``"nan"``.
|
|
79
|
+
|
|
80
|
+
A DATETIME column is the exception: its values stay datetimes (missing
|
|
81
|
+
ones named), and ``LongTable.from_frame`` gives each its identity label
|
|
82
|
+
in time order (``scistackplot.temporal.level_labels``) — text that says
|
|
83
|
+
when, not ``2026-07-01 14:00:00+02:00`` sorted as text. The generated code
|
|
84
|
+
emits the same labels (``temporal.factor_label_code``).
|
|
78
85
|
"""
|
|
86
|
+
if is_temporal_column(column):
|
|
87
|
+
out = column.astype(object).where(column.notna(), MISSING_LEVEL)
|
|
88
|
+
return out
|
|
79
89
|
return column.fillna(MISSING_LEVEL).astype(str)
|
|
80
90
|
|
|
81
91
|
|
|
@@ -1380,6 +1390,20 @@ class ScidbSource(BaseSource):
|
|
|
1380
1390
|
usable.append(column)
|
|
1381
1391
|
else:
|
|
1382
1392
|
skipped.append(column)
|
|
1393
|
+
# Datetime fields melt into ONE value column with the rest, which is a
|
|
1394
|
+
# time axis only if every field is a datetime (scistackplot.temporal).
|
|
1395
|
+
# Beside numeric fields they would read as numbers (NaN), so they are
|
|
1396
|
+
# left out and named — group by one (Variable.column) instead.
|
|
1397
|
+
datetimes = [c for c in usable if is_temporal_column(frame[c])]
|
|
1398
|
+
if datetimes and len(datetimes) < len(usable):
|
|
1399
|
+
Log.warn(
|
|
1400
|
+
"variable %r: datetime field(s) %s cannot share one axis with its "
|
|
1401
|
+
"numeric field(s) and were left out — group by one instead",
|
|
1402
|
+
variable_frame.name,
|
|
1403
|
+
datetimes,
|
|
1404
|
+
layer=LAYER,
|
|
1405
|
+
)
|
|
1406
|
+
usable = [c for c in usable if c not in datetimes]
|
|
1383
1407
|
if skipped:
|
|
1384
1408
|
Log.warn(
|
|
1385
1409
|
"variable %r: %d field(s) are not plottable and were dropped: %s",
|
|
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
|