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.
Files changed (238) hide show
  1. data_profiling/__init__.py +34 -0
  2. data_profiling/compare_reports.py +359 -0
  3. data_profiling/config.py +496 -0
  4. data_profiling/config_default.yaml +223 -0
  5. data_profiling/config_minimal.yaml +222 -0
  6. data_profiling/controller/__init__.py +1 -0
  7. data_profiling/controller/console.py +125 -0
  8. data_profiling/controller/pandas_decorator.py +21 -0
  9. data_profiling/expectations_report.py +117 -0
  10. data_profiling/model/__init__.py +4 -0
  11. data_profiling/model/alerts.py +780 -0
  12. data_profiling/model/correlations.py +163 -0
  13. data_profiling/model/dataframe.py +35 -0
  14. data_profiling/model/describe.py +210 -0
  15. data_profiling/model/description.py +108 -0
  16. data_profiling/model/duplicates.py +14 -0
  17. data_profiling/model/expectation_algorithms.py +112 -0
  18. data_profiling/model/handler.py +81 -0
  19. data_profiling/model/missing.py +146 -0
  20. data_profiling/model/pairwise.py +33 -0
  21. data_profiling/model/pandas/__init__.py +55 -0
  22. data_profiling/model/pandas/correlations_pandas.py +207 -0
  23. data_profiling/model/pandas/dataframe_pandas.py +26 -0
  24. data_profiling/model/pandas/describe_boolean_pandas.py +43 -0
  25. data_profiling/model/pandas/describe_categorical_pandas.py +274 -0
  26. data_profiling/model/pandas/describe_counts_pandas.py +63 -0
  27. data_profiling/model/pandas/describe_date_pandas.py +77 -0
  28. data_profiling/model/pandas/describe_file_pandas.py +56 -0
  29. data_profiling/model/pandas/describe_generic_pandas.py +36 -0
  30. data_profiling/model/pandas/describe_image_pandas.py +255 -0
  31. data_profiling/model/pandas/describe_numeric_pandas.py +175 -0
  32. data_profiling/model/pandas/describe_path_pandas.py +63 -0
  33. data_profiling/model/pandas/describe_supported_pandas.py +41 -0
  34. data_profiling/model/pandas/describe_text_pandas.py +62 -0
  35. data_profiling/model/pandas/describe_timeseries_pandas.py +222 -0
  36. data_profiling/model/pandas/describe_url_pandas.py +57 -0
  37. data_profiling/model/pandas/discretize_pandas.py +81 -0
  38. data_profiling/model/pandas/duplicates_pandas.py +56 -0
  39. data_profiling/model/pandas/imbalance_pandas.py +35 -0
  40. data_profiling/model/pandas/missing_pandas.py +42 -0
  41. data_profiling/model/pandas/sample_pandas.py +38 -0
  42. data_profiling/model/pandas/summary_pandas.py +101 -0
  43. data_profiling/model/pandas/table_pandas.py +56 -0
  44. data_profiling/model/pandas/timeseries_index_pandas.py +33 -0
  45. data_profiling/model/pandas/utils_pandas.py +27 -0
  46. data_profiling/model/sample.py +37 -0
  47. data_profiling/model/spark/__init__.py +48 -0
  48. data_profiling/model/spark/correlations_spark.py +152 -0
  49. data_profiling/model/spark/dataframe_spark.py +34 -0
  50. data_profiling/model/spark/describe_boolean_spark.py +27 -0
  51. data_profiling/model/spark/describe_categorical_spark.py +28 -0
  52. data_profiling/model/spark/describe_counts_spark.py +105 -0
  53. data_profiling/model/spark/describe_date_spark.py +51 -0
  54. data_profiling/model/spark/describe_generic_spark.py +30 -0
  55. data_profiling/model/spark/describe_numeric_spark.py +155 -0
  56. data_profiling/model/spark/describe_supported_spark.py +33 -0
  57. data_profiling/model/spark/describe_text_spark.py +25 -0
  58. data_profiling/model/spark/duplicates_spark.py +54 -0
  59. data_profiling/model/spark/missing_spark.py +96 -0
  60. data_profiling/model/spark/sample_spark.py +43 -0
  61. data_profiling/model/spark/summary_spark.py +95 -0
  62. data_profiling/model/spark/table_spark.py +58 -0
  63. data_profiling/model/spark/timeseries_index_spark.py +12 -0
  64. data_profiling/model/summarizer.py +207 -0
  65. data_profiling/model/summary.py +66 -0
  66. data_profiling/model/summary_algorithms.py +276 -0
  67. data_profiling/model/table.py +10 -0
  68. data_profiling/model/timeseries_index.py +16 -0
  69. data_profiling/model/typeset.py +365 -0
  70. data_profiling/model/typeset_relations.py +143 -0
  71. data_profiling/profile_report.py +573 -0
  72. data_profiling/report/__init__.py +4 -0
  73. data_profiling/report/formatters.py +346 -0
  74. data_profiling/report/presentation/__init__.py +1 -0
  75. data_profiling/report/presentation/core/__init__.py +39 -0
  76. data_profiling/report/presentation/core/alerts.py +18 -0
  77. data_profiling/report/presentation/core/collapse.py +24 -0
  78. data_profiling/report/presentation/core/container.py +50 -0
  79. data_profiling/report/presentation/core/correlation_table.py +21 -0
  80. data_profiling/report/presentation/core/dropdown.py +44 -0
  81. data_profiling/report/presentation/core/duplicate.py +16 -0
  82. data_profiling/report/presentation/core/frequency_table.py +14 -0
  83. data_profiling/report/presentation/core/frequency_table_small.py +16 -0
  84. data_profiling/report/presentation/core/html.py +14 -0
  85. data_profiling/report/presentation/core/image.py +34 -0
  86. data_profiling/report/presentation/core/item_renderer.py +17 -0
  87. data_profiling/report/presentation/core/renderable.py +42 -0
  88. data_profiling/report/presentation/core/root.py +35 -0
  89. data_profiling/report/presentation/core/sample.py +20 -0
  90. data_profiling/report/presentation/core/scores.py +32 -0
  91. data_profiling/report/presentation/core/table.py +26 -0
  92. data_profiling/report/presentation/core/toggle_button.py +14 -0
  93. data_profiling/report/presentation/core/variable.py +40 -0
  94. data_profiling/report/presentation/core/variable_info.py +36 -0
  95. data_profiling/report/presentation/flavours/__init__.py +9 -0
  96. data_profiling/report/presentation/flavours/flavour_html.py +64 -0
  97. data_profiling/report/presentation/flavours/flavour_widget.py +61 -0
  98. data_profiling/report/presentation/flavours/flavours.py +43 -0
  99. data_profiling/report/presentation/flavours/html/__init__.py +47 -0
  100. data_profiling/report/presentation/flavours/html/alerts.py +10 -0
  101. data_profiling/report/presentation/flavours/html/collapse.py +7 -0
  102. data_profiling/report/presentation/flavours/html/container.py +58 -0
  103. data_profiling/report/presentation/flavours/html/correlation_table.py +13 -0
  104. data_profiling/report/presentation/flavours/html/dropdown.py +7 -0
  105. data_profiling/report/presentation/flavours/html/duplicate.py +24 -0
  106. data_profiling/report/presentation/flavours/html/frequency_table.py +20 -0
  107. data_profiling/report/presentation/flavours/html/frequency_table_small.py +15 -0
  108. data_profiling/report/presentation/flavours/html/html.py +6 -0
  109. data_profiling/report/presentation/flavours/html/image.py +7 -0
  110. data_profiling/report/presentation/flavours/html/root.py +14 -0
  111. data_profiling/report/presentation/flavours/html/sample.py +12 -0
  112. data_profiling/report/presentation/flavours/html/scores.py +11 -0
  113. data_profiling/report/presentation/flavours/html/table.py +7 -0
  114. data_profiling/report/presentation/flavours/html/templates/alerts/alert_constant.html +1 -0
  115. data_profiling/report/presentation/flavours/html/templates/alerts/alert_constant_length.html +1 -0
  116. data_profiling/report/presentation/flavours/html/templates/alerts/alert_dirty_category.html +1 -0
  117. data_profiling/report/presentation/flavours/html/templates/alerts/alert_duplicates.html +1 -0
  118. data_profiling/report/presentation/flavours/html/templates/alerts/alert_empty.html +1 -0
  119. data_profiling/report/presentation/flavours/html/templates/alerts/alert_high_cardinality.html +1 -0
  120. data_profiling/report/presentation/flavours/html/templates/alerts/alert_high_correlation.html +4 -0
  121. data_profiling/report/presentation/flavours/html/templates/alerts/alert_imbalance.html +1 -0
  122. data_profiling/report/presentation/flavours/html/templates/alerts/alert_infinite.html +1 -0
  123. data_profiling/report/presentation/flavours/html/templates/alerts/alert_missing.html +1 -0
  124. data_profiling/report/presentation/flavours/html/templates/alerts/alert_near_duplicates.html +1 -0
  125. data_profiling/report/presentation/flavours/html/templates/alerts/alert_non_stationary.html +1 -0
  126. data_profiling/report/presentation/flavours/html/templates/alerts/alert_seasonal.html +1 -0
  127. data_profiling/report/presentation/flavours/html/templates/alerts/alert_skewed.html +1 -0
  128. data_profiling/report/presentation/flavours/html/templates/alerts/alert_truncated.html +1 -0
  129. data_profiling/report/presentation/flavours/html/templates/alerts/alert_type_date.html +1 -0
  130. data_profiling/report/presentation/flavours/html/templates/alerts/alert_uniform.html +1 -0
  131. data_profiling/report/presentation/flavours/html/templates/alerts/alert_unique.html +1 -0
  132. data_profiling/report/presentation/flavours/html/templates/alerts/alert_unsupported.html +1 -0
  133. data_profiling/report/presentation/flavours/html/templates/alerts/alert_zeros.html +1 -0
  134. data_profiling/report/presentation/flavours/html/templates/alerts.html +47 -0
  135. data_profiling/report/presentation/flavours/html/templates/collapse.html +11 -0
  136. data_profiling/report/presentation/flavours/html/templates/correlation_table.html +5 -0
  137. data_profiling/report/presentation/flavours/html/templates/diagram.html +11 -0
  138. data_profiling/report/presentation/flavours/html/templates/dropdown.html +16 -0
  139. data_profiling/report/presentation/flavours/html/templates/duplicate.html +5 -0
  140. data_profiling/report/presentation/flavours/html/templates/frequency_table.html +45 -0
  141. data_profiling/report/presentation/flavours/html/templates/frequency_table_small.html +34 -0
  142. data_profiling/report/presentation/flavours/html/templates/report.html +26 -0
  143. data_profiling/report/presentation/flavours/html/templates/sample.html +10 -0
  144. data_profiling/report/presentation/flavours/html/templates/scores.html +78 -0
  145. data_profiling/report/presentation/flavours/html/templates/sequence/batch_grid.html +16 -0
  146. data_profiling/report/presentation/flavours/html/templates/sequence/grid.html +18 -0
  147. data_profiling/report/presentation/flavours/html/templates/sequence/list.html +7 -0
  148. data_profiling/report/presentation/flavours/html/templates/sequence/named_list.html +8 -0
  149. data_profiling/report/presentation/flavours/html/templates/sequence/overview_tabs.html +30 -0
  150. data_profiling/report/presentation/flavours/html/templates/sequence/scores.html +3 -0
  151. data_profiling/report/presentation/flavours/html/templates/sequence/sections.html +13 -0
  152. data_profiling/report/presentation/flavours/html/templates/sequence/select.html +40 -0
  153. data_profiling/report/presentation/flavours/html/templates/sequence/tabs.html +30 -0
  154. data_profiling/report/presentation/flavours/html/templates/table.html +38 -0
  155. data_profiling/report/presentation/flavours/html/templates/toggle_button.html +18 -0
  156. data_profiling/report/presentation/flavours/html/templates/variable.html +7 -0
  157. data_profiling/report/presentation/flavours/html/templates/variable_info.html +49 -0
  158. data_profiling/report/presentation/flavours/html/templates/wrapper/assets/bootstrap.bundle.min.js +7 -0
  159. data_profiling/report/presentation/flavours/html/templates/wrapper/assets/bootstrap.min.css +6 -0
  160. data_profiling/report/presentation/flavours/html/templates/wrapper/assets/cosmo.bootstrap.min.css +12 -0
  161. data_profiling/report/presentation/flavours/html/templates/wrapper/assets/flatly.bootstrap.min.css +12 -0
  162. data_profiling/report/presentation/flavours/html/templates/wrapper/assets/script.js +52 -0
  163. data_profiling/report/presentation/flavours/html/templates/wrapper/assets/simplex.bootstrap.min.css +12 -0
  164. data_profiling/report/presentation/flavours/html/templates/wrapper/assets/style.css +253 -0
  165. data_profiling/report/presentation/flavours/html/templates/wrapper/assets/united.bootstrap.min.css +12 -0
  166. data_profiling/report/presentation/flavours/html/templates/wrapper/footer.html +7 -0
  167. data_profiling/report/presentation/flavours/html/templates/wrapper/javascript.html +18 -0
  168. data_profiling/report/presentation/flavours/html/templates/wrapper/navigation.html +36 -0
  169. data_profiling/report/presentation/flavours/html/templates/wrapper/style.html +53 -0
  170. data_profiling/report/presentation/flavours/html/templates.py +76 -0
  171. data_profiling/report/presentation/flavours/html/toggle_button.py +7 -0
  172. data_profiling/report/presentation/flavours/html/variable.py +7 -0
  173. data_profiling/report/presentation/flavours/html/variable_info.py +7 -0
  174. data_profiling/report/presentation/flavours/widget/__init__.py +49 -0
  175. data_profiling/report/presentation/flavours/widget/alerts.py +45 -0
  176. data_profiling/report/presentation/flavours/widget/collapse.py +43 -0
  177. data_profiling/report/presentation/flavours/widget/container.py +121 -0
  178. data_profiling/report/presentation/flavours/widget/correlation_table.py +14 -0
  179. data_profiling/report/presentation/flavours/widget/dropdown.py +31 -0
  180. data_profiling/report/presentation/flavours/widget/duplicate.py +14 -0
  181. data_profiling/report/presentation/flavours/widget/frequency_table.py +57 -0
  182. data_profiling/report/presentation/flavours/widget/frequency_table_small.py +66 -0
  183. data_profiling/report/presentation/flavours/widget/html.py +11 -0
  184. data_profiling/report/presentation/flavours/widget/image.py +26 -0
  185. data_profiling/report/presentation/flavours/widget/notebook.py +81 -0
  186. data_profiling/report/presentation/flavours/widget/root.py +10 -0
  187. data_profiling/report/presentation/flavours/widget/sample.py +14 -0
  188. data_profiling/report/presentation/flavours/widget/table.py +30 -0
  189. data_profiling/report/presentation/flavours/widget/toggle_button.py +17 -0
  190. data_profiling/report/presentation/flavours/widget/variable.py +12 -0
  191. data_profiling/report/presentation/flavours/widget/variable_info.py +11 -0
  192. data_profiling/report/presentation/frequency_table_utils.py +141 -0
  193. data_profiling/report/structure/__init__.py +1 -0
  194. data_profiling/report/structure/correlations.py +123 -0
  195. data_profiling/report/structure/overview.py +376 -0
  196. data_profiling/report/structure/report.py +457 -0
  197. data_profiling/report/structure/variables/__init__.py +35 -0
  198. data_profiling/report/structure/variables/render_boolean.py +132 -0
  199. data_profiling/report/structure/variables/render_categorical.py +566 -0
  200. data_profiling/report/structure/variables/render_common.py +31 -0
  201. data_profiling/report/structure/variables/render_complex.py +102 -0
  202. data_profiling/report/structure/variables/render_count.py +172 -0
  203. data_profiling/report/structure/variables/render_date.py +143 -0
  204. data_profiling/report/structure/variables/render_file.py +70 -0
  205. data_profiling/report/structure/variables/render_generic.py +45 -0
  206. data_profiling/report/structure/variables/render_image.py +204 -0
  207. data_profiling/report/structure/variables/render_path.py +134 -0
  208. data_profiling/report/structure/variables/render_real.py +314 -0
  209. data_profiling/report/structure/variables/render_text.py +189 -0
  210. data_profiling/report/structure/variables/render_timeseries.py +371 -0
  211. data_profiling/report/structure/variables/render_url.py +132 -0
  212. data_profiling/report/utils.py +34 -0
  213. data_profiling/serialize_report.py +143 -0
  214. data_profiling/utils/__init__.py +1 -0
  215. data_profiling/utils/backend.py +9 -0
  216. data_profiling/utils/cache.py +59 -0
  217. data_profiling/utils/common.py +142 -0
  218. data_profiling/utils/compat.py +31 -0
  219. data_profiling/utils/dataframe.py +238 -0
  220. data_profiling/utils/logger.py +53 -0
  221. data_profiling/utils/notebook.py +8 -0
  222. data_profiling/utils/paths.py +45 -0
  223. data_profiling/utils/progress_bar.py +15 -0
  224. data_profiling/utils/styles.py +22 -0
  225. data_profiling/utils/versions.py +19 -0
  226. data_profiling/version.py +1 -0
  227. data_profiling/visualisation/__init__.py +1 -0
  228. data_profiling/visualisation/context.py +87 -0
  229. data_profiling/visualisation/missing.py +138 -0
  230. data_profiling/visualisation/plot.py +1158 -0
  231. data_profiling/visualisation/utils.py +113 -0
  232. fg_data_profiling-4.19.0.dist-info/METADATA +362 -0
  233. fg_data_profiling-4.19.0.dist-info/RECORD +238 -0
  234. fg_data_profiling-4.19.0.dist-info/WHEEL +6 -0
  235. fg_data_profiling-4.19.0.dist-info/entry_points.txt +3 -0
  236. fg_data_profiling-4.19.0.dist-info/licenses/LICENSE +21 -0
  237. fg_data_profiling-4.19.0.dist-info/top_level.txt +2 -0
  238. 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