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,365 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import os
|
|
3
|
+
import warnings
|
|
4
|
+
from functools import partial, wraps
|
|
5
|
+
from typing import Callable, Sequence, Set
|
|
6
|
+
from urllib.parse import urlparse
|
|
7
|
+
|
|
8
|
+
import filetype
|
|
9
|
+
import pandas as pd
|
|
10
|
+
import visions
|
|
11
|
+
from multimethod import multimethod
|
|
12
|
+
from pandas.api import types as pdt
|
|
13
|
+
from visions.backends.pandas.series_utils import series_not_empty
|
|
14
|
+
from visions.relations import IdentityRelation, InferenceRelation, TypeRelation
|
|
15
|
+
|
|
16
|
+
from data_profiling.config import Settings
|
|
17
|
+
from data_profiling.model.typeset_relations import (
|
|
18
|
+
numeric_is_category,
|
|
19
|
+
series_is_string,
|
|
20
|
+
string_is_bool,
|
|
21
|
+
string_is_category,
|
|
22
|
+
string_is_datetime,
|
|
23
|
+
string_is_numeric,
|
|
24
|
+
string_to_bool,
|
|
25
|
+
string_to_datetime,
|
|
26
|
+
string_to_numeric,
|
|
27
|
+
to_bool,
|
|
28
|
+
to_category,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
pandas_has_string_dtype_flag = hasattr(pdt, "is_string_dtype")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def series_handle_nulls(fn: Callable[..., bool]) -> Callable[..., bool]:
|
|
35
|
+
"""Decorator for nullable series"""
|
|
36
|
+
|
|
37
|
+
@wraps(fn)
|
|
38
|
+
def inner(series: pd.Series, state: dict, *args, **kwargs) -> bool:
|
|
39
|
+
if "hasnans" not in state:
|
|
40
|
+
state["hasnans"] = series.hasnans
|
|
41
|
+
|
|
42
|
+
if state["hasnans"]:
|
|
43
|
+
series = series.dropna()
|
|
44
|
+
if series.empty:
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
return fn(series, state, *args, **kwargs)
|
|
48
|
+
|
|
49
|
+
return inner
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def typeset_types(config: Settings) -> Set[visions.VisionsBaseType]:
|
|
53
|
+
"""Define types based on the config"""
|
|
54
|
+
|
|
55
|
+
class Unsupported(visions.Generic):
|
|
56
|
+
"""Base type. All other types have relationship with this type."""
|
|
57
|
+
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
class Numeric(visions.VisionsBaseType):
|
|
61
|
+
"""Type for all numeric (float, int) columns.
|
|
62
|
+
|
|
63
|
+
Can be transformed from
|
|
64
|
+
- Unsupported
|
|
65
|
+
- String
|
|
66
|
+
|
|
67
|
+
Examples
|
|
68
|
+
--------
|
|
69
|
+
>>> s = pd.Series([1, 2, 5, 3, 8, 9])
|
|
70
|
+
>>> s in Numeric
|
|
71
|
+
True
|
|
72
|
+
|
|
73
|
+
>>> s = pd.Series([.34, 2.9, 55, 3.14, 89, 91])
|
|
74
|
+
>>> s in Numeric
|
|
75
|
+
True
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
@staticmethod
|
|
79
|
+
def get_relations() -> Sequence[TypeRelation]:
|
|
80
|
+
return [
|
|
81
|
+
IdentityRelation(Unsupported),
|
|
82
|
+
InferenceRelation(
|
|
83
|
+
Text,
|
|
84
|
+
relationship=lambda x, y: partial(string_is_numeric, k=config)(
|
|
85
|
+
x, y
|
|
86
|
+
),
|
|
87
|
+
transformer=string_to_numeric,
|
|
88
|
+
),
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
@staticmethod
|
|
92
|
+
@multimethod
|
|
93
|
+
@series_not_empty
|
|
94
|
+
@series_handle_nulls
|
|
95
|
+
def contains_op(series: pd.Series, state: dict) -> bool:
|
|
96
|
+
return pdt.is_numeric_dtype(series) and not pdt.is_bool_dtype(series)
|
|
97
|
+
|
|
98
|
+
class Text(visions.VisionsBaseType):
|
|
99
|
+
"""Type for plaintext columns.
|
|
100
|
+
Like name, note, string identifier, residence etc.
|
|
101
|
+
|
|
102
|
+
Examples
|
|
103
|
+
--------
|
|
104
|
+
>>> s = pd.Series(["AX01", "BC32", "AC00"])
|
|
105
|
+
>>> s in Categorical
|
|
106
|
+
True
|
|
107
|
+
|
|
108
|
+
>>> s = pd.Series([1, 2, 3, 4])
|
|
109
|
+
>>> s in Categorical
|
|
110
|
+
False
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
@staticmethod
|
|
114
|
+
def get_relations() -> Sequence[TypeRelation]:
|
|
115
|
+
return [
|
|
116
|
+
IdentityRelation(Unsupported),
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
@staticmethod
|
|
120
|
+
@multimethod
|
|
121
|
+
@series_not_empty
|
|
122
|
+
@series_handle_nulls
|
|
123
|
+
def contains_op(series: pd.Series, state: dict) -> bool:
|
|
124
|
+
return (
|
|
125
|
+
not isinstance(series.dtype, pd.CategoricalDtype)
|
|
126
|
+
and pdt.is_string_dtype(series)
|
|
127
|
+
and series_is_string(series, state)
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
class DateTime(visions.VisionsBaseType):
|
|
131
|
+
@staticmethod
|
|
132
|
+
def get_relations() -> Sequence[TypeRelation]:
|
|
133
|
+
return [
|
|
134
|
+
IdentityRelation(Unsupported),
|
|
135
|
+
InferenceRelation(
|
|
136
|
+
Text,
|
|
137
|
+
relationship=lambda x, y: partial(string_is_datetime)(x, y),
|
|
138
|
+
transformer=string_to_datetime,
|
|
139
|
+
),
|
|
140
|
+
]
|
|
141
|
+
|
|
142
|
+
@staticmethod
|
|
143
|
+
@multimethod
|
|
144
|
+
@series_not_empty
|
|
145
|
+
@series_handle_nulls
|
|
146
|
+
def contains_op(series: pd.Series, state: dict) -> bool:
|
|
147
|
+
is_datetime = pdt.is_datetime64_any_dtype(series)
|
|
148
|
+
if is_datetime:
|
|
149
|
+
return True
|
|
150
|
+
has_builtin_datetime = (
|
|
151
|
+
series.dropna()
|
|
152
|
+
.apply(type)
|
|
153
|
+
.isin([datetime.date, datetime.datetime])
|
|
154
|
+
.all()
|
|
155
|
+
)
|
|
156
|
+
return has_builtin_datetime
|
|
157
|
+
|
|
158
|
+
class Categorical(visions.VisionsBaseType):
|
|
159
|
+
"""Type for categorical columns.
|
|
160
|
+
Categorical columns in pandas categorical format
|
|
161
|
+
and columns in string format with small count of unique values.
|
|
162
|
+
|
|
163
|
+
Can be transformed from:
|
|
164
|
+
- Unsupported
|
|
165
|
+
- Numeric
|
|
166
|
+
- String
|
|
167
|
+
|
|
168
|
+
Examples
|
|
169
|
+
--------
|
|
170
|
+
>>> s = pd.Series(["male", "female", "female", "male"], dtype="category")
|
|
171
|
+
>>> s in Categorical
|
|
172
|
+
True
|
|
173
|
+
|
|
174
|
+
>>> s = pd.Series(["male", "female"])
|
|
175
|
+
>>> s in Categorical
|
|
176
|
+
False
|
|
177
|
+
|
|
178
|
+
>>> s = pd.Series(["male", "female", "female", "male"])
|
|
179
|
+
>>> s in Categorical
|
|
180
|
+
True
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
@staticmethod
|
|
184
|
+
def get_relations() -> Sequence[TypeRelation]:
|
|
185
|
+
return [
|
|
186
|
+
IdentityRelation(Unsupported),
|
|
187
|
+
InferenceRelation(
|
|
188
|
+
Numeric,
|
|
189
|
+
relationship=lambda x, y: partial(numeric_is_category, k=config)(
|
|
190
|
+
x, y
|
|
191
|
+
),
|
|
192
|
+
transformer=to_category,
|
|
193
|
+
),
|
|
194
|
+
InferenceRelation(
|
|
195
|
+
Text,
|
|
196
|
+
relationship=lambda x, y: partial(string_is_category, k=config)(
|
|
197
|
+
x, y
|
|
198
|
+
),
|
|
199
|
+
transformer=to_category,
|
|
200
|
+
),
|
|
201
|
+
]
|
|
202
|
+
|
|
203
|
+
@staticmethod
|
|
204
|
+
@multimethod
|
|
205
|
+
@series_not_empty
|
|
206
|
+
@series_handle_nulls
|
|
207
|
+
def contains_op(series: pd.Series, state: dict) -> bool:
|
|
208
|
+
is_valid_dtype = isinstance(
|
|
209
|
+
series.dtype, pd.CategoricalDtype
|
|
210
|
+
) and not pdt.is_bool_dtype(series)
|
|
211
|
+
if is_valid_dtype:
|
|
212
|
+
return True
|
|
213
|
+
return False
|
|
214
|
+
|
|
215
|
+
class Boolean(visions.VisionsBaseType):
|
|
216
|
+
"""Type for boolean columns."""
|
|
217
|
+
|
|
218
|
+
@staticmethod
|
|
219
|
+
def get_relations() -> Sequence[TypeRelation]:
|
|
220
|
+
# Numeric [0, 1] goes via Categorical with distinct_count_without_nan <= 2
|
|
221
|
+
mapping = config.vars.bool.mappings
|
|
222
|
+
|
|
223
|
+
return [
|
|
224
|
+
IdentityRelation(Unsupported),
|
|
225
|
+
InferenceRelation(
|
|
226
|
+
Text,
|
|
227
|
+
relationship=lambda x, y: partial(string_is_bool, k=mapping)(x, y),
|
|
228
|
+
transformer=lambda s, st: to_bool(
|
|
229
|
+
partial(string_to_bool, k=mapping)(s, st)
|
|
230
|
+
),
|
|
231
|
+
),
|
|
232
|
+
]
|
|
233
|
+
|
|
234
|
+
@staticmethod
|
|
235
|
+
@multimethod
|
|
236
|
+
@series_not_empty
|
|
237
|
+
@series_handle_nulls
|
|
238
|
+
def contains_op(series: pd.Series, state: dict) -> bool:
|
|
239
|
+
if pdt.is_object_dtype(series):
|
|
240
|
+
try:
|
|
241
|
+
return series.isin({True, False}).all()
|
|
242
|
+
except: # noqa: E722
|
|
243
|
+
return False
|
|
244
|
+
|
|
245
|
+
return pdt.is_bool_dtype(series)
|
|
246
|
+
|
|
247
|
+
class URL(visions.VisionsBaseType):
|
|
248
|
+
@staticmethod
|
|
249
|
+
def get_relations() -> Sequence[TypeRelation]:
|
|
250
|
+
return [IdentityRelation(Text)]
|
|
251
|
+
|
|
252
|
+
@staticmethod
|
|
253
|
+
@multimethod
|
|
254
|
+
@series_handle_nulls
|
|
255
|
+
def contains_op(series: pd.Series, state: dict) -> bool:
|
|
256
|
+
# TODO: use coercion utils
|
|
257
|
+
try:
|
|
258
|
+
url_gen = (urlparse(x) for x in series)
|
|
259
|
+
return all(x.netloc and x.scheme for x in url_gen) # noqa: TC300
|
|
260
|
+
except AttributeError:
|
|
261
|
+
return False
|
|
262
|
+
|
|
263
|
+
class Path(visions.VisionsBaseType):
|
|
264
|
+
@staticmethod
|
|
265
|
+
def get_relations() -> Sequence[TypeRelation]:
|
|
266
|
+
return [IdentityRelation(Text)]
|
|
267
|
+
|
|
268
|
+
@staticmethod
|
|
269
|
+
@multimethod
|
|
270
|
+
@series_handle_nulls
|
|
271
|
+
def contains_op(series: pd.Series, state: dict) -> bool:
|
|
272
|
+
# TODO: use coercion utils
|
|
273
|
+
try:
|
|
274
|
+
return all(os.path.isabs(p) for p in series)
|
|
275
|
+
except TypeError:
|
|
276
|
+
return False
|
|
277
|
+
|
|
278
|
+
class File(visions.VisionsBaseType):
|
|
279
|
+
@staticmethod
|
|
280
|
+
def get_relations() -> Sequence[TypeRelation]:
|
|
281
|
+
return [IdentityRelation(Path)]
|
|
282
|
+
|
|
283
|
+
@staticmethod
|
|
284
|
+
@multimethod
|
|
285
|
+
@series_handle_nulls
|
|
286
|
+
def contains_op(series: pd.Series, state: dict) -> bool:
|
|
287
|
+
return all(os.path.exists(p) for p in series)
|
|
288
|
+
|
|
289
|
+
class Image(visions.VisionsBaseType):
|
|
290
|
+
@staticmethod
|
|
291
|
+
def get_relations() -> Sequence[TypeRelation]:
|
|
292
|
+
return [IdentityRelation(File)]
|
|
293
|
+
|
|
294
|
+
@staticmethod
|
|
295
|
+
@multimethod
|
|
296
|
+
@series_handle_nulls
|
|
297
|
+
def contains_op(series: pd.Series, state: dict) -> bool:
|
|
298
|
+
return all(
|
|
299
|
+
filetype.guess(str(p))
|
|
300
|
+
and filetype.guess(str(p)).mime.startswith("image/")
|
|
301
|
+
for p in series
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
class TimeSeries(visions.VisionsBaseType):
|
|
305
|
+
@staticmethod
|
|
306
|
+
def get_relations() -> Sequence[TypeRelation]:
|
|
307
|
+
return [IdentityRelation(Numeric)]
|
|
308
|
+
|
|
309
|
+
@staticmethod
|
|
310
|
+
@multimethod
|
|
311
|
+
@series_not_empty
|
|
312
|
+
@series_handle_nulls
|
|
313
|
+
def contains_op(series: pd.Series, state: dict) -> bool:
|
|
314
|
+
def is_timedependent(series: pd.Series) -> bool:
|
|
315
|
+
autocorrelation_threshold = config.vars.timeseries.autocorrelation
|
|
316
|
+
lags = config.vars.timeseries.lags
|
|
317
|
+
with warnings.catch_warnings():
|
|
318
|
+
warnings.simplefilter("ignore", RuntimeWarning)
|
|
319
|
+
for lag in lags:
|
|
320
|
+
autcorr = series.autocorr(lag=lag)
|
|
321
|
+
if autcorr >= autocorrelation_threshold:
|
|
322
|
+
return True
|
|
323
|
+
|
|
324
|
+
return False
|
|
325
|
+
|
|
326
|
+
is_numeric = pdt.is_numeric_dtype(series) and not pdt.is_bool_dtype(series)
|
|
327
|
+
return is_numeric and is_timedependent(series)
|
|
328
|
+
|
|
329
|
+
types = {Unsupported, Boolean, Numeric, Text, Categorical, DateTime}
|
|
330
|
+
if config.vars.path.active:
|
|
331
|
+
types.add(Path)
|
|
332
|
+
if config.vars.file.active:
|
|
333
|
+
types.add(File)
|
|
334
|
+
if config.vars.image.active:
|
|
335
|
+
types.add(Image)
|
|
336
|
+
|
|
337
|
+
if config.vars.url.active:
|
|
338
|
+
types.add(URL)
|
|
339
|
+
|
|
340
|
+
if config.vars.timeseries.active:
|
|
341
|
+
types.add(TimeSeries)
|
|
342
|
+
|
|
343
|
+
return types
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
class ProfilingTypeSet(visions.VisionsTypeset):
|
|
347
|
+
def __init__(self, config: Settings, type_schema: dict = None):
|
|
348
|
+
self.config = config
|
|
349
|
+
|
|
350
|
+
types = typeset_types(config)
|
|
351
|
+
|
|
352
|
+
with warnings.catch_warnings():
|
|
353
|
+
warnings.filterwarnings("ignore", category=UserWarning)
|
|
354
|
+
super().__init__(types)
|
|
355
|
+
|
|
356
|
+
self.type_schema = self._init_type_schema(type_schema or {})
|
|
357
|
+
|
|
358
|
+
def _init_type_schema(self, type_schema: dict) -> dict:
|
|
359
|
+
return {k: self._get_type(v) for k, v in type_schema.items()}
|
|
360
|
+
|
|
361
|
+
def _get_type(self, type_name: str) -> visions.VisionsBaseType:
|
|
362
|
+
for t in self.types:
|
|
363
|
+
if t.__name__.lower() == type_name.lower():
|
|
364
|
+
return t
|
|
365
|
+
raise ValueError(f"Type [{type_name}] not found.")
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
from typing import Callable, Dict
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import pandas as pd
|
|
6
|
+
from pandas.api import types as pdt
|
|
7
|
+
from visions.backends.pandas.series_utils import series_handle_nulls
|
|
8
|
+
|
|
9
|
+
from data_profiling.config import Settings
|
|
10
|
+
from data_profiling.utils.versions import is_pandas_1
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def is_nullable(series: pd.Series, state: dict) -> bool:
|
|
14
|
+
return series.count() > 0
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def try_func(fn: Callable) -> Callable:
|
|
18
|
+
@functools.wraps(fn)
|
|
19
|
+
def inner(series: pd.Series, *args, **kwargs) -> bool:
|
|
20
|
+
try:
|
|
21
|
+
return fn(series, *args, **kwargs)
|
|
22
|
+
except: # noqa: E722
|
|
23
|
+
return False
|
|
24
|
+
|
|
25
|
+
return inner
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def string_is_bool(series: pd.Series, state: dict, k: Dict[str, bool]) -> bool:
|
|
29
|
+
@series_handle_nulls
|
|
30
|
+
@try_func
|
|
31
|
+
def tester(s: pd.Series, state: dict) -> bool:
|
|
32
|
+
return s.str.lower().isin(k.keys()).all()
|
|
33
|
+
|
|
34
|
+
if isinstance(series.dtype, pd.CategoricalDtype):
|
|
35
|
+
return False
|
|
36
|
+
|
|
37
|
+
return tester(series, state)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def string_to_bool(series: pd.Series, state: dict, k: Dict[str, bool]) -> pd.Series:
|
|
41
|
+
return series.str.lower().map(k)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def numeric_is_category(series: pd.Series, state: dict, k: Settings) -> bool:
|
|
45
|
+
n_unique = series.nunique()
|
|
46
|
+
threshold = k.vars.num.low_categorical_threshold
|
|
47
|
+
return 1 <= n_unique <= threshold
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def to_category(series: pd.Series, state: dict) -> pd.Series:
|
|
51
|
+
hasnans = series.hasnans
|
|
52
|
+
val = series.astype(str)
|
|
53
|
+
if hasnans:
|
|
54
|
+
val = val.replace("nan", np.nan)
|
|
55
|
+
val = val.replace("<NA>", np.nan)
|
|
56
|
+
|
|
57
|
+
return val.astype("string")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@series_handle_nulls
|
|
61
|
+
def series_is_string(series: pd.Series, state: dict) -> bool:
|
|
62
|
+
if not all(isinstance(v, str) for v in series.values[0:5]):
|
|
63
|
+
return False
|
|
64
|
+
try:
|
|
65
|
+
return (series.astype(str).values == series.values).all()
|
|
66
|
+
except (TypeError, ValueError):
|
|
67
|
+
return False
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@series_handle_nulls
|
|
71
|
+
def string_is_category(series: pd.Series, state: dict, k: Settings) -> bool:
|
|
72
|
+
"""String is category, if the following conditions are met
|
|
73
|
+
- has at least one and less or equal distinct values as threshold
|
|
74
|
+
- (distinct values / count of all values) is less than threshold
|
|
75
|
+
- is not bool"""
|
|
76
|
+
n_unique = series.nunique()
|
|
77
|
+
unique_threshold = k.vars.cat.percentage_cat_threshold
|
|
78
|
+
threshold = k.vars.cat.cardinality_threshold
|
|
79
|
+
return (
|
|
80
|
+
1 <= n_unique <= threshold
|
|
81
|
+
and (
|
|
82
|
+
n_unique / series.size < unique_threshold
|
|
83
|
+
if unique_threshold <= 1
|
|
84
|
+
else n_unique / series.size <= unique_threshold
|
|
85
|
+
)
|
|
86
|
+
and not string_is_bool(series, state, k.vars.bool.mappings)
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@series_handle_nulls
|
|
91
|
+
def string_is_datetime(series: pd.Series, state: dict) -> bool:
|
|
92
|
+
"""If we can transform data to datetime and at least one is valid date."""
|
|
93
|
+
try:
|
|
94
|
+
return not string_to_datetime(series, state).isna().all()
|
|
95
|
+
except: # noqa: E722
|
|
96
|
+
return False
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@series_handle_nulls
|
|
100
|
+
def string_is_numeric(series: pd.Series, state: dict, k: Settings) -> bool:
|
|
101
|
+
if pdt.is_bool_dtype(series) or object_is_bool(series, state):
|
|
102
|
+
return False
|
|
103
|
+
|
|
104
|
+
try:
|
|
105
|
+
_ = series.astype(float)
|
|
106
|
+
r = pd.to_numeric(series, errors="coerce")
|
|
107
|
+
if r.hasnans and r.count() == 0:
|
|
108
|
+
return False
|
|
109
|
+
except: # noqa: E722
|
|
110
|
+
return False
|
|
111
|
+
|
|
112
|
+
return not numeric_is_category(series, state, k)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def string_to_datetime(series: pd.Series, state: dict) -> pd.Series:
|
|
116
|
+
if is_pandas_1():
|
|
117
|
+
return pd.to_datetime(series)
|
|
118
|
+
return pd.to_datetime(series, format="mixed")
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def string_to_numeric(series: pd.Series, state: dict) -> pd.Series:
|
|
122
|
+
return pd.to_numeric(series, errors="coerce")
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
hasnan_bool_name = "boolean"
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def to_bool(series: pd.Series) -> pd.Series:
|
|
129
|
+
dtype = hasnan_bool_name if series.hasnans else bool
|
|
130
|
+
return series.astype(dtype)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@series_handle_nulls
|
|
134
|
+
def object_is_bool(series: pd.Series, state: dict) -> bool:
|
|
135
|
+
if pdt.is_object_dtype(series):
|
|
136
|
+
bool_set = {True, False}
|
|
137
|
+
try:
|
|
138
|
+
ret = all(item in bool_set for item in series)
|
|
139
|
+
except: # noqa: E722
|
|
140
|
+
ret = False
|
|
141
|
+
|
|
142
|
+
return ret
|
|
143
|
+
return False
|