apmtools 0.2.6__tar.gz → 0.2.8__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: apmtools
3
- Version: 0.2.006
3
+ Version: 0.2.008
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>
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "apmtools"
7
- version = "0.2.006"
7
+ version = "0.2.008"
8
8
  authors = [
9
9
  { name="Federico Lorenzetti", email="lorenzetti.federico@gmail.com" },
10
10
  ]
@@ -884,60 +884,57 @@ class Summary(pd.DataFrame):
884
884
  return self
885
885
 
886
886
  def subset(self, filter_dict={}, filter_style='all'):
887
+ """Return rows matching values specified in ``filter_dict``.
888
+
889
+ Parameters
890
+ ----------
891
+ filter_dict : dict
892
+ Mapping of column names to iterables of accepted values.
893
+ filter_style : {'all', 'any', 'negative all', 'negative any'}
894
+ ``all`` keeps rows matching every supplied condition; ``any`` keeps
895
+ rows matching at least one condition. ``negative all`` keeps rows
896
+ that match none of the supplied conditions, and ``negative any``
897
+ keeps rows that do not match any individual condition.
898
+
899
+ Notes
900
+ -----
901
+ Filtering is performed positionally rather than assuming that the
902
+ DataFrame index is ``0..n-1``. This is important because ``Summary``
903
+ objects commonly acquire non-contiguous indices after previous
904
+ filtering operations.
887
905
  """
888
- Return a subset of a Summary, specified in the parameter filter_dict (itself a dictionary) or condition (a function that takes at minimum a value from the dictionary as an input parameter, and return True/False if some condition specified in the function is met. Typically a lambda function of the form lambda x: True if condition else False)
889
- filter_dict is {attrib:["attrib_value_x","attrib_value_y",..]}, where
890
- attrib is an attribute of the elements of dictionary, and attrib_value is a list
891
- of the values of such attrib that the elements of returned dictionary can have
892
- specify filter_style='all' if all conditions should be met to be included in the return dictionary, specify filter_style='any' for including when any condition is met. Default is 'all'.
893
- """
894
- if type(filter_dict) != type(dict()):
906
+
907
+ if not isinstance(filter_dict, dict):
895
908
  print("subset function error: type filter_dict should be dict")
896
909
  return
910
+
897
911
  return_dict = copy.deepcopy(self)
898
912
  a = {}
899
913
 
900
- if filter_style == 'all':
901
- filtering_list = [False]*(len(return_dict))
902
- for i, j in filter_dict.items():
903
- if (i in return_dict.columns):
904
- for z in range(len(return_dict)):
905
- if return_dict.loc[z, i] in j:
906
- filtering_list[z] = True
907
- else:
908
- filtering_list[z] = False
909
- return_dict = return_dict.loc[filtering_list]
914
+ if filter_style not in {'all', 'any', 'negative all', 'negative any'}:
915
+ return return_dict
910
916
 
911
- if filter_style == 'any':
912
- filtering_list = [False]*(len(return_dict))
913
- for i, j in filter_dict.items():
914
- if (i in return_dict.columns):
915
- for z in range(len(return_dict)):
916
- if return_dict.loc[z, i] in j:
917
- filtering_list[z] = True
918
- return_dict = return_dict.loc[filtering_list]
919
-
920
- if filter_style == 'negative all':
921
- filtering_list = [True]*(len(return_dict))
922
- for i, j in filter_dict.items():
923
- if (i in return_dict.columns):
924
- for z in range(len(return_dict)):
925
- if return_dict.loc[z, i] in j:
926
- filtering_list[z] = False
927
- else:
928
- filtering_list[z] = True
929
- return_dict = return_dict.loc[filtering_list]
930
-
931
- if filter_style == 'negative any':
932
- filtering_list = [True]*(len(return_dict))
933
- for i, j in filter_dict.items():
934
- if (i in return_dict.columns):
935
- for z in range(len(return_dict)):
936
- if return_dict.loc[z, i] in j:
937
- filtering_list[z] = False
938
- return_dict = return_dict.loc[filtering_list]
939
-
940
- return return_dict
917
+ # Only conditions referring to existing columns can be evaluated.
918
+ conditions = []
919
+ for column, values in filter_dict.items():
920
+ if column in return_dict.columns:
921
+ conditions.append(return_dict[column].isin(values).to_numpy())
922
+
923
+ if not conditions:
924
+ return return_dict
925
+
926
+ conditions = pd.DataFrame(conditions, dtype=bool)
927
+
928
+ if filter_style == 'all':
929
+ mask = conditions.all(axis=0).to_numpy()
930
+ elif filter_style == 'any':
931
+ mask = conditions.any(axis=0).to_numpy()
932
+ elif filter_style == 'negative all':
933
+ mask = (~conditions).all(axis=0).to_numpy()
934
+ else: # negative any
935
+ mask = (~conditions).any(axis=0).to_numpy()
936
+
937
+ return return_dict.loc[mask]
941
938
 
942
939
  def show(self, number=0, key=None):
943
940
  """
@@ -351,12 +351,43 @@ def create_summary_app(
351
351
  )
352
352
 
353
353
  if selected_plot == "histogram":
354
- long_df = plot_df[y_columns].melt(
355
- var_name="variable", value_name="value")
356
- fig = px.histogram(
357
- long_df, x="value", color="variable", nbins=int(input.bins()),
358
- barmode="overlay" if len(y_columns) > 1 else "relative",
359
- )
354
+ if group_columns:
355
+ # With grouping, overlay one histogram per group on the
356
+ # same axes. ``group`` is the color dimension rather
357
+ # than the selected variable, so each group gets its own
358
+ # histogram. Transparency makes overlapping groups
359
+ # directly comparable.
360
+ group_column = "__plot_group"
361
+ long_df = plot_df[[group_column, *y_columns]].melt(
362
+ id_vars=[group_column],
363
+ var_name="variable",
364
+ value_name="value",
365
+ )
366
+ fig = px.histogram(
367
+ long_df,
368
+ x="value",
369
+ color=group_column,
370
+ nbins=int(input.bins()),
371
+ barmode="overlay",
372
+ opacity=0.45,
373
+ facet_row=("variable" if len(y_columns) > 1 else None),
374
+ )
375
+ # Keep the groups as the legend entries rather than the
376
+ # selected Y variables.
377
+ fig.update_traces(marker_line_width=0)
378
+ else:
379
+ long_df = plot_df[y_columns].melt(
380
+ var_name="variable", value_name="value"
381
+ )
382
+ fig = px.histogram(
383
+ long_df,
384
+ x="value",
385
+ color="variable",
386
+ nbins=int(input.bins()),
387
+ barmode="overlay" if len(
388
+ y_columns) > 1 else "relative",
389
+ opacity=0.65 if len(y_columns) > 1 else 1.0,
390
+ )
360
391
  elif selected_plot == "box":
361
392
  if not group_columns:
362
393
  long_df = plot_df[y_columns].melt(
File without changes
File without changes
File without changes
File without changes