pandas-dstoolkit 0.15.2__py3-none-any.whl

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.
dstoolkit/__init__.py ADDED
@@ -0,0 +1,75 @@
1
+ """dstoolkit: data cleaning and exploratory analysis helpers for pandas.
2
+
3
+ Everything is available from the top level:
4
+
5
+ import dstoolkit as dst
6
+
7
+ df = dst.convert_types(raw) # yes/no text -> True/False, date text -> dates
8
+ df = dst.clean(df, missing={"numeric": "median"})
9
+ dst.show_distribution_profile(df["age"])
10
+ dst.profile(df).save("report.html")
11
+ dst.validate(df, {"age": {"min": 0, "max": 120}})
12
+
13
+ or from its module: `dstoolkit.cleaning`, `dstoolkit.exploration`,
14
+ `dstoolkit.profiling`, `dstoolkit.validation` and `dstoolkit.monitoring`
15
+ (`compare()`: has new data drifted from the training data?). The scikit-learn
16
+ transformers (MissingFiller, OutlierCapper, CategoryEncoder, Scaler, DateFeatures) are in
17
+ `dstoolkit.transformers` and need the optional scikit-learn extra.
18
+ """
19
+
20
+ from . import cleaning, exploration, monitoring, profiling, validation
21
+ from ._messages import get_message_output, set_message_output
22
+ from .cleaning import (
23
+ add_age_column,
24
+ clean,
25
+ clean_column_names,
26
+ convert_types,
27
+ encode_categorical,
28
+ fill_missing,
29
+ find_outliers,
30
+ handle_outliers,
31
+ log_transform,
32
+ normalize,
33
+ remove_duplicates,
34
+ standardize_labels,
35
+ to_boolean,
36
+ to_category,
37
+ to_datetime_columns,
38
+ winsorize,
39
+ )
40
+ from .exploration import (
41
+ add_passed,
42
+ associations,
43
+ drop_nulls,
44
+ fit_line,
45
+ show_counts,
46
+ show_density,
47
+ show_distribution,
48
+ show_distribution_profile,
49
+ show_eda,
50
+ show_eda_table,
51
+ show_grouped_box,
52
+ show_histogram,
53
+ show_scatter,
54
+ )
55
+ from .monitoring import compare
56
+ from .profiling import ColumnProfile, Profile, Suggestion, TargetProfile, profile
57
+ from .validation import ColumnRule, Comparison, RuleResult, ValidationError, ValidationReport, validate
58
+
59
+ __version__ = "0.15.2"
60
+
61
+ # The transformers need scikit-learn (an optional extra), so they're imported on
62
+ # first use: `dst.Scaler` works when scikit-learn is installed, and `import
63
+ # dstoolkit` never needs it.
64
+ _TRANSFORMERS = ("MissingFiller", "OutlierCapper", "CategoryEncoder", "Scaler", "DateFeatures")
65
+
66
+
67
+ def __getattr__(name: str) -> object:
68
+ if name in _TRANSFORMERS:
69
+ from . import transformers
70
+ return getattr(transformers, name)
71
+ raise AttributeError(f"module 'dstoolkit' has no attribute {name!r}")
72
+
73
+ __all__ = ["cleaning", "exploration", "monitoring", "profiling", "validation", "__version__",
74
+ "set_message_output", "get_message_output",
75
+ *cleaning.__all__, *exploration.__all__, *monitoring.__all__, *profiling.__all__, *validation.__all__]
dstoolkit/_messages.py ADDED
@@ -0,0 +1,92 @@
1
+ """Where dstoolkit's one-line summaries go ("Filled missing values: age (263)", ...).
2
+
3
+ Every message is sent to the standard `logging` logger named "dstoolkit" at
4
+ INFO level. What happens next depends on set_message_output():
5
+
6
+ - "print" (default): the logger writes the message to the screen (standard
7
+ output), exactly like print(), so notebooks and scripts show it.
8
+ - "logging": the logger hands messages to your own logging setup instead, so
9
+ they get your timestamps and format and go wherever your logs go (a file,
10
+ AWS CloudWatch from a Lambda function, ...).
11
+ - "off": nothing is shown anywhere.
12
+
13
+ `verbose=False` on any function still silences that one call.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import logging
19
+ import sys
20
+ from typing import Literal
21
+
22
+ MessageOutput = Literal["print", "logging", "off"]
23
+
24
+ logger = logging.getLogger("dstoolkit")
25
+
26
+
27
+ class _StdoutHandler(logging.Handler):
28
+ """Writes the bare message to whatever sys.stdout is at that moment.
29
+
30
+ Looking sys.stdout up on every message (rather than once, like
31
+ logging.StreamHandler) keeps Jupyter, pytest's capsys and
32
+ contextlib.redirect_stdout working.
33
+ """
34
+
35
+ def emit(self, record: logging.LogRecord) -> None:
36
+ try:
37
+ sys.stdout.write(self.format(record) + "\n")
38
+ except Exception: # pragma: no cover - logging must never crash the caller
39
+ self.handleError(record)
40
+
41
+
42
+ _console = _StdoutHandler()
43
+ _output: MessageOutput = "print"
44
+
45
+
46
+ def set_message_output(output: MessageOutput) -> None:
47
+ """Choose where dstoolkit's summaries go: "print" (default), "logging" or "off".
48
+
49
+ "logging" sends them to the "dstoolkit" logger at INFO level and lets your
50
+ logging configuration handle them, for example:
51
+
52
+ import logging
53
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s %(name)s %(message)s")
54
+ dst.set_message_output("logging")
55
+
56
+ The setting applies to the whole Python session.
57
+ """
58
+ global _output
59
+ if output not in ("print", "logging", "off"):
60
+ raise ValueError(f'output must be "print", "logging" or "off", got {output!r}')
61
+ _output = output
62
+ # Remove every copy of the print handler, including one left by an earlier import of
63
+ # this module (importlib.reload or Jupyter's %autoreload), so messages never print twice
64
+ for handler in list(logger.handlers):
65
+ if type(handler).__name__ == "_StdoutHandler":
66
+ logger.removeHandler(handler)
67
+ if output == "print":
68
+ logger.addHandler(_console)
69
+ logger.setLevel(logging.INFO)
70
+ logger.propagate = False # don't also pass it to the app's logging setup
71
+ elif output == "logging":
72
+ logger.setLevel(logging.NOTSET) # the app's logging configuration decides
73
+ logger.propagate = True
74
+ else:
75
+ logger.setLevel(logging.CRITICAL + 1)
76
+ logger.propagate = False
77
+
78
+
79
+ def get_message_output() -> MessageOutput:
80
+ """The current setting of set_message_output()."""
81
+ return _output
82
+
83
+
84
+ def say(verbose: bool, message: str) -> None:
85
+ """Send one summary message, unless this call was made with verbose=False."""
86
+ if verbose:
87
+ logger.info(message)
88
+
89
+
90
+ if not any(isinstance(h, logging.NullHandler) for h in logger.handlers):
91
+ logger.addHandler(logging.NullHandler()) # standard library practice for packages
92
+ set_message_output("print")