apmtools 0.2.38__tar.gz → 0.2.40__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.
- {apmtools-0.2.38 → apmtools-0.2.40}/PKG-INFO +1 -1
- {apmtools-0.2.38 → apmtools-0.2.40}/pyproject.toml +1 -1
- {apmtools-0.2.38 → apmtools-0.2.40}/src/apmtools/shiny/summary.py +36 -4
- {apmtools-0.2.38 → apmtools-0.2.40}/.gitignore +0 -0
- {apmtools-0.2.38 → apmtools-0.2.40}/LICENSE +0 -0
- {apmtools-0.2.38 → apmtools-0.2.40}/README.md +0 -0
- {apmtools-0.2.38 → apmtools-0.2.40}/src/apmtools/__init__.py +0 -0
- {apmtools-0.2.38 → apmtools-0.2.40}/src/apmtools/classes.py +0 -0
- {apmtools-0.2.38 → apmtools-0.2.40}/src/apmtools/data_processing.py +0 -0
- {apmtools-0.2.38 → apmtools-0.2.40}/src/apmtools/functions.py +0 -0
- {apmtools-0.2.38 → apmtools-0.2.40}/src/apmtools/legacy.py +0 -0
- {apmtools-0.2.38 → apmtools-0.2.40}/src/apmtools/shiny/__init__.py +0 -0
- {apmtools-0.2.38 → apmtools-0.2.40}/src/apmtools/units.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: apmtools
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.40
|
|
4
4
|
Summary: A collection of tools for processing air pollution monitoring data
|
|
5
5
|
Project-URL: Homepage, https://github.com/federlorenz/apmtools
|
|
6
6
|
Author-email: Federico Lorenzetti <lorenzetti.federico@gmail.com>
|
|
@@ -36,7 +36,7 @@ def _axis_label(variable: str) -> str:
|
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
def _label_plot_axes(fig, plot_type, x_column, y_columns, *,
|
|
39
|
-
category_columns=None):
|
|
39
|
+
category_columns=None, x_label_override=None):
|
|
40
40
|
"""Label plot axes while retaining special histogram and facet labels."""
|
|
41
41
|
if plot_type == "histogram":
|
|
42
42
|
fig.update_xaxes(title_text=_axis_label(x_column))
|
|
@@ -58,6 +58,8 @@ def _label_plot_axes(fig, plot_type, x_column, y_columns, *,
|
|
|
58
58
|
fig.update_xaxes(title_text=_axis_label(x_column))
|
|
59
59
|
if y_columns:
|
|
60
60
|
fig.update_yaxes(title_text=_axis_label(y_columns[0]))
|
|
61
|
+
if x_label_override is not None:
|
|
62
|
+
fig.update_xaxes(title_text=x_label_override)
|
|
61
63
|
return fig
|
|
62
64
|
|
|
63
65
|
|
|
@@ -947,6 +949,7 @@ def _matplotlib_defaults(spec, *, palette="Default"):
|
|
|
947
949
|
y_label = ("Probability density" if kind == "histogram" else
|
|
948
950
|
"Value" if kind == "line" and len(ys) > 1 else
|
|
949
951
|
_axis_label(ys[0]) if ys else "Count")
|
|
952
|
+
x_label = spec.get("x_label_override") or x_label
|
|
950
953
|
return dict(x_label=x_label, y_label=y_label, names=names, colors=colors)
|
|
951
954
|
|
|
952
955
|
|
|
@@ -1680,6 +1683,7 @@ def create_summary_app(
|
|
|
1680
1683
|
numeric_columns = _alphabetical(_numeric_columns(summary))
|
|
1681
1684
|
categorical_columns = _categorical_columns(summary)
|
|
1682
1685
|
datetime_columns = _datetime_columns(summary)
|
|
1686
|
+
timedelta_columns = _timedelta_columns(summary)
|
|
1683
1687
|
time_group_columns = [f for f in FREQUENCIES.values(
|
|
1684
1688
|
) if f is not None and f not in column_map]
|
|
1685
1689
|
group_columns = _alphabetical(
|
|
@@ -1986,7 +1990,8 @@ def create_summary_app(
|
|
|
1986
1990
|
choices = categorical_columns
|
|
1987
1991
|
multiple = True
|
|
1988
1992
|
elif kind == "line":
|
|
1989
|
-
choices =
|
|
1993
|
+
choices = _alphabetical(
|
|
1994
|
+
datetime_columns + timedelta_columns)
|
|
1990
1995
|
multiple = False
|
|
1991
1996
|
else:
|
|
1992
1997
|
choices = numeric_columns
|
|
@@ -2131,6 +2136,12 @@ def create_summary_app(
|
|
|
2131
2136
|
value_columns = list(dict.fromkeys(
|
|
2132
2137
|
[column_map[selected_x[0]], *value_columns]
|
|
2133
2138
|
))
|
|
2139
|
+
if (kind == "line" and selected_x[0] in timedelta_columns
|
|
2140
|
+
and should_aggregate):
|
|
2141
|
+
# group_stat aggregation drops the original timedelta X
|
|
2142
|
+
# unless it is explicitly aggregated. Never silently plot
|
|
2143
|
+
# an unrelated frequency column instead.
|
|
2144
|
+
return pd.DataFrame()
|
|
2134
2145
|
if should_aggregate:
|
|
2135
2146
|
current = _aggregate(
|
|
2136
2147
|
current, value_columns, frequency, grouping_by, aggregate,
|
|
@@ -2156,6 +2167,10 @@ def create_summary_app(
|
|
|
2156
2167
|
should_aggregate = AGGREGATES.get(
|
|
2157
2168
|
input.aggregate()) is not None
|
|
2158
2169
|
|
|
2170
|
+
if (selected_plot == "line" and selected_x
|
|
2171
|
+
and selected_x[0] in timedelta_columns
|
|
2172
|
+
and should_aggregate):
|
|
2173
|
+
return {"error": "Timedelta X-axis requires Aggregation: None. Disable aggregation to plot elapsed time."}
|
|
2159
2174
|
if plot_df.empty or not selected_x or (
|
|
2160
2175
|
selected_plot != "histogram" and not selected_y
|
|
2161
2176
|
):
|
|
@@ -2178,7 +2193,9 @@ def create_summary_app(
|
|
|
2178
2193
|
x_column = mapped(selected_x[0])
|
|
2179
2194
|
y_columns = [mapped(selected_y[0])]
|
|
2180
2195
|
else:
|
|
2181
|
-
x_column =
|
|
2196
|
+
x_column = (column_map[selected_x[0]] if selected_x[0] in timedelta_columns
|
|
2197
|
+
else frequency if frequency is not None
|
|
2198
|
+
else column_map[selected_x[0]])
|
|
2182
2199
|
y_columns = [mapped(c) for c in selected_y]
|
|
2183
2200
|
|
|
2184
2201
|
if selected_plot == "line" and should_aggregate and frequency is None:
|
|
@@ -2196,6 +2213,20 @@ def create_summary_app(
|
|
|
2196
2213
|
plot_df, x_columns)
|
|
2197
2214
|
x_column = "__plot_category"
|
|
2198
2215
|
|
|
2216
|
+
# Convert durations once for Plotly, Matplotlib, editable
|
|
2217
|
+
# numeric bounds, faceting and exported plots. Retain the
|
|
2218
|
+
# original variable name and label its displayed unit.
|
|
2219
|
+
x_label_override = None
|
|
2220
|
+
if (selected_plot == "line" and
|
|
2221
|
+
pd.api.types.is_timedelta64_dtype(plot_df[x_column])):
|
|
2222
|
+
plot_df = plot_df.copy()
|
|
2223
|
+
plot_df[x_column] = plot_df[x_column] / \
|
|
2224
|
+
pd.Timedelta(hours=1)
|
|
2225
|
+
x_label_override = f"{x_column} [hours]"
|
|
2226
|
+
# Frequency-based gap detection is only meaningful for
|
|
2227
|
+
# aggregated timestamp series, not raw durations.
|
|
2228
|
+
frequency = None
|
|
2229
|
+
|
|
2199
2230
|
# Multiple grouping columns are supported by group_stat(). For
|
|
2200
2231
|
# plotting, Plotly needs one color/category column, so combine the
|
|
2201
2232
|
# selected grouping columns into a readable composite label.
|
|
@@ -2210,7 +2241,7 @@ def create_summary_app(
|
|
|
2210
2241
|
return {"data": plot_df, "kind": selected_plot, "x": x_column,
|
|
2211
2242
|
"ys": y_columns, "groups": group_columns,
|
|
2212
2243
|
"categories": x_columns if selected_plot in ("box", "bar") else None,
|
|
2213
|
-
"frequency": frequency}
|
|
2244
|
+
"frequency": frequency, "x_label_override": x_label_override}
|
|
2214
2245
|
|
|
2215
2246
|
@reactive.calc
|
|
2216
2247
|
def base_figure():
|
|
@@ -2258,6 +2289,7 @@ def create_summary_app(
|
|
|
2258
2289
|
fig, selected_plot, x_column, y_columns,
|
|
2259
2290
|
category_columns=x_columns if selected_plot in (
|
|
2260
2291
|
"box", "bar") else None,
|
|
2292
|
+
x_label_override=spec.get("x_label_override"),
|
|
2261
2293
|
)
|
|
2262
2294
|
fig.update_layout(
|
|
2263
2295
|
hovermode="x unified" if selected_plot == "line" else "closest",
|
|
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
|