fg-data-profiling 4.19.0__py2.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.
- data_profiling/__init__.py +34 -0
- data_profiling/compare_reports.py +359 -0
- data_profiling/config.py +496 -0
- data_profiling/config_default.yaml +223 -0
- data_profiling/config_minimal.yaml +222 -0
- data_profiling/controller/__init__.py +1 -0
- data_profiling/controller/console.py +125 -0
- data_profiling/controller/pandas_decorator.py +21 -0
- data_profiling/expectations_report.py +117 -0
- data_profiling/model/__init__.py +4 -0
- data_profiling/model/alerts.py +780 -0
- data_profiling/model/correlations.py +163 -0
- data_profiling/model/dataframe.py +35 -0
- data_profiling/model/describe.py +210 -0
- data_profiling/model/description.py +108 -0
- data_profiling/model/duplicates.py +14 -0
- data_profiling/model/expectation_algorithms.py +112 -0
- data_profiling/model/handler.py +81 -0
- data_profiling/model/missing.py +146 -0
- data_profiling/model/pairwise.py +33 -0
- data_profiling/model/pandas/__init__.py +55 -0
- data_profiling/model/pandas/correlations_pandas.py +207 -0
- data_profiling/model/pandas/dataframe_pandas.py +26 -0
- data_profiling/model/pandas/describe_boolean_pandas.py +43 -0
- data_profiling/model/pandas/describe_categorical_pandas.py +274 -0
- data_profiling/model/pandas/describe_counts_pandas.py +63 -0
- data_profiling/model/pandas/describe_date_pandas.py +77 -0
- data_profiling/model/pandas/describe_file_pandas.py +56 -0
- data_profiling/model/pandas/describe_generic_pandas.py +36 -0
- data_profiling/model/pandas/describe_image_pandas.py +255 -0
- data_profiling/model/pandas/describe_numeric_pandas.py +175 -0
- data_profiling/model/pandas/describe_path_pandas.py +63 -0
- data_profiling/model/pandas/describe_supported_pandas.py +41 -0
- data_profiling/model/pandas/describe_text_pandas.py +62 -0
- data_profiling/model/pandas/describe_timeseries_pandas.py +222 -0
- data_profiling/model/pandas/describe_url_pandas.py +57 -0
- data_profiling/model/pandas/discretize_pandas.py +81 -0
- data_profiling/model/pandas/duplicates_pandas.py +56 -0
- data_profiling/model/pandas/imbalance_pandas.py +35 -0
- data_profiling/model/pandas/missing_pandas.py +42 -0
- data_profiling/model/pandas/sample_pandas.py +38 -0
- data_profiling/model/pandas/summary_pandas.py +101 -0
- data_profiling/model/pandas/table_pandas.py +56 -0
- data_profiling/model/pandas/timeseries_index_pandas.py +33 -0
- data_profiling/model/pandas/utils_pandas.py +27 -0
- data_profiling/model/sample.py +37 -0
- data_profiling/model/spark/__init__.py +48 -0
- data_profiling/model/spark/correlations_spark.py +152 -0
- data_profiling/model/spark/dataframe_spark.py +34 -0
- data_profiling/model/spark/describe_boolean_spark.py +27 -0
- data_profiling/model/spark/describe_categorical_spark.py +28 -0
- data_profiling/model/spark/describe_counts_spark.py +105 -0
- data_profiling/model/spark/describe_date_spark.py +51 -0
- data_profiling/model/spark/describe_generic_spark.py +30 -0
- data_profiling/model/spark/describe_numeric_spark.py +155 -0
- data_profiling/model/spark/describe_supported_spark.py +33 -0
- data_profiling/model/spark/describe_text_spark.py +25 -0
- data_profiling/model/spark/duplicates_spark.py +54 -0
- data_profiling/model/spark/missing_spark.py +96 -0
- data_profiling/model/spark/sample_spark.py +43 -0
- data_profiling/model/spark/summary_spark.py +95 -0
- data_profiling/model/spark/table_spark.py +58 -0
- data_profiling/model/spark/timeseries_index_spark.py +12 -0
- data_profiling/model/summarizer.py +207 -0
- data_profiling/model/summary.py +66 -0
- data_profiling/model/summary_algorithms.py +276 -0
- data_profiling/model/table.py +10 -0
- data_profiling/model/timeseries_index.py +16 -0
- data_profiling/model/typeset.py +365 -0
- data_profiling/model/typeset_relations.py +143 -0
- data_profiling/profile_report.py +573 -0
- data_profiling/report/__init__.py +4 -0
- data_profiling/report/formatters.py +346 -0
- data_profiling/report/presentation/__init__.py +1 -0
- data_profiling/report/presentation/core/__init__.py +39 -0
- data_profiling/report/presentation/core/alerts.py +18 -0
- data_profiling/report/presentation/core/collapse.py +24 -0
- data_profiling/report/presentation/core/container.py +50 -0
- data_profiling/report/presentation/core/correlation_table.py +21 -0
- data_profiling/report/presentation/core/dropdown.py +44 -0
- data_profiling/report/presentation/core/duplicate.py +16 -0
- data_profiling/report/presentation/core/frequency_table.py +14 -0
- data_profiling/report/presentation/core/frequency_table_small.py +16 -0
- data_profiling/report/presentation/core/html.py +14 -0
- data_profiling/report/presentation/core/image.py +34 -0
- data_profiling/report/presentation/core/item_renderer.py +17 -0
- data_profiling/report/presentation/core/renderable.py +42 -0
- data_profiling/report/presentation/core/root.py +35 -0
- data_profiling/report/presentation/core/sample.py +20 -0
- data_profiling/report/presentation/core/scores.py +32 -0
- data_profiling/report/presentation/core/table.py +26 -0
- data_profiling/report/presentation/core/toggle_button.py +14 -0
- data_profiling/report/presentation/core/variable.py +40 -0
- data_profiling/report/presentation/core/variable_info.py +36 -0
- data_profiling/report/presentation/flavours/__init__.py +9 -0
- data_profiling/report/presentation/flavours/flavour_html.py +64 -0
- data_profiling/report/presentation/flavours/flavour_widget.py +61 -0
- data_profiling/report/presentation/flavours/flavours.py +43 -0
- data_profiling/report/presentation/flavours/html/__init__.py +47 -0
- data_profiling/report/presentation/flavours/html/alerts.py +10 -0
- data_profiling/report/presentation/flavours/html/collapse.py +7 -0
- data_profiling/report/presentation/flavours/html/container.py +58 -0
- data_profiling/report/presentation/flavours/html/correlation_table.py +13 -0
- data_profiling/report/presentation/flavours/html/dropdown.py +7 -0
- data_profiling/report/presentation/flavours/html/duplicate.py +24 -0
- data_profiling/report/presentation/flavours/html/frequency_table.py +20 -0
- data_profiling/report/presentation/flavours/html/frequency_table_small.py +15 -0
- data_profiling/report/presentation/flavours/html/html.py +6 -0
- data_profiling/report/presentation/flavours/html/image.py +7 -0
- data_profiling/report/presentation/flavours/html/root.py +14 -0
- data_profiling/report/presentation/flavours/html/sample.py +12 -0
- data_profiling/report/presentation/flavours/html/scores.py +11 -0
- data_profiling/report/presentation/flavours/html/table.py +7 -0
- data_profiling/report/presentation/flavours/html/templates/alerts/alert_constant.html +1 -0
- data_profiling/report/presentation/flavours/html/templates/alerts/alert_constant_length.html +1 -0
- data_profiling/report/presentation/flavours/html/templates/alerts/alert_dirty_category.html +1 -0
- data_profiling/report/presentation/flavours/html/templates/alerts/alert_duplicates.html +1 -0
- data_profiling/report/presentation/flavours/html/templates/alerts/alert_empty.html +1 -0
- data_profiling/report/presentation/flavours/html/templates/alerts/alert_high_cardinality.html +1 -0
- data_profiling/report/presentation/flavours/html/templates/alerts/alert_high_correlation.html +4 -0
- data_profiling/report/presentation/flavours/html/templates/alerts/alert_imbalance.html +1 -0
- data_profiling/report/presentation/flavours/html/templates/alerts/alert_infinite.html +1 -0
- data_profiling/report/presentation/flavours/html/templates/alerts/alert_missing.html +1 -0
- data_profiling/report/presentation/flavours/html/templates/alerts/alert_near_duplicates.html +1 -0
- data_profiling/report/presentation/flavours/html/templates/alerts/alert_non_stationary.html +1 -0
- data_profiling/report/presentation/flavours/html/templates/alerts/alert_seasonal.html +1 -0
- data_profiling/report/presentation/flavours/html/templates/alerts/alert_skewed.html +1 -0
- data_profiling/report/presentation/flavours/html/templates/alerts/alert_truncated.html +1 -0
- data_profiling/report/presentation/flavours/html/templates/alerts/alert_type_date.html +1 -0
- data_profiling/report/presentation/flavours/html/templates/alerts/alert_uniform.html +1 -0
- data_profiling/report/presentation/flavours/html/templates/alerts/alert_unique.html +1 -0
- data_profiling/report/presentation/flavours/html/templates/alerts/alert_unsupported.html +1 -0
- data_profiling/report/presentation/flavours/html/templates/alerts/alert_zeros.html +1 -0
- data_profiling/report/presentation/flavours/html/templates/alerts.html +47 -0
- data_profiling/report/presentation/flavours/html/templates/collapse.html +11 -0
- data_profiling/report/presentation/flavours/html/templates/correlation_table.html +5 -0
- data_profiling/report/presentation/flavours/html/templates/diagram.html +11 -0
- data_profiling/report/presentation/flavours/html/templates/dropdown.html +16 -0
- data_profiling/report/presentation/flavours/html/templates/duplicate.html +5 -0
- data_profiling/report/presentation/flavours/html/templates/frequency_table.html +45 -0
- data_profiling/report/presentation/flavours/html/templates/frequency_table_small.html +34 -0
- data_profiling/report/presentation/flavours/html/templates/report.html +26 -0
- data_profiling/report/presentation/flavours/html/templates/sample.html +10 -0
- data_profiling/report/presentation/flavours/html/templates/scores.html +78 -0
- data_profiling/report/presentation/flavours/html/templates/sequence/batch_grid.html +16 -0
- data_profiling/report/presentation/flavours/html/templates/sequence/grid.html +18 -0
- data_profiling/report/presentation/flavours/html/templates/sequence/list.html +7 -0
- data_profiling/report/presentation/flavours/html/templates/sequence/named_list.html +8 -0
- data_profiling/report/presentation/flavours/html/templates/sequence/overview_tabs.html +30 -0
- data_profiling/report/presentation/flavours/html/templates/sequence/scores.html +3 -0
- data_profiling/report/presentation/flavours/html/templates/sequence/sections.html +13 -0
- data_profiling/report/presentation/flavours/html/templates/sequence/select.html +40 -0
- data_profiling/report/presentation/flavours/html/templates/sequence/tabs.html +30 -0
- data_profiling/report/presentation/flavours/html/templates/table.html +38 -0
- data_profiling/report/presentation/flavours/html/templates/toggle_button.html +18 -0
- data_profiling/report/presentation/flavours/html/templates/variable.html +7 -0
- data_profiling/report/presentation/flavours/html/templates/variable_info.html +49 -0
- data_profiling/report/presentation/flavours/html/templates/wrapper/assets/bootstrap.bundle.min.js +7 -0
- data_profiling/report/presentation/flavours/html/templates/wrapper/assets/bootstrap.min.css +6 -0
- data_profiling/report/presentation/flavours/html/templates/wrapper/assets/cosmo.bootstrap.min.css +12 -0
- data_profiling/report/presentation/flavours/html/templates/wrapper/assets/flatly.bootstrap.min.css +12 -0
- data_profiling/report/presentation/flavours/html/templates/wrapper/assets/script.js +52 -0
- data_profiling/report/presentation/flavours/html/templates/wrapper/assets/simplex.bootstrap.min.css +12 -0
- data_profiling/report/presentation/flavours/html/templates/wrapper/assets/style.css +253 -0
- data_profiling/report/presentation/flavours/html/templates/wrapper/assets/united.bootstrap.min.css +12 -0
- data_profiling/report/presentation/flavours/html/templates/wrapper/footer.html +7 -0
- data_profiling/report/presentation/flavours/html/templates/wrapper/javascript.html +18 -0
- data_profiling/report/presentation/flavours/html/templates/wrapper/navigation.html +36 -0
- data_profiling/report/presentation/flavours/html/templates/wrapper/style.html +53 -0
- data_profiling/report/presentation/flavours/html/templates.py +76 -0
- data_profiling/report/presentation/flavours/html/toggle_button.py +7 -0
- data_profiling/report/presentation/flavours/html/variable.py +7 -0
- data_profiling/report/presentation/flavours/html/variable_info.py +7 -0
- data_profiling/report/presentation/flavours/widget/__init__.py +49 -0
- data_profiling/report/presentation/flavours/widget/alerts.py +45 -0
- data_profiling/report/presentation/flavours/widget/collapse.py +43 -0
- data_profiling/report/presentation/flavours/widget/container.py +121 -0
- data_profiling/report/presentation/flavours/widget/correlation_table.py +14 -0
- data_profiling/report/presentation/flavours/widget/dropdown.py +31 -0
- data_profiling/report/presentation/flavours/widget/duplicate.py +14 -0
- data_profiling/report/presentation/flavours/widget/frequency_table.py +57 -0
- data_profiling/report/presentation/flavours/widget/frequency_table_small.py +66 -0
- data_profiling/report/presentation/flavours/widget/html.py +11 -0
- data_profiling/report/presentation/flavours/widget/image.py +26 -0
- data_profiling/report/presentation/flavours/widget/notebook.py +81 -0
- data_profiling/report/presentation/flavours/widget/root.py +10 -0
- data_profiling/report/presentation/flavours/widget/sample.py +14 -0
- data_profiling/report/presentation/flavours/widget/table.py +30 -0
- data_profiling/report/presentation/flavours/widget/toggle_button.py +17 -0
- data_profiling/report/presentation/flavours/widget/variable.py +12 -0
- data_profiling/report/presentation/flavours/widget/variable_info.py +11 -0
- data_profiling/report/presentation/frequency_table_utils.py +141 -0
- data_profiling/report/structure/__init__.py +1 -0
- data_profiling/report/structure/correlations.py +123 -0
- data_profiling/report/structure/overview.py +376 -0
- data_profiling/report/structure/report.py +457 -0
- data_profiling/report/structure/variables/__init__.py +35 -0
- data_profiling/report/structure/variables/render_boolean.py +132 -0
- data_profiling/report/structure/variables/render_categorical.py +566 -0
- data_profiling/report/structure/variables/render_common.py +31 -0
- data_profiling/report/structure/variables/render_complex.py +102 -0
- data_profiling/report/structure/variables/render_count.py +172 -0
- data_profiling/report/structure/variables/render_date.py +143 -0
- data_profiling/report/structure/variables/render_file.py +70 -0
- data_profiling/report/structure/variables/render_generic.py +45 -0
- data_profiling/report/structure/variables/render_image.py +204 -0
- data_profiling/report/structure/variables/render_path.py +134 -0
- data_profiling/report/structure/variables/render_real.py +314 -0
- data_profiling/report/structure/variables/render_text.py +189 -0
- data_profiling/report/structure/variables/render_timeseries.py +371 -0
- data_profiling/report/structure/variables/render_url.py +132 -0
- data_profiling/report/utils.py +34 -0
- data_profiling/serialize_report.py +143 -0
- data_profiling/utils/__init__.py +1 -0
- data_profiling/utils/backend.py +9 -0
- data_profiling/utils/cache.py +59 -0
- data_profiling/utils/common.py +142 -0
- data_profiling/utils/compat.py +31 -0
- data_profiling/utils/dataframe.py +238 -0
- data_profiling/utils/logger.py +53 -0
- data_profiling/utils/notebook.py +8 -0
- data_profiling/utils/paths.py +45 -0
- data_profiling/utils/progress_bar.py +15 -0
- data_profiling/utils/styles.py +22 -0
- data_profiling/utils/versions.py +19 -0
- data_profiling/version.py +1 -0
- data_profiling/visualisation/__init__.py +1 -0
- data_profiling/visualisation/context.py +87 -0
- data_profiling/visualisation/missing.py +138 -0
- data_profiling/visualisation/plot.py +1158 -0
- data_profiling/visualisation/utils.py +113 -0
- fg_data_profiling-4.19.0.dist-info/METADATA +362 -0
- fg_data_profiling-4.19.0.dist-info/RECORD +238 -0
- fg_data_profiling-4.19.0.dist-info/WHEEL +6 -0
- fg_data_profiling-4.19.0.dist-info/entry_points.txt +3 -0
- fg_data_profiling-4.19.0.dist-info/licenses/LICENSE +21 -0
- fg_data_profiling-4.19.0.dist-info/top_level.txt +2 -0
- ydata_profiling/__init__.py +43 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Auxiliary handler methods for data summary extraction
|
|
3
|
+
"""
|
|
4
|
+
from typing import Any, Callable, Dict, List, Sequence
|
|
5
|
+
|
|
6
|
+
import networkx as nx
|
|
7
|
+
from visions import VisionsTypeset
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def compose(functions: Sequence[Callable]) -> Callable:
|
|
11
|
+
"""
|
|
12
|
+
Compose a sequence of functions.
|
|
13
|
+
|
|
14
|
+
:param functions: sequence of functions
|
|
15
|
+
:return: combined function applying all functions in order.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def composed_function(*args) -> List[Any]:
|
|
19
|
+
result = args # Start with the input arguments
|
|
20
|
+
for func in functions:
|
|
21
|
+
result = func(*result) if isinstance(result, tuple) else func(result)
|
|
22
|
+
return result # type: ignore
|
|
23
|
+
|
|
24
|
+
return composed_function # type: ignore
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Handler:
|
|
28
|
+
"""A generic handler
|
|
29
|
+
|
|
30
|
+
Allows any custom mapping between data types and functions
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
mapping: Dict[str, List[Callable]],
|
|
36
|
+
typeset: VisionsTypeset,
|
|
37
|
+
*args,
|
|
38
|
+
**kwargs
|
|
39
|
+
):
|
|
40
|
+
self.mapping = mapping
|
|
41
|
+
self.typeset = typeset
|
|
42
|
+
self._complete_dag()
|
|
43
|
+
|
|
44
|
+
def _complete_dag(self) -> None:
|
|
45
|
+
for from_type, to_type in nx.topological_sort(
|
|
46
|
+
nx.line_graph(self.typeset.base_graph)
|
|
47
|
+
):
|
|
48
|
+
self.mapping[str(to_type)] = (
|
|
49
|
+
self.mapping[str(from_type)] + self.mapping[str(to_type)]
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
def handle(self, dtype: str, *args, **kwargs) -> dict:
|
|
53
|
+
"""
|
|
54
|
+
Returns:
|
|
55
|
+
object: a tuple containing the config, the dataset series and the summary extracted
|
|
56
|
+
"""
|
|
57
|
+
funcs = self.mapping.get(dtype, [])
|
|
58
|
+
op = compose(funcs)
|
|
59
|
+
summary = op(*args)[-1]
|
|
60
|
+
return summary
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def get_render_map() -> Dict[str, Callable]:
|
|
64
|
+
import data_profiling.report.structure.variables as render_algorithms
|
|
65
|
+
|
|
66
|
+
render_map = {
|
|
67
|
+
"Boolean": render_algorithms.render_boolean,
|
|
68
|
+
"Numeric": render_algorithms.render_real,
|
|
69
|
+
"Complex": render_algorithms.render_complex,
|
|
70
|
+
"Text": render_algorithms.render_text,
|
|
71
|
+
"DateTime": render_algorithms.render_date,
|
|
72
|
+
"Categorical": render_algorithms.render_categorical,
|
|
73
|
+
"URL": render_algorithms.render_url,
|
|
74
|
+
"Path": render_algorithms.render_path,
|
|
75
|
+
"File": render_algorithms.render_file,
|
|
76
|
+
"Image": render_algorithms.render_image,
|
|
77
|
+
"Unsupported": render_algorithms.render_generic,
|
|
78
|
+
"TimeSeries": render_algorithms.render_timeseries,
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return render_map
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import warnings
|
|
3
|
+
from typing import Any, Callable, Dict, Optional, Sized
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
from data_profiling.config import Settings
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class MissingDataBackend:
|
|
11
|
+
"""Helper class to select and cache the appropriate missing-data backend (Pandas or Spark)."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, df: Sized):
|
|
14
|
+
"""Determine backend once and store it for all missing-data computations."""
|
|
15
|
+
if isinstance(df, pd.DataFrame):
|
|
16
|
+
self.backend_module = "data_profiling.model.pandas.missing_pandas"
|
|
17
|
+
else:
|
|
18
|
+
self.backend_module = "data_profiling.model.spark.missing_spark"
|
|
19
|
+
|
|
20
|
+
self.module = importlib.import_module(self.backend_module)
|
|
21
|
+
|
|
22
|
+
def get_method(self, method_name: str) -> Callable:
|
|
23
|
+
"""Retrieve the appropriate missing-data function from the backend module."""
|
|
24
|
+
try:
|
|
25
|
+
return getattr(self.module, method_name)
|
|
26
|
+
except AttributeError as ex:
|
|
27
|
+
raise AttributeError(
|
|
28
|
+
f"Missing-data function '{method_name}' is not available in {self.backend_module}."
|
|
29
|
+
) from ex
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class MissingData:
|
|
33
|
+
_method_name: str = ""
|
|
34
|
+
|
|
35
|
+
def compute(
|
|
36
|
+
self, config: Settings, df: Sized, backend: MissingDataBackend
|
|
37
|
+
) -> Optional[Sized]:
|
|
38
|
+
"""Computes correlation using the correct backend (Pandas or Spark)."""
|
|
39
|
+
try:
|
|
40
|
+
method = backend.get_method(self._method_name)
|
|
41
|
+
except AttributeError as ex:
|
|
42
|
+
raise NotImplementedError() from ex
|
|
43
|
+
else:
|
|
44
|
+
return method(config, df)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class MissingBar(MissingData):
|
|
48
|
+
_method_name = "missing_bar"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class MissingMatrix(MissingData):
|
|
52
|
+
_method_name = "missing_matrix"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class MissingHeatmap(MissingData):
|
|
56
|
+
_method_name = "missing_heatmap"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def get_missing_active(config: Settings, table_stats: dict) -> Dict[str, Any]:
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
config: report Settings object
|
|
64
|
+
table_stats: The overall statistics for the DataFrame.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
missing_map = {
|
|
71
|
+
"bar": {
|
|
72
|
+
"min_missing": 0,
|
|
73
|
+
"name": "Count",
|
|
74
|
+
"caption": "A simple visualization of nullity by column.",
|
|
75
|
+
"function": MissingBar(),
|
|
76
|
+
},
|
|
77
|
+
"matrix": {
|
|
78
|
+
"min_missing": 0,
|
|
79
|
+
"name": "Matrix",
|
|
80
|
+
"caption": "Nullity matrix is a data-dense display which lets you quickly visually pick out patterns in data completion.",
|
|
81
|
+
"function": MissingMatrix(),
|
|
82
|
+
},
|
|
83
|
+
"heatmap": {
|
|
84
|
+
"min_missing": 2,
|
|
85
|
+
"name": "Heatmap",
|
|
86
|
+
"caption": "The correlation heatmap measures nullity correlation: how strongly the presence or absence of one variable affects the presence of another.",
|
|
87
|
+
"function": MissingHeatmap(),
|
|
88
|
+
},
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
missing_map = {
|
|
92
|
+
name: settings
|
|
93
|
+
for name, settings in missing_map.items()
|
|
94
|
+
if (
|
|
95
|
+
config.missing_diagrams[name]
|
|
96
|
+
and table_stats["n_vars_with_missing"] >= settings["min_missing"]
|
|
97
|
+
)
|
|
98
|
+
and (
|
|
99
|
+
name != "heatmap"
|
|
100
|
+
or (
|
|
101
|
+
table_stats["n_vars_with_missing"] - table_stats["n_vars_all_missing"]
|
|
102
|
+
>= settings["min_missing"]
|
|
103
|
+
)
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return missing_map
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def get_missing_diagram(
|
|
111
|
+
config: Settings, df: pd.DataFrame, settings: Dict[str, Any]
|
|
112
|
+
) -> Optional[Dict[str, Any]]:
|
|
113
|
+
"""Gets the rendered diagrams for missing values.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
config: report Settings object
|
|
117
|
+
df: The DataFrame on which to calculate the missing values.
|
|
118
|
+
settings: missing diagram name, caption and function
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
A dictionary containing the base64 encoded plots for each diagram that is active in the config (matrix, bar, heatmap).
|
|
122
|
+
"""
|
|
123
|
+
backend = MissingDataBackend(df)
|
|
124
|
+
|
|
125
|
+
missing_func = settings.get("function")
|
|
126
|
+
if missing_func is None:
|
|
127
|
+
return None # No function defined, skip execution
|
|
128
|
+
|
|
129
|
+
try:
|
|
130
|
+
result = missing_func.compute(config, df, backend)
|
|
131
|
+
except ValueError as e:
|
|
132
|
+
warnings.warn(
|
|
133
|
+
f"""There was an attempt to generate the {settings['name']} missing values diagrams, but this failed.
|
|
134
|
+
To hide this warning, disable the calculation
|
|
135
|
+
(using `df.profile_report(missing_diagrams={{"{settings['name']}": False}}`)
|
|
136
|
+
If this is problematic for your use case, please report this as an issue:
|
|
137
|
+
https://github.com/Data-Centric-AI-Community/data-profiling/issues
|
|
138
|
+
(include the error message: '{e}')"""
|
|
139
|
+
)
|
|
140
|
+
return None
|
|
141
|
+
else:
|
|
142
|
+
return {
|
|
143
|
+
"name": settings["name"],
|
|
144
|
+
"caption": settings["caption"],
|
|
145
|
+
"matrix": result,
|
|
146
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from typing import Any, List, Tuple
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
|
|
5
|
+
from data_profiling.config import Settings
|
|
6
|
+
from data_profiling.visualisation.plot import scatter_pairwise
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_scatter_tasks(
|
|
10
|
+
config: Settings, continuous_variables: list
|
|
11
|
+
) -> List[Tuple[Any, Any]]:
|
|
12
|
+
if not config.interactions.continuous:
|
|
13
|
+
return []
|
|
14
|
+
|
|
15
|
+
targets = config.interactions.targets
|
|
16
|
+
if len(targets) == 0:
|
|
17
|
+
targets = continuous_variables
|
|
18
|
+
|
|
19
|
+
tasks = [(x, y) for y in continuous_variables for x in targets]
|
|
20
|
+
return tasks
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_scatter_plot(
|
|
24
|
+
config: Settings, df: pd.DataFrame, x: Any, y: Any, continuous_variables: list
|
|
25
|
+
) -> str:
|
|
26
|
+
if x in continuous_variables:
|
|
27
|
+
if y == x:
|
|
28
|
+
df_temp = df[[x]].dropna()
|
|
29
|
+
else:
|
|
30
|
+
df_temp = df[[x, y]].dropna()
|
|
31
|
+
return scatter_pairwise(config, df_temp[x], df_temp[y], x, y)
|
|
32
|
+
else:
|
|
33
|
+
return ""
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
|
|
3
|
+
# List of modules in the 'pandas' model that should be imported explicitly
|
|
4
|
+
PANDAS_MODULES = [
|
|
5
|
+
"correlations_pandas",
|
|
6
|
+
"describe_generic_pandas",
|
|
7
|
+
"describe_boolean_pandas",
|
|
8
|
+
"describe_categorical_pandas",
|
|
9
|
+
"describe_url_pandas",
|
|
10
|
+
"describe_file_pandas",
|
|
11
|
+
"describe_text_pandas",
|
|
12
|
+
"describe_timeseries_pandas",
|
|
13
|
+
"describe_numeric_pandas",
|
|
14
|
+
"describe_path_pandas",
|
|
15
|
+
"describe_image_pandas",
|
|
16
|
+
"describe_date_pandas",
|
|
17
|
+
"describe_counts_pandas",
|
|
18
|
+
"duplicates_pandas",
|
|
19
|
+
"sample_pandas",
|
|
20
|
+
"table_pandas",
|
|
21
|
+
"timeseries_index_pandas",
|
|
22
|
+
"summary_pandas",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
# Dynamically import and expose functions from modules
|
|
26
|
+
for module_name in PANDAS_MODULES:
|
|
27
|
+
module = importlib.import_module(f"data_profiling.model.pandas.{module_name}")
|
|
28
|
+
globals().update(
|
|
29
|
+
{
|
|
30
|
+
name: getattr(module, name)
|
|
31
|
+
for name in dir(module)
|
|
32
|
+
if not name.startswith("_")
|
|
33
|
+
}
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
# Explicitly list exposed names for clarity
|
|
37
|
+
__all__ = [
|
|
38
|
+
"pandas_describe_generic",
|
|
39
|
+
"pandas_describe_boolean_1d",
|
|
40
|
+
"pandas_describe_categorical_1d",
|
|
41
|
+
"pandas_describe_url_1d",
|
|
42
|
+
"pandas_describe_file_1d",
|
|
43
|
+
"pandas_describe_text_1d",
|
|
44
|
+
"pandas_describe_timeseries_1d",
|
|
45
|
+
"pandas_describe_numeric_1d",
|
|
46
|
+
"pandas_describe_path_1d",
|
|
47
|
+
"pandas_describe_image_1d",
|
|
48
|
+
"pandas_describe_date_1d",
|
|
49
|
+
"pandas_describe_counts",
|
|
50
|
+
"pandas_get_duplicates",
|
|
51
|
+
"pandas_get_sample",
|
|
52
|
+
"pandas_get_table_stats",
|
|
53
|
+
"pandas_get_time_index_description",
|
|
54
|
+
"pandas_get_series_descriptions",
|
|
55
|
+
]
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"""Correlations between variables."""
|
|
2
|
+
import itertools
|
|
3
|
+
import warnings
|
|
4
|
+
from typing import Callable, Optional
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pandas as pd
|
|
8
|
+
from scipy import stats
|
|
9
|
+
|
|
10
|
+
from data_profiling.config import Settings
|
|
11
|
+
from data_profiling.model.pandas.discretize_pandas import (
|
|
12
|
+
DiscretizationType,
|
|
13
|
+
Discretizer,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def spearman_compute(
|
|
18
|
+
config: Settings, df: pd.DataFrame, summary: dict
|
|
19
|
+
) -> Optional[pd.DataFrame]:
|
|
20
|
+
df_aux = df.select_dtypes(include="number").copy()
|
|
21
|
+
return df_aux.corr(method="spearman")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def pearson_compute(
|
|
25
|
+
config: Settings, df: pd.DataFrame, summary: dict
|
|
26
|
+
) -> Optional[pd.DataFrame]:
|
|
27
|
+
df_aux = df.select_dtypes(include="number").copy()
|
|
28
|
+
return df_aux.corr(method="pearson")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def kendall_compute(
|
|
32
|
+
config: Settings, df: pd.DataFrame, summary: dict
|
|
33
|
+
) -> Optional[pd.DataFrame]:
|
|
34
|
+
df_aux = df.select_dtypes(include="number").copy()
|
|
35
|
+
return df_aux.corr(method="kendall")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _cramers_corrected_stat(confusion_matrix: pd.DataFrame, correction: bool) -> float:
|
|
39
|
+
"""Calculate the Cramer's V corrected stat for two variables.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
confusion_matrix: Crosstab between two variables.
|
|
43
|
+
correction: Should the correction be applied?
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
The Cramer's V corrected stat for the two variables.
|
|
47
|
+
"""
|
|
48
|
+
# handles empty crosstab
|
|
49
|
+
if confusion_matrix.empty:
|
|
50
|
+
return 0
|
|
51
|
+
|
|
52
|
+
chi2 = stats.chi2_contingency(confusion_matrix, correction=correction)[0]
|
|
53
|
+
n = confusion_matrix.sum().sum()
|
|
54
|
+
phi2 = chi2 / n
|
|
55
|
+
r = confusion_matrix.shape[0]
|
|
56
|
+
k = confusion_matrix.shape[1] if len(confusion_matrix.shape) > 1 else 1
|
|
57
|
+
|
|
58
|
+
# Deal with NaNs later on
|
|
59
|
+
with np.errstate(divide="ignore", invalid="ignore"):
|
|
60
|
+
phi2corr = max(0.0, phi2 - ((k - 1.0) * (r - 1.0)) / (n - 1.0))
|
|
61
|
+
rcorr = r - ((r - 1.0) ** 2.0) / (n - 1.0)
|
|
62
|
+
kcorr = k - ((k - 1.0) ** 2.0) / (n - 1.0)
|
|
63
|
+
rkcorr = min((kcorr - 1.0), (rcorr - 1.0))
|
|
64
|
+
if rkcorr == 0.0:
|
|
65
|
+
corr = 1.0
|
|
66
|
+
else:
|
|
67
|
+
corr = np.sqrt(phi2corr / rkcorr)
|
|
68
|
+
return corr
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _pairwise_spearman(col_1: pd.Series, col_2: pd.Series) -> float:
|
|
72
|
+
return col_1.corr(col_2, method="spearman")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _pairwise_cramers(col_1: pd.Series, col_2: pd.Series) -> float:
|
|
76
|
+
return _cramers_corrected_stat(pd.crosstab(col_1, col_2), correction=True)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def cramers_compute(
|
|
80
|
+
config: Settings, df: pd.DataFrame, summary: dict
|
|
81
|
+
) -> Optional[pd.DataFrame]:
|
|
82
|
+
threshold = config.categorical_maximum_correlation_distinct
|
|
83
|
+
|
|
84
|
+
# `index` and `columns` must not be a set since Pandas 1.5,
|
|
85
|
+
# so convert it to a list. The order of the list is arbitrary.
|
|
86
|
+
categoricals = list(
|
|
87
|
+
{
|
|
88
|
+
key
|
|
89
|
+
for key, value in summary.items()
|
|
90
|
+
if value["type"] in {"Categorical", "Boolean"}
|
|
91
|
+
and 1 < value["n_distinct"] <= threshold
|
|
92
|
+
}
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
if len(categoricals) <= 1:
|
|
96
|
+
return None
|
|
97
|
+
|
|
98
|
+
categoricals = sorted(categoricals)
|
|
99
|
+
matrix = np.zeros((len(categoricals), len(categoricals)))
|
|
100
|
+
np.fill_diagonal(matrix, 1.0)
|
|
101
|
+
correlation_matrix = pd.DataFrame(
|
|
102
|
+
matrix,
|
|
103
|
+
index=categoricals,
|
|
104
|
+
columns=categoricals,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
for name1, name2 in itertools.combinations(categoricals, 2):
|
|
108
|
+
confusion_matrix = pd.crosstab(df[name1], df[name2])
|
|
109
|
+
if confusion_matrix.empty:
|
|
110
|
+
correlation_matrix.loc[name2, name1] = np.nan
|
|
111
|
+
else:
|
|
112
|
+
correlation_matrix.loc[name2, name1] = _cramers_corrected_stat(
|
|
113
|
+
confusion_matrix, correction=True
|
|
114
|
+
)
|
|
115
|
+
correlation_matrix.loc[name1, name2] = correlation_matrix.loc[name2, name1]
|
|
116
|
+
return correlation_matrix
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def phik_compute(
|
|
120
|
+
config: Settings, df: pd.DataFrame, summary: dict
|
|
121
|
+
) -> Optional[pd.DataFrame]:
|
|
122
|
+
df_cols_dict = {i: list(df.columns).index(i) for i in df.columns}
|
|
123
|
+
|
|
124
|
+
intcols = {
|
|
125
|
+
key
|
|
126
|
+
for key, value in summary.items()
|
|
127
|
+
# DateTime currently excluded
|
|
128
|
+
# In some use cases, it makes sense to convert it to interval
|
|
129
|
+
# See https://github.com/KaveIO/PhiK/issues/7
|
|
130
|
+
if value["type"] == "Numeric" and 1 < value["n_distinct"]
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
selcols = {
|
|
134
|
+
key
|
|
135
|
+
for key, value in summary.items()
|
|
136
|
+
if value["type"] != "Unsupported"
|
|
137
|
+
and 1 < value["n_distinct"] <= config.categorical_maximum_correlation_distinct
|
|
138
|
+
}
|
|
139
|
+
selcols = selcols.union(intcols)
|
|
140
|
+
selected_cols = sorted(selcols, key=lambda i: df_cols_dict[i])
|
|
141
|
+
|
|
142
|
+
if len(selected_cols) <= 1:
|
|
143
|
+
return None
|
|
144
|
+
|
|
145
|
+
with warnings.catch_warnings():
|
|
146
|
+
warnings.simplefilter("ignore")
|
|
147
|
+
from phik import phik_matrix
|
|
148
|
+
|
|
149
|
+
correlation = phik_matrix(df[selected_cols], interval_cols=list(intcols))
|
|
150
|
+
|
|
151
|
+
return correlation
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def auto_compute(
|
|
155
|
+
config: Settings, df: pd.DataFrame, summary: dict
|
|
156
|
+
) -> Optional[pd.DataFrame]:
|
|
157
|
+
threshold = config.categorical_maximum_correlation_distinct
|
|
158
|
+
numerical_columns = [
|
|
159
|
+
key
|
|
160
|
+
for key, value in summary.items()
|
|
161
|
+
if value["type"] in {"Numeric", "TimeSeries"} and value["n_distinct"] > 1
|
|
162
|
+
]
|
|
163
|
+
categorical_columns = [
|
|
164
|
+
key
|
|
165
|
+
for key, value in summary.items()
|
|
166
|
+
if value["type"] in {"Categorical", "Boolean"}
|
|
167
|
+
and 1 < value["n_distinct"] <= threshold
|
|
168
|
+
]
|
|
169
|
+
|
|
170
|
+
if len(numerical_columns + categorical_columns) <= 1:
|
|
171
|
+
return None
|
|
172
|
+
|
|
173
|
+
df_discretized = Discretizer(
|
|
174
|
+
DiscretizationType.UNIFORM, n_bins=config.correlations["auto"].n_bins
|
|
175
|
+
).discretize_dataframe(df)
|
|
176
|
+
columns_tested = sorted(numerical_columns + categorical_columns)
|
|
177
|
+
|
|
178
|
+
correlation_matrix = pd.DataFrame(
|
|
179
|
+
np.ones((len(columns_tested), len(columns_tested))),
|
|
180
|
+
index=columns_tested,
|
|
181
|
+
columns=columns_tested,
|
|
182
|
+
)
|
|
183
|
+
for col_1_name, col_2_name in itertools.combinations(columns_tested, 2):
|
|
184
|
+
|
|
185
|
+
method = (
|
|
186
|
+
_pairwise_spearman
|
|
187
|
+
if any(elem in categorical_columns for elem in [col_1_name, col_2_name])
|
|
188
|
+
is False
|
|
189
|
+
else _pairwise_cramers
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
def f(col_name: str, method: Callable) -> pd.Series:
|
|
193
|
+
return (
|
|
194
|
+
df_discretized
|
|
195
|
+
if col_name in numerical_columns and method is _pairwise_cramers
|
|
196
|
+
else df
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
score = method(
|
|
200
|
+
f(col_1_name, method)[col_1_name], f(col_2_name, method)[col_2_name]
|
|
201
|
+
)
|
|
202
|
+
(
|
|
203
|
+
correlation_matrix.loc[col_1_name, col_2_name],
|
|
204
|
+
correlation_matrix.loc[col_2_name, col_1_name],
|
|
205
|
+
) = (score, score)
|
|
206
|
+
|
|
207
|
+
return correlation_matrix
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
|
|
3
|
+
from data_profiling.config import Settings
|
|
4
|
+
from data_profiling.utils.dataframe import rename_index
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def pandas_preprocess(config: Settings, df: pd.DataFrame) -> pd.DataFrame:
|
|
8
|
+
"""Preprocess the dataframe
|
|
9
|
+
|
|
10
|
+
- Appends the index to the dataframe when it contains information
|
|
11
|
+
- Rename the "index" column to "df_index", if exists
|
|
12
|
+
- Convert the DataFrame's columns to str
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
config: report Settings object
|
|
16
|
+
df: the pandas DataFrame
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
The preprocessed DataFrame
|
|
20
|
+
"""
|
|
21
|
+
# Rename reserved column names
|
|
22
|
+
df = rename_index(df)
|
|
23
|
+
|
|
24
|
+
# Ensure that columns are strings
|
|
25
|
+
df.columns = df.columns.map(str)
|
|
26
|
+
return df
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from typing import Tuple
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pandas as pd
|
|
5
|
+
|
|
6
|
+
from data_profiling.config import Settings
|
|
7
|
+
from data_profiling.model.pandas.imbalance_pandas import column_imbalance_score
|
|
8
|
+
from data_profiling.model.summary_algorithms import (
|
|
9
|
+
describe_boolean_1d,
|
|
10
|
+
series_hashable,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@describe_boolean_1d.register
|
|
15
|
+
@series_hashable
|
|
16
|
+
def pandas_describe_boolean_1d(
|
|
17
|
+
config: Settings, series: pd.Series, summary: dict
|
|
18
|
+
) -> Tuple[Settings, pd.Series, dict]:
|
|
19
|
+
"""Describe a boolean series.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
config: report Settings object
|
|
23
|
+
series: The Series to describe.
|
|
24
|
+
summary: The dict containing the series description so far.
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
A dict containing calculated series description values.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
value_counts: pd.Series = summary["value_counts_without_nan"]
|
|
31
|
+
if not value_counts.empty:
|
|
32
|
+
summary.update({"top": value_counts.index[0], "freq": value_counts.iloc[0]})
|
|
33
|
+
summary["imbalance"] = column_imbalance_score(value_counts, len(value_counts))
|
|
34
|
+
else:
|
|
35
|
+
summary.update(
|
|
36
|
+
{
|
|
37
|
+
"top": np.nan,
|
|
38
|
+
"freq": 0,
|
|
39
|
+
"imbalance": 0,
|
|
40
|
+
}
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
return config, series, summary
|