apmtools 0.2.39__tar.gz → 0.2.41__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.39 → apmtools-0.2.41}/PKG-INFO +1 -1
- {apmtools-0.2.39 → apmtools-0.2.41}/pyproject.toml +1 -1
- {apmtools-0.2.39 → apmtools-0.2.41}/src/apmtools/classes.py +1 -1
- {apmtools-0.2.39 → apmtools-0.2.41}/src/apmtools/shiny/summary.py +35 -5
- {apmtools-0.2.39 → apmtools-0.2.41}/.gitignore +0 -0
- {apmtools-0.2.39 → apmtools-0.2.41}/LICENSE +0 -0
- {apmtools-0.2.39 → apmtools-0.2.41}/README.md +0 -0
- {apmtools-0.2.39 → apmtools-0.2.41}/src/apmtools/__init__.py +0 -0
- {apmtools-0.2.39 → apmtools-0.2.41}/src/apmtools/data_processing.py +0 -0
- {apmtools-0.2.39 → apmtools-0.2.41}/src/apmtools/functions.py +0 -0
- {apmtools-0.2.39 → apmtools-0.2.41}/src/apmtools/legacy.py +0 -0
- {apmtools-0.2.39 → apmtools-0.2.41}/src/apmtools/shiny/__init__.py +0 -0
- {apmtools-0.2.39 → apmtools-0.2.41}/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.41
|
|
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>
|
|
@@ -1161,7 +1161,7 @@ class Summary(pd.DataFrame):
|
|
|
1161
1161
|
if attribute in self.columns:
|
|
1162
1162
|
return set(self[attribute])
|
|
1163
1163
|
|
|
1164
|
-
def group_stat(self, value_cols, frequency=None, grouping_by=["identifier"], time_col='
|
|
1164
|
+
def group_stat(self, value_cols, frequency=None, grouping_by=["identifier"], time_col='datetime', id_col='identifier',
|
|
1165
1165
|
min_completeness=1.0, only_complete=True, flooring_timestamp=True, aggregate=["mean"]):
|
|
1166
1166
|
"""
|
|
1167
1167
|
Filter a DataFrame down to only the (monitor, hour) groups that have
|
|
@@ -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
|
|
|
@@ -1681,7 +1684,6 @@ def create_summary_app(
|
|
|
1681
1684
|
categorical_columns = _categorical_columns(summary)
|
|
1682
1685
|
datetime_columns = _datetime_columns(summary)
|
|
1683
1686
|
timedelta_columns = _timedelta_columns(summary)
|
|
1684
|
-
|
|
1685
1687
|
time_group_columns = [f for f in FREQUENCIES.values(
|
|
1686
1688
|
) if f is not None and f not in column_map]
|
|
1687
1689
|
group_columns = _alphabetical(
|
|
@@ -1988,7 +1990,8 @@ def create_summary_app(
|
|
|
1988
1990
|
choices = categorical_columns
|
|
1989
1991
|
multiple = True
|
|
1990
1992
|
elif kind == "line":
|
|
1991
|
-
choices =
|
|
1993
|
+
choices = _alphabetical(
|
|
1994
|
+
datetime_columns + timedelta_columns)
|
|
1992
1995
|
multiple = False
|
|
1993
1996
|
else:
|
|
1994
1997
|
choices = numeric_columns
|
|
@@ -2133,6 +2136,12 @@ def create_summary_app(
|
|
|
2133
2136
|
value_columns = list(dict.fromkeys(
|
|
2134
2137
|
[column_map[selected_x[0]], *value_columns]
|
|
2135
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()
|
|
2136
2145
|
if should_aggregate:
|
|
2137
2146
|
current = _aggregate(
|
|
2138
2147
|
current, value_columns, frequency, grouping_by, aggregate,
|
|
@@ -2158,6 +2167,10 @@ def create_summary_app(
|
|
|
2158
2167
|
should_aggregate = AGGREGATES.get(
|
|
2159
2168
|
input.aggregate()) is not None
|
|
2160
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."}
|
|
2161
2174
|
if plot_df.empty or not selected_x or (
|
|
2162
2175
|
selected_plot != "histogram" and not selected_y
|
|
2163
2176
|
):
|
|
@@ -2180,7 +2193,9 @@ def create_summary_app(
|
|
|
2180
2193
|
x_column = mapped(selected_x[0])
|
|
2181
2194
|
y_columns = [mapped(selected_y[0])]
|
|
2182
2195
|
else:
|
|
2183
|
-
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]])
|
|
2184
2199
|
y_columns = [mapped(c) for c in selected_y]
|
|
2185
2200
|
|
|
2186
2201
|
if selected_plot == "line" and should_aggregate and frequency is None:
|
|
@@ -2198,6 +2213,20 @@ def create_summary_app(
|
|
|
2198
2213
|
plot_df, x_columns)
|
|
2199
2214
|
x_column = "__plot_category"
|
|
2200
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
|
+
|
|
2201
2230
|
# Multiple grouping columns are supported by group_stat(). For
|
|
2202
2231
|
# plotting, Plotly needs one color/category column, so combine the
|
|
2203
2232
|
# selected grouping columns into a readable composite label.
|
|
@@ -2212,7 +2241,7 @@ def create_summary_app(
|
|
|
2212
2241
|
return {"data": plot_df, "kind": selected_plot, "x": x_column,
|
|
2213
2242
|
"ys": y_columns, "groups": group_columns,
|
|
2214
2243
|
"categories": x_columns if selected_plot in ("box", "bar") else None,
|
|
2215
|
-
"frequency": frequency}
|
|
2244
|
+
"frequency": frequency, "x_label_override": x_label_override}
|
|
2216
2245
|
|
|
2217
2246
|
@reactive.calc
|
|
2218
2247
|
def base_figure():
|
|
@@ -2260,6 +2289,7 @@ def create_summary_app(
|
|
|
2260
2289
|
fig, selected_plot, x_column, y_columns,
|
|
2261
2290
|
category_columns=x_columns if selected_plot in (
|
|
2262
2291
|
"box", "bar") else None,
|
|
2292
|
+
x_label_override=spec.get("x_label_override"),
|
|
2263
2293
|
)
|
|
2264
2294
|
fig.update_layout(
|
|
2265
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
|