upgini 1.1.308__tar.gz → 1.1.309a1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of upgini might be problematic. Click here for more details.
- {upgini-1.1.308 → upgini-1.1.309a1}/PKG-INFO +1 -1
- upgini-1.1.309a1/src/upgini/__about__.py +1 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/dataset.py +30 -385
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/features_enricher.py +286 -130
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/metadata.py +16 -1
- upgini-1.1.309a1/src/upgini/normalizer/normalize_utils.py +203 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/utils/country_utils.py +16 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/utils/datetime_utils.py +34 -15
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/utils/email_utils.py +19 -5
- upgini-1.1.309a1/src/upgini/utils/ip_utils.py +152 -0
- upgini-1.1.308/src/upgini/normalizer/phone_normalizer.py → upgini-1.1.309a1/src/upgini/utils/phone_utils.py +41 -25
- upgini-1.1.309a1/src/upgini/utils/postal_code_utils.py +45 -0
- upgini-1.1.308/src/upgini/__about__.py +0 -1
- upgini-1.1.308/src/upgini/utils/ip_utils.py +0 -53
- upgini-1.1.308/src/upgini/utils/phone_utils.py +0 -11
- upgini-1.1.308/src/upgini/utils/postal_code_utils.py +0 -11
- {upgini-1.1.308 → upgini-1.1.309a1}/.gitignore +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/LICENSE +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/README.md +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/pyproject.toml +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/__init__.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/ads.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/ads_management/__init__.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/ads_management/ads_manager.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/autofe/__init__.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/autofe/all_operands.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/autofe/binary.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/autofe/date.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/autofe/feature.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/autofe/groupby.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/autofe/operand.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/autofe/unary.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/autofe/vector.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/data_source/__init__.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/data_source/data_source_publisher.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/errors.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/http.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/lazy_import.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/mdc/__init__.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/mdc/context.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/metrics.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/normalizer/__init__.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/resource_bundle/__init__.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/resource_bundle/exceptions.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/resource_bundle/strings.properties +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/resource_bundle/strings_widget.properties +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/sampler/__init__.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/sampler/base.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/sampler/random_under_sampler.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/sampler/utils.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/search_task.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/spinner.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/utils/__init__.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/utils/base_search_key_detector.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/utils/blocked_time_series.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/utils/custom_loss_utils.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/utils/cv_utils.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/utils/deduplicate_utils.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/utils/display_utils.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/utils/fallback_progress_bar.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/utils/features_validator.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/utils/format.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/utils/progress_bar.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/utils/sklearn_ext.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/utils/target_utils.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/utils/track_info.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/utils/warning_counter.py +0 -0
- {upgini-1.1.308 → upgini-1.1.309a1}/src/upgini/version_validator.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.1.309a1"
|
|
@@ -1,32 +1,24 @@
|
|
|
1
1
|
import csv
|
|
2
|
-
import hashlib
|
|
3
2
|
import logging
|
|
4
3
|
import tempfile
|
|
5
4
|
import time
|
|
6
|
-
from ipaddress import IPv4Address, IPv6Address, _BaseAddress, ip_address
|
|
7
5
|
from pathlib import Path
|
|
8
|
-
from typing import Any, Callable, Dict, List, Optional, Tuple
|
|
6
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple
|
|
9
7
|
|
|
10
8
|
import numpy as np
|
|
11
9
|
import pandas as pd
|
|
12
|
-
from pandas.api.types import is_bool_dtype as is_bool
|
|
13
|
-
from pandas.api.types import is_datetime64_any_dtype as is_datetime
|
|
14
10
|
from pandas.api.types import (
|
|
15
11
|
is_float_dtype,
|
|
16
12
|
is_integer_dtype,
|
|
17
13
|
is_numeric_dtype,
|
|
18
14
|
is_object_dtype,
|
|
19
|
-
is_period_dtype,
|
|
20
15
|
is_string_dtype,
|
|
21
16
|
)
|
|
22
17
|
|
|
23
18
|
from upgini.errors import ValidationError
|
|
24
19
|
from upgini.http import ProgressStage, SearchProgress, _RestClient
|
|
25
20
|
from upgini.metadata import (
|
|
26
|
-
ENTITY_SYSTEM_RECORD_ID,
|
|
27
21
|
EVAL_SET_INDEX,
|
|
28
|
-
SEARCH_KEY_UNNEST,
|
|
29
|
-
SYSTEM_COLUMNS,
|
|
30
22
|
SYSTEM_RECORD_ID,
|
|
31
23
|
TARGET,
|
|
32
24
|
DataType,
|
|
@@ -40,10 +32,8 @@ from upgini.metadata import (
|
|
|
40
32
|
RuntimeParameters,
|
|
41
33
|
SearchCustomization,
|
|
42
34
|
)
|
|
43
|
-
from upgini.normalizer.phone_normalizer import PhoneNormalizer
|
|
44
35
|
from upgini.resource_bundle import ResourceBundle, get_custom_bundle
|
|
45
36
|
from upgini.search_task import SearchTask
|
|
46
|
-
from upgini.utils import combine_search_keys, find_numbers_with_decimal_comma
|
|
47
37
|
from upgini.utils.email_utils import EmailSearchKeyConverter
|
|
48
38
|
from upgini.utils.target_utils import balance_undersample
|
|
49
39
|
|
|
@@ -117,7 +107,6 @@ class Dataset: # (pd.DataFrame):
|
|
|
117
107
|
self.meaning_types = meaning_types
|
|
118
108
|
self.search_keys = search_keys
|
|
119
109
|
self.unnest_search_keys = unnest_search_keys
|
|
120
|
-
self.ignore_columns = []
|
|
121
110
|
self.hierarchical_group_keys = []
|
|
122
111
|
self.hierarchical_subgroup_keys = []
|
|
123
112
|
self.file_upload_id: Optional[str] = None
|
|
@@ -171,241 +160,6 @@ class Dataset: # (pd.DataFrame):
|
|
|
171
160
|
if len(self.data) > self.MAX_ROWS:
|
|
172
161
|
raise ValidationError(self.bundle.get("dataset_too_many_rows_registered").format(self.MAX_ROWS))
|
|
173
162
|
|
|
174
|
-
def __rename_columns(self):
|
|
175
|
-
# self.logger.info("Replace restricted symbols in column names")
|
|
176
|
-
new_columns = []
|
|
177
|
-
dup_counter = 0
|
|
178
|
-
for column in self.data.columns:
|
|
179
|
-
if column in [TARGET, EVAL_SET_INDEX, SYSTEM_RECORD_ID, ENTITY_SYSTEM_RECORD_ID, SEARCH_KEY_UNNEST]:
|
|
180
|
-
self.columns_renaming[column] = column
|
|
181
|
-
new_columns.append(column)
|
|
182
|
-
continue
|
|
183
|
-
|
|
184
|
-
new_column = str(column)
|
|
185
|
-
suffix = hashlib.sha256(new_column.encode()).hexdigest()[:6]
|
|
186
|
-
if len(new_column) == 0:
|
|
187
|
-
raise ValidationError(self.bundle.get("dataset_empty_column_names"))
|
|
188
|
-
# db limit for column length
|
|
189
|
-
if len(new_column) > 250:
|
|
190
|
-
new_column = new_column[:250]
|
|
191
|
-
|
|
192
|
-
# make column name unique relative to server features
|
|
193
|
-
new_column = f"{new_column}_{suffix}"
|
|
194
|
-
|
|
195
|
-
new_column = new_column.lower()
|
|
196
|
-
|
|
197
|
-
# if column starts with non alphabetic symbol then add "a" to the beginning of string
|
|
198
|
-
if ord(new_column[0]) not in range(ord("a"), ord("z") + 1):
|
|
199
|
-
new_column = "a" + new_column
|
|
200
|
-
|
|
201
|
-
# replace unsupported characters to "_"
|
|
202
|
-
for idx, c in enumerate(new_column):
|
|
203
|
-
if ord(c) not in range(ord("a"), ord("z") + 1) and ord(c) not in range(ord("0"), ord("9") + 1):
|
|
204
|
-
new_column = new_column[:idx] + "_" + new_column[idx + 1 :]
|
|
205
|
-
|
|
206
|
-
if new_column in new_columns:
|
|
207
|
-
new_column = f"{new_column}_{dup_counter}"
|
|
208
|
-
dup_counter += 1
|
|
209
|
-
new_columns.append(new_column)
|
|
210
|
-
|
|
211
|
-
# self.data.columns.values[col_idx] = new_column
|
|
212
|
-
# self.rename(columns={column: new_column}, inplace=True)
|
|
213
|
-
self.meaning_types = {
|
|
214
|
-
(new_column if key == str(column) else key): value for key, value in self.meaning_types_checked.items()
|
|
215
|
-
}
|
|
216
|
-
self.search_keys = [
|
|
217
|
-
tuple(new_column if key == str(column) else key for key in keys) for keys in self.search_keys_checked
|
|
218
|
-
]
|
|
219
|
-
self.columns_renaming[new_column] = str(column)
|
|
220
|
-
self.data.columns = new_columns
|
|
221
|
-
self.etalon_def = None
|
|
222
|
-
|
|
223
|
-
def __validate_too_long_string_values(self):
|
|
224
|
-
"""Check that string values less than maximum characters for LLM"""
|
|
225
|
-
# self.logger.info("Validate too long string values")
|
|
226
|
-
for col in self.data.columns:
|
|
227
|
-
if is_string_dtype(self.data[col]) or is_object_dtype(self.data[col]):
|
|
228
|
-
max_length: int = self.data[col].astype("str").str.len().max()
|
|
229
|
-
if max_length > self.MAX_STRING_FEATURE_LENGTH:
|
|
230
|
-
self.data[col] = self.data[col].astype("str").str.slice(stop=self.MAX_STRING_FEATURE_LENGTH)
|
|
231
|
-
|
|
232
|
-
def __convert_bools(self):
|
|
233
|
-
"""Convert bool columns to string"""
|
|
234
|
-
# self.logger.info("Converting bool to int")
|
|
235
|
-
for col in self.data.columns:
|
|
236
|
-
if is_bool(self.data[col]):
|
|
237
|
-
self.data[col] = self.data[col].astype("str")
|
|
238
|
-
|
|
239
|
-
def __convert_float16(self):
|
|
240
|
-
"""Convert float16 to float"""
|
|
241
|
-
# self.logger.info("Converting float16 to float")
|
|
242
|
-
for col in self.data.columns:
|
|
243
|
-
if is_float_dtype(self.data[col]):
|
|
244
|
-
self.data[col] = self.data[col].astype("float64")
|
|
245
|
-
|
|
246
|
-
def __correct_decimal_comma(self):
|
|
247
|
-
"""Check DataSet for decimal commas and fix them"""
|
|
248
|
-
# self.logger.info("Correct decimal commas")
|
|
249
|
-
columns_to_fix = find_numbers_with_decimal_comma(self.data)
|
|
250
|
-
if len(columns_to_fix) > 0:
|
|
251
|
-
self.logger.warning(f"Convert strings with decimal comma to float: {columns_to_fix}")
|
|
252
|
-
for col in columns_to_fix:
|
|
253
|
-
self.data[col] = self.data[col].astype("string").str.replace(",", ".", regex=False).astype(np.float64)
|
|
254
|
-
|
|
255
|
-
@staticmethod
|
|
256
|
-
def _ip_to_int(ip: Optional[_BaseAddress]) -> Optional[int]:
|
|
257
|
-
try:
|
|
258
|
-
if isinstance(ip, (IPv4Address, IPv6Address)):
|
|
259
|
-
return int(ip)
|
|
260
|
-
except Exception:
|
|
261
|
-
pass
|
|
262
|
-
|
|
263
|
-
@staticmethod
|
|
264
|
-
def _ip_to_int_str(ip: Optional[_BaseAddress]) -> Optional[str]:
|
|
265
|
-
try:
|
|
266
|
-
if isinstance(ip, (IPv4Address, IPv6Address)):
|
|
267
|
-
return str(int(ip))
|
|
268
|
-
except Exception:
|
|
269
|
-
pass
|
|
270
|
-
|
|
271
|
-
@staticmethod
|
|
272
|
-
def _safe_ip_parse(ip: Union[str, int, IPv4Address, IPv6Address]) -> Optional[_BaseAddress]:
|
|
273
|
-
try:
|
|
274
|
-
return ip_address(ip)
|
|
275
|
-
except ValueError:
|
|
276
|
-
pass
|
|
277
|
-
|
|
278
|
-
@staticmethod
|
|
279
|
-
def _is_ipv4(ip: Optional[_BaseAddress]):
|
|
280
|
-
return ip is not None and (
|
|
281
|
-
isinstance(ip, IPv4Address) or (isinstance(ip, IPv6Address) and ip.ipv4_mapped is not None)
|
|
282
|
-
)
|
|
283
|
-
|
|
284
|
-
@staticmethod
|
|
285
|
-
def _to_ipv4(ip: Optional[_BaseAddress]) -> Optional[IPv4Address]:
|
|
286
|
-
if isinstance(ip, IPv4Address):
|
|
287
|
-
return ip
|
|
288
|
-
return None
|
|
289
|
-
|
|
290
|
-
@staticmethod
|
|
291
|
-
def _to_ipv6(ip: Optional[_BaseAddress]) -> Optional[IPv6Address]:
|
|
292
|
-
if isinstance(ip, IPv6Address):
|
|
293
|
-
return ip
|
|
294
|
-
if isinstance(ip, IPv4Address):
|
|
295
|
-
return IPv6Address("::ffff:" + str(ip))
|
|
296
|
-
return None
|
|
297
|
-
|
|
298
|
-
def __convert_ip(self):
|
|
299
|
-
"""Convert ip address to int"""
|
|
300
|
-
ip = self.etalon_def_checked.get(FileColumnMeaningType.IP_ADDRESS.value)
|
|
301
|
-
if ip is not None and ip in self.data.columns:
|
|
302
|
-
self.logger.info("Convert ip address to int")
|
|
303
|
-
del self.etalon_def[FileColumnMeaningType.IP_ADDRESS.value]
|
|
304
|
-
del self.meaning_types[ip]
|
|
305
|
-
original_ip = self.columns_renaming[ip]
|
|
306
|
-
del self.columns_renaming[ip]
|
|
307
|
-
|
|
308
|
-
search_keys = set()
|
|
309
|
-
for tup in self.search_keys_checked:
|
|
310
|
-
search_keys.update(tup)
|
|
311
|
-
search_keys.remove(ip)
|
|
312
|
-
|
|
313
|
-
self.data[ip] = self.data[ip].apply(self._safe_ip_parse)
|
|
314
|
-
if self.data[ip].isnull().all():
|
|
315
|
-
raise ValidationError(self.bundle.get("invalid_ip").format(ip))
|
|
316
|
-
|
|
317
|
-
ipv4 = ip + "_v4"
|
|
318
|
-
self.data[ipv4] = self.data[ip].apply(self._to_ipv4).apply(self._ip_to_int).astype("Int64")
|
|
319
|
-
self.meaning_types[ipv4] = FileColumnMeaningType.IP_ADDRESS
|
|
320
|
-
self.etalon_def[FileColumnMeaningType.IP_ADDRESS.value] = ipv4
|
|
321
|
-
search_keys.add(ipv4)
|
|
322
|
-
self.columns_renaming[ipv4] = original_ip
|
|
323
|
-
|
|
324
|
-
ipv6 = ip + "_v6"
|
|
325
|
-
self.data[ipv6] = (
|
|
326
|
-
self.data[ip]
|
|
327
|
-
.apply(self._to_ipv6)
|
|
328
|
-
.apply(self._ip_to_int_str)
|
|
329
|
-
.astype("string")
|
|
330
|
-
# .str.replace(".0", "", regex=False)
|
|
331
|
-
)
|
|
332
|
-
self.data = self.data.drop(columns=ip)
|
|
333
|
-
self.meaning_types[ipv6] = FileColumnMeaningType.IPV6_ADDRESS
|
|
334
|
-
self.etalon_def[FileColumnMeaningType.IPV6_ADDRESS.value] = ipv6
|
|
335
|
-
search_keys.add(ipv6)
|
|
336
|
-
self.columns_renaming[ipv6] = original_ip
|
|
337
|
-
self.search_keys = combine_search_keys(search_keys)
|
|
338
|
-
|
|
339
|
-
def __normalize_iso_code(self):
|
|
340
|
-
iso_code = self.etalon_def_checked.get(FileColumnMeaningType.COUNTRY.value)
|
|
341
|
-
if iso_code is not None and iso_code in self.data.columns:
|
|
342
|
-
# self.logger.info("Normalize iso code column")
|
|
343
|
-
self.data[iso_code] = (
|
|
344
|
-
self.data[iso_code]
|
|
345
|
-
.astype("string")
|
|
346
|
-
.str.upper()
|
|
347
|
-
.str.replace(r"[^A-Z]", "", regex=True)
|
|
348
|
-
.str.replace("UK", "GB", regex=False)
|
|
349
|
-
)
|
|
350
|
-
if (self.data[iso_code] == "").all():
|
|
351
|
-
raise ValidationError(self.bundle.get("invalid_country").format(iso_code))
|
|
352
|
-
|
|
353
|
-
def __normalize_postal_code(self):
|
|
354
|
-
postal_code = self.etalon_def_checked.get(FileColumnMeaningType.POSTAL_CODE.value)
|
|
355
|
-
if postal_code is not None and postal_code in self.data.columns:
|
|
356
|
-
# self.logger.info("Normalize postal code")
|
|
357
|
-
|
|
358
|
-
if is_string_dtype(self.data[postal_code]) or is_object_dtype(self.data[postal_code]):
|
|
359
|
-
try:
|
|
360
|
-
self.data[postal_code] = (
|
|
361
|
-
self.data[postal_code].astype("string").astype("Float64").astype("Int64").astype("string")
|
|
362
|
-
)
|
|
363
|
-
except Exception:
|
|
364
|
-
pass
|
|
365
|
-
elif is_float_dtype(self.data[postal_code]):
|
|
366
|
-
self.data[postal_code] = self.data[postal_code].astype("Int64").astype("string")
|
|
367
|
-
|
|
368
|
-
self.data[postal_code] = (
|
|
369
|
-
self.data[postal_code]
|
|
370
|
-
.astype("string")
|
|
371
|
-
.str.upper()
|
|
372
|
-
.str.replace(r"[^0-9A-Z]", "", regex=True) # remove non alphanumeric characters
|
|
373
|
-
.str.replace(r"^0+\B", "", regex=True) # remove leading zeros
|
|
374
|
-
)
|
|
375
|
-
if (self.data[postal_code] == "").all():
|
|
376
|
-
raise ValidationError(self.bundle.get("invalid_postal_code").format(postal_code))
|
|
377
|
-
|
|
378
|
-
def __normalize_hem(self):
|
|
379
|
-
hem = self.etalon_def_checked.get(FileColumnMeaningType.HEM.value)
|
|
380
|
-
if hem is not None and hem in self.data.columns:
|
|
381
|
-
self.data[hem] = self.data[hem].str.lower()
|
|
382
|
-
|
|
383
|
-
def __remove_old_dates(self, silent_mode: bool = False):
|
|
384
|
-
date_column = self.etalon_def_checked.get(FileColumnMeaningType.DATE.value) or self.etalon_def_checked.get(
|
|
385
|
-
FileColumnMeaningType.DATETIME.value
|
|
386
|
-
)
|
|
387
|
-
if date_column is not None and is_numeric_dtype(self.data[date_column]):
|
|
388
|
-
old_subset = self.data[self.data[date_column] < self.MIN_SUPPORTED_DATE_TS]
|
|
389
|
-
if len(old_subset) > 0:
|
|
390
|
-
self.logger.info(f"df before dropping old rows: {self.data.shape}")
|
|
391
|
-
self.data.drop(index=old_subset.index, inplace=True) # type: ignore
|
|
392
|
-
self.logger.info(f"df after dropping old rows: {self.data.shape}")
|
|
393
|
-
if len(self.data) == 0:
|
|
394
|
-
raise ValidationError(self.bundle.get("dataset_all_dates_old"))
|
|
395
|
-
else:
|
|
396
|
-
msg = self.bundle.get("dataset_drop_old_dates")
|
|
397
|
-
self.logger.warning(msg)
|
|
398
|
-
if not silent_mode:
|
|
399
|
-
print(msg)
|
|
400
|
-
self.warning_counter.increment()
|
|
401
|
-
|
|
402
|
-
def __drop_ignore_columns(self):
|
|
403
|
-
"""Drop ignore columns"""
|
|
404
|
-
columns_to_drop = list(set(self.data.columns) & set(self.ignore_columns))
|
|
405
|
-
if len(columns_to_drop) > 0:
|
|
406
|
-
# self.logger.info(f"Dropping ignore columns: {self.ignore_columns}")
|
|
407
|
-
self.data.drop(columns_to_drop, axis=1, inplace=True)
|
|
408
|
-
|
|
409
163
|
def __target_value(self) -> pd.Series:
|
|
410
164
|
target_column = self.etalon_def_checked.get(FileColumnMeaningType.TARGET.value, "")
|
|
411
165
|
target: pd.Series = self.data[target_column]
|
|
@@ -526,52 +280,6 @@ class Dataset: # (pd.DataFrame):
|
|
|
526
280
|
self.data = resampled_data
|
|
527
281
|
self.logger.info(f"Shape after threshold resampling: {self.data.shape}")
|
|
528
282
|
|
|
529
|
-
def __convert_phone(self):
|
|
530
|
-
"""Convert phone/msisdn to int"""
|
|
531
|
-
# self.logger.info("Convert phone to int")
|
|
532
|
-
msisdn_column = self.etalon_def_checked.get(FileColumnMeaningType.MSISDN.value)
|
|
533
|
-
country_column = self.etalon_def_checked.get(FileColumnMeaningType.COUNTRY.value)
|
|
534
|
-
if msisdn_column is not None and msisdn_column in self.data.columns:
|
|
535
|
-
normalizer = PhoneNormalizer(self.data, msisdn_column, country_column)
|
|
536
|
-
self.data[msisdn_column] = normalizer.normalize()
|
|
537
|
-
if self.data[msisdn_column].isnull().all():
|
|
538
|
-
raise ValidationError(f"All values of PHONE column `{msisdn_column}` are invalid")
|
|
539
|
-
|
|
540
|
-
def __features(self):
|
|
541
|
-
return [
|
|
542
|
-
f for f, meaning_type in self.meaning_types_checked.items() if meaning_type == FileColumnMeaningType.FEATURE
|
|
543
|
-
]
|
|
544
|
-
|
|
545
|
-
def __remove_dates_from_features(self, silent_mode: bool = False):
|
|
546
|
-
# self.logger.info("Remove date columns from features")
|
|
547
|
-
|
|
548
|
-
removed_features = []
|
|
549
|
-
for f in self.__features():
|
|
550
|
-
if is_datetime(self.data[f]) or is_period_dtype(self.data[f]):
|
|
551
|
-
removed_features.append(f)
|
|
552
|
-
self.data.drop(columns=f, inplace=True)
|
|
553
|
-
del self.meaning_types_checked[f]
|
|
554
|
-
|
|
555
|
-
if removed_features:
|
|
556
|
-
msg = self.bundle.get("dataset_date_features").format(removed_features)
|
|
557
|
-
self.logger.warning(msg)
|
|
558
|
-
if not silent_mode:
|
|
559
|
-
print(msg)
|
|
560
|
-
self.warning_counter.increment()
|
|
561
|
-
|
|
562
|
-
def __validate_features_count(self):
|
|
563
|
-
if len(self.__features()) > self.MAX_FEATURES_COUNT:
|
|
564
|
-
msg = self.bundle.get("dataset_too_many_features").format(self.MAX_FEATURES_COUNT)
|
|
565
|
-
self.logger.warning(msg)
|
|
566
|
-
raise ValidationError(msg)
|
|
567
|
-
|
|
568
|
-
def __convert_features_types(self):
|
|
569
|
-
# self.logger.info("Convert features to supported data types")
|
|
570
|
-
|
|
571
|
-
for f in self.__features():
|
|
572
|
-
if not is_numeric_dtype(self.data[f]):
|
|
573
|
-
self.data[f] = self.data[f].astype("string")
|
|
574
|
-
|
|
575
283
|
def __validate_dataset(self, validate_target: bool, silent_mode: bool):
|
|
576
284
|
"""Validate DataSet"""
|
|
577
285
|
# self.logger.info("validating etalon")
|
|
@@ -702,69 +410,7 @@ class Dataset: # (pd.DataFrame):
|
|
|
702
410
|
if len(self.data) == 0:
|
|
703
411
|
raise ValidationError(self.bundle.get("all_search_keys_invalid"))
|
|
704
412
|
|
|
705
|
-
def __validate_meaning_types(self, validate_target: bool):
|
|
706
|
-
# self.logger.info("Validating meaning types")
|
|
707
|
-
if self.meaning_types is None or len(self.meaning_types) == 0:
|
|
708
|
-
raise ValueError(self.bundle.get("dataset_missing_meaning_types"))
|
|
709
|
-
|
|
710
|
-
if SYSTEM_RECORD_ID not in self.data.columns:
|
|
711
|
-
raise ValueError("Internal error")
|
|
712
|
-
|
|
713
|
-
for column in self.meaning_types:
|
|
714
|
-
if column not in self.data.columns:
|
|
715
|
-
raise ValueError(self.bundle.get("dataset_missing_meaning_column").format(column, self.data.columns))
|
|
716
|
-
if validate_target and FileColumnMeaningType.TARGET not in self.meaning_types.values():
|
|
717
|
-
raise ValueError(self.bundle.get("dataset_missing_target"))
|
|
718
|
-
|
|
719
|
-
def __validate_search_keys(self):
|
|
720
|
-
# self.logger.info("Validating search keys")
|
|
721
|
-
if self.search_keys is None or len(self.search_keys) == 0:
|
|
722
|
-
raise ValueError(self.bundle.get("dataset_missing_search_keys"))
|
|
723
|
-
for keys_group in self.search_keys:
|
|
724
|
-
for key in keys_group:
|
|
725
|
-
if key not in self.data.columns:
|
|
726
|
-
showing_columns = set(self.data.columns) - SYSTEM_COLUMNS
|
|
727
|
-
raise ValidationError(
|
|
728
|
-
self.bundle.get("dataset_missing_search_key_column").format(key, showing_columns)
|
|
729
|
-
)
|
|
730
|
-
|
|
731
413
|
def validate(self, validate_target: bool = True, silent_mode: bool = False):
|
|
732
|
-
# self.logger.info("Validating dataset")
|
|
733
|
-
|
|
734
|
-
self.__validate_search_keys()
|
|
735
|
-
|
|
736
|
-
self.__validate_meaning_types(validate_target=validate_target)
|
|
737
|
-
|
|
738
|
-
self.__drop_ignore_columns()
|
|
739
|
-
|
|
740
|
-
self.__rename_columns()
|
|
741
|
-
|
|
742
|
-
self.__remove_dates_from_features(silent_mode)
|
|
743
|
-
|
|
744
|
-
self.__validate_features_count()
|
|
745
|
-
|
|
746
|
-
self.__validate_too_long_string_values()
|
|
747
|
-
|
|
748
|
-
self.__convert_bools()
|
|
749
|
-
|
|
750
|
-
self.__convert_float16()
|
|
751
|
-
|
|
752
|
-
self.__correct_decimal_comma()
|
|
753
|
-
|
|
754
|
-
self.__remove_old_dates(silent_mode)
|
|
755
|
-
|
|
756
|
-
self.__convert_ip()
|
|
757
|
-
|
|
758
|
-
self.__convert_phone()
|
|
759
|
-
|
|
760
|
-
self.__normalize_iso_code()
|
|
761
|
-
|
|
762
|
-
self.__normalize_postal_code()
|
|
763
|
-
|
|
764
|
-
self.__normalize_hem()
|
|
765
|
-
|
|
766
|
-
self.__convert_features_types()
|
|
767
|
-
|
|
768
414
|
self.__validate_dataset(validate_target, silent_mode)
|
|
769
415
|
|
|
770
416
|
if validate_target:
|
|
@@ -782,38 +428,37 @@ class Dataset: # (pd.DataFrame):
|
|
|
782
428
|
# self.logger.info("Constructing dataset metadata")
|
|
783
429
|
columns = []
|
|
784
430
|
for index, (column_name, column_type) in enumerate(zip(self.data.columns, self.data.dtypes)):
|
|
785
|
-
if column_name
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
maxValue=self.data[column_name].astype("Int64").max(),
|
|
801
|
-
)
|
|
802
|
-
else:
|
|
803
|
-
min_max_values = None
|
|
804
|
-
column_meta = FileColumnMetadata(
|
|
805
|
-
index=index,
|
|
806
|
-
name=column_name,
|
|
807
|
-
originalName=self.columns_renaming.get(column_name) or column_name,
|
|
808
|
-
dataType=self.__get_data_type(column_type, column_name),
|
|
809
|
-
meaningType=meaning_type,
|
|
810
|
-
minMaxValues=min_max_values,
|
|
431
|
+
if column_name in self.meaning_types_checked:
|
|
432
|
+
meaning_type = self.meaning_types_checked[column_name]
|
|
433
|
+
# Temporary workaround while backend doesn't support datetime
|
|
434
|
+
if meaning_type == FileColumnMeaningType.DATETIME:
|
|
435
|
+
meaning_type = FileColumnMeaningType.DATE
|
|
436
|
+
else:
|
|
437
|
+
meaning_type = FileColumnMeaningType.FEATURE
|
|
438
|
+
if meaning_type in {
|
|
439
|
+
FileColumnMeaningType.DATE,
|
|
440
|
+
FileColumnMeaningType.DATETIME,
|
|
441
|
+
# FileColumnMeaningType.IP_ADDRESS,
|
|
442
|
+
}:
|
|
443
|
+
min_max_values = NumericInterval(
|
|
444
|
+
minValue=self.data[column_name].astype("Int64").min(),
|
|
445
|
+
maxValue=self.data[column_name].astype("Int64").max(),
|
|
811
446
|
)
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
447
|
+
else:
|
|
448
|
+
min_max_values = None
|
|
449
|
+
column_meta = FileColumnMetadata(
|
|
450
|
+
index=index,
|
|
451
|
+
name=column_name,
|
|
452
|
+
originalName=self.columns_renaming.get(column_name) or column_name,
|
|
453
|
+
dataType=self.__get_data_type(column_type, column_name),
|
|
454
|
+
meaningType=meaning_type,
|
|
455
|
+
minMaxValues=min_max_values,
|
|
456
|
+
)
|
|
457
|
+
if self.unnest_search_keys and column_meta.originalName in self.unnest_search_keys:
|
|
458
|
+
column_meta.isUnnest = True
|
|
459
|
+
column_meta.unnestKeyNames = self.unnest_search_keys[column_meta.originalName]
|
|
815
460
|
|
|
816
|
-
|
|
461
|
+
columns.append(column_meta)
|
|
817
462
|
|
|
818
463
|
return FileMetadata(
|
|
819
464
|
name=self.dataset_name,
|