jiboia 0.1.0__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.
- jiboia-0.1.0/LICENSE +21 -0
- jiboia-0.1.0/PKG-INFO +68 -0
- jiboia-0.1.0/README.md +33 -0
- jiboia-0.1.0/jiboia/__init__.py +87 -0
- jiboia-0.1.0/jiboia/analysis_utils.py +323 -0
- jiboia-0.1.0/jiboia/file_analysis_utils.py +45 -0
- jiboia-0.1.0/jiboia/normalize_date_utils.py +384 -0
- jiboia-0.1.0/jiboia/normalize_df_utils.py +110 -0
- jiboia-0.1.0/jiboia/normalize_float_utils.py +56 -0
- jiboia-0.1.0/jiboia/normalize_int_utils.py +56 -0
- jiboia-0.1.0/jiboia/normalize_null_utils.py +41 -0
- jiboia-0.1.0/jiboia/normalize_string_utils.py +75 -0
- jiboia-0.1.0/jiboia/regex_patterns.py +152 -0
- jiboia-0.1.0/jiboia.egg-info/PKG-INFO +68 -0
- jiboia-0.1.0/jiboia.egg-info/SOURCES.txt +18 -0
- jiboia-0.1.0/jiboia.egg-info/dependency_links.txt +1 -0
- jiboia-0.1.0/jiboia.egg-info/requires.txt +1 -0
- jiboia-0.1.0/jiboia.egg-info/top_level.txt +1 -0
- jiboia-0.1.0/pyproject.toml +25 -0
- jiboia-0.1.0/setup.cfg +4 -0
jiboia-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Lucas M.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
jiboia-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jiboia
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: jiboia is a python package for automatically normalizing DataFrames and optimizing types efficiently.
|
|
5
|
+
Author-email: Lucas Muffato <lucasmuffato.eng@gmail.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2025 Lucas M.
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in
|
|
18
|
+
all copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
26
|
+
THE SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Homepage, https://github.com/lmuffato/jiboia
|
|
29
|
+
Project-URL: Bug_Tracker, https://github.com/lmuffato/jiboia/issues
|
|
30
|
+
Requires-Python: >=3.9
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
License-File: LICENSE
|
|
33
|
+
Requires-Dist: pandas>=2.0
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
# Jiboia
|
|
37
|
+
|
|
38
|
+
**Jiboia** is a Python package for **automatically normalizing and optimizing DataFrames** efficiently.
|
|
39
|
+
|
|
40
|
+
Key features:
|
|
41
|
+
- **String normalization**:
|
|
42
|
+
- Removes extra spaces.
|
|
43
|
+
- Strips leading and trailing spaces.
|
|
44
|
+
- Detects data pollution (e.g., columns that should be numeric but contain strings).
|
|
45
|
+
- **Type conversion**:
|
|
46
|
+
- Numeric strings and floats ending in `.0` → integers (`int8`, `int16`, `int32`, …).
|
|
47
|
+
- Converts floats and integers to the most memory-efficient type.
|
|
48
|
+
- Converts strings in various date formats to `datetime` (`yyyy?mm?dd`, `dd?mm?yyyy`, `yyyymmd`, `dd?mm?yy`).
|
|
49
|
+
- Converts time strings (`hhmm UTC`, `hh:mm:ss`, `hh:mm:ss.s`) to `timedelta`.
|
|
50
|
+
- **Null standardization** → converts different null representations to `pd.NA`.
|
|
51
|
+
- **Automatic CSV detection**:
|
|
52
|
+
- Detects delimiter.
|
|
53
|
+
- Detects encoding.
|
|
54
|
+
- **Memory optimization**:
|
|
55
|
+
- Provides memory usage information for DataFrames.
|
|
56
|
+
- Converts columns to the most compact types possible.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Example Usage
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
import jiboia as jb
|
|
64
|
+
|
|
65
|
+
df = pd.read_csv("data.csv")
|
|
66
|
+
df = jb.normalize_df(df)
|
|
67
|
+
|
|
68
|
+
```
|
jiboia-0.1.0/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Jiboia
|
|
2
|
+
|
|
3
|
+
**Jiboia** is a Python package for **automatically normalizing and optimizing DataFrames** efficiently.
|
|
4
|
+
|
|
5
|
+
Key features:
|
|
6
|
+
- **String normalization**:
|
|
7
|
+
- Removes extra spaces.
|
|
8
|
+
- Strips leading and trailing spaces.
|
|
9
|
+
- Detects data pollution (e.g., columns that should be numeric but contain strings).
|
|
10
|
+
- **Type conversion**:
|
|
11
|
+
- Numeric strings and floats ending in `.0` → integers (`int8`, `int16`, `int32`, …).
|
|
12
|
+
- Converts floats and integers to the most memory-efficient type.
|
|
13
|
+
- Converts strings in various date formats to `datetime` (`yyyy?mm?dd`, `dd?mm?yyyy`, `yyyymmd`, `dd?mm?yy`).
|
|
14
|
+
- Converts time strings (`hhmm UTC`, `hh:mm:ss`, `hh:mm:ss.s`) to `timedelta`.
|
|
15
|
+
- **Null standardization** → converts different null representations to `pd.NA`.
|
|
16
|
+
- **Automatic CSV detection**:
|
|
17
|
+
- Detects delimiter.
|
|
18
|
+
- Detects encoding.
|
|
19
|
+
- **Memory optimization**:
|
|
20
|
+
- Provides memory usage information for DataFrames.
|
|
21
|
+
- Converts columns to the most compact types possible.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Example Usage
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
import jiboia as jb
|
|
29
|
+
|
|
30
|
+
df = pd.read_csv("data.csv")
|
|
31
|
+
df = jb.normalize_df(df)
|
|
32
|
+
|
|
33
|
+
```
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from .analysis_utils import AnalysisUtils
|
|
2
|
+
from .normalize_date_utils import NormalizeDateUtils
|
|
3
|
+
from .normalize_null_utils import NormalizeNullUtils
|
|
4
|
+
from .normalize_int_utils import NormalizeIntUtils
|
|
5
|
+
from .normalize_float_utils import NormalizeFloatUtils
|
|
6
|
+
from .normalize_string_utils import NormalizeStringUtils
|
|
7
|
+
from .normalize_df_utils import NormalizeDfUtils
|
|
8
|
+
from .file_analysis_utils import FileAnalysisUtils
|
|
9
|
+
from .regex_patterns import (
|
|
10
|
+
RegexPatternsCombine,
|
|
11
|
+
RAW_INVALID_VALUES,
|
|
12
|
+
DATE_REGEX_PATTERNS,
|
|
13
|
+
TIME_REGEX_PATTERNS,
|
|
14
|
+
DATE_TIME_REGEX_PATTERNS,
|
|
15
|
+
RAW_INVALID_VALUES
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Jiboia:
|
|
20
|
+
|
|
21
|
+
# DATAFRAME
|
|
22
|
+
normalize_df = staticmethod(NormalizeDfUtils.normalize_df)
|
|
23
|
+
|
|
24
|
+
# TYPES:
|
|
25
|
+
infer_column_types = staticmethod(AnalysisUtils.infer_column_types)
|
|
26
|
+
|
|
27
|
+
# STRING
|
|
28
|
+
normalize_string_by_columns = staticmethod(NormalizeStringUtils.normalize_string_by_columns)
|
|
29
|
+
# normalize_category = staticmethod(NormalizeStringUtils.normalize_category)
|
|
30
|
+
# normalize_unique_values = staticmethod(NormalizeStringUtils.normalize_unique_values)
|
|
31
|
+
|
|
32
|
+
# NULL
|
|
33
|
+
normalize_null_by_columns = staticmethod(NormalizeNullUtils.normalize_null_by_columns)
|
|
34
|
+
|
|
35
|
+
# BOOL
|
|
36
|
+
# normalize_bool = staticmethod(GPUNormalizeBooleanUtils.normalize_bool)
|
|
37
|
+
|
|
38
|
+
# NUMBER
|
|
39
|
+
normalize_int_by_columns = staticmethod(NormalizeIntUtils.normalize_int_by_columns)
|
|
40
|
+
normalize_float_by_columns = staticmethod(NormalizeFloatUtils.normalize_float_by_columns)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# DATE
|
|
44
|
+
normalize_str_date_to_date_standard_str_by_columns = staticmethod(NormalizeDateUtils.normalize_str_date_to_date_standard_str_by_columns)
|
|
45
|
+
# normalize_datetime = staticmethod(NormalizeDateUtils.normalize_datetime)
|
|
46
|
+
# is_date_column_by_samples = staticmethod(NormalizeDateUtils.is_date_column_by_samples)
|
|
47
|
+
# is_date = staticmethod(NormalizeDateUtils.is_date)
|
|
48
|
+
# to_date_iso = staticmethod(NormalizeDateUtils.to_date_iso)
|
|
49
|
+
# to_datetime = staticmethod(NormalizeDateUtils.to_datetime)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# TIME
|
|
53
|
+
normalize_str_time_to_hh_mm_ss_by_columns = staticmethod(NormalizeDateUtils.normalize_str_time_to_hh_mm_ss_by_columns)
|
|
54
|
+
|
|
55
|
+
# normalize_time = staticmethod(GPUNormalizeTimeUtils.normalize_time)
|
|
56
|
+
# is_time = staticmethod(GPUNormalizeTimeUtils.is_time)
|
|
57
|
+
|
|
58
|
+
# # TIMEDELTA
|
|
59
|
+
# normalize_timedelta = staticmethod(GPUNormalizeTimeUtils.normalize_timedelta)
|
|
60
|
+
|
|
61
|
+
# # DATETIME
|
|
62
|
+
# create_datetime_column_from_date_and_time = staticmethod(GPUNormalizeDateTimeUtils.create_datetime_column_from_date_and_time)
|
|
63
|
+
# create_day_week_column = staticmethod(GPUNormalizeDateTimeUtils.create_day_week_column)
|
|
64
|
+
# create_month_br_category_column = staticmethod(GPUNormalizeDateTimeUtils.create_month_br_category_column)
|
|
65
|
+
# create_day_week_br_category_column = staticmethod(GPUNormalizeDateTimeUtils.create_day_week_br_category_column)
|
|
66
|
+
# create_hour_category_column = staticmethod(GPUNormalizeDateTimeUtils.create_hour_category_column)
|
|
67
|
+
|
|
68
|
+
# ANALYSIS
|
|
69
|
+
# drop_columns = staticmethod(AnalysisUtils.drop_columns)
|
|
70
|
+
# cudf_size_info = staticmethod(AnalysisUtils.cudf_size_info)
|
|
71
|
+
# pandasdf_size_info = staticmethod(AnalysisUtils.pandasdf_size_info)
|
|
72
|
+
# get_gpu_memory_info = staticmethod(AnalysisUtils.get_gpu_memory_info)
|
|
73
|
+
# is_vram_use_limit = staticmethod(AnalysisUtils.is_vram_use_limit)
|
|
74
|
+
# get_index_samples = staticmethod(AnalysisUtils.get_index_samples)
|
|
75
|
+
# infer_by_sample = staticmethod(AnalysisUtils.infer_by_sample)
|
|
76
|
+
|
|
77
|
+
# # FILE
|
|
78
|
+
get_csv_sep_delimiter = staticmethod(FileAnalysisUtils.get_csv_sep_delimiter)
|
|
79
|
+
# get_csv_info = staticmethod(FileAnalysisUtils.get_csv_info)
|
|
80
|
+
# convert_csv_to_utf8 = staticmethod(FileAnalysisUtils.convert_csv_to_utf8)
|
|
81
|
+
# convert_all_csvs_to_utf8 = staticmethod(FileAnalysisUtils.convert_all_csvs_to_utf8)
|
|
82
|
+
# read_csv_files = staticmethod(FileAnalysisUtils.read_csv_files)
|
|
83
|
+
|
|
84
|
+
# instância única para ser usada como namespace
|
|
85
|
+
jiboia = Jiboia()
|
|
86
|
+
|
|
87
|
+
__all__ = ["jiboia", "Jiboia"]
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
from csv import Dialect
|
|
2
|
+
from datetime import datetime, time
|
|
3
|
+
from pandas.api.types import CategoricalDtype
|
|
4
|
+
from typing import Literal, Optional
|
|
5
|
+
from .regex_patterns import (
|
|
6
|
+
DATE_REGEX_PATTERNS,
|
|
7
|
+
TIME_REGEX_PATTERNS,
|
|
8
|
+
DATE_TIME_REGEX_PATTERNS,
|
|
9
|
+
)
|
|
10
|
+
import csv
|
|
11
|
+
import numpy as np
|
|
12
|
+
import pandas as pd
|
|
13
|
+
import re
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AnalysisUtils:
|
|
17
|
+
data_types = Literal['int', 'float', 'bool', 'date', 'time', 'datetime', 'str']
|
|
18
|
+
|
|
19
|
+
@staticmethod
|
|
20
|
+
def _number_cleaned(value: object) -> str:
|
|
21
|
+
|
|
22
|
+
value_str: str = str(value).strip()
|
|
23
|
+
|
|
24
|
+
if '.' in value_str and ',' in value_str:
|
|
25
|
+
# formato 1000,00 -> 1000.00
|
|
26
|
+
value_str: str = value_str.replace('.', '').replace(',', '.')
|
|
27
|
+
|
|
28
|
+
if ',' in value_str:
|
|
29
|
+
# formato 1.000,00 -> 1000.00
|
|
30
|
+
value_str: str = value_str.replace(',', '.')
|
|
31
|
+
|
|
32
|
+
return value_str
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@staticmethod
|
|
36
|
+
def _is_zero(value: object) -> bool:
|
|
37
|
+
cleaned_number_str: str = AnalysisUtils._number_cleaned(value)
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
float_value: float = float(cleaned_number_str)
|
|
41
|
+
|
|
42
|
+
if float_value == 0.0:
|
|
43
|
+
return True
|
|
44
|
+
else:
|
|
45
|
+
return False
|
|
46
|
+
except ValueError:
|
|
47
|
+
return False
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@staticmethod
|
|
51
|
+
def _is_int(value: object) -> bool:
|
|
52
|
+
cleaned_number_str: str = AnalysisUtils._number_cleaned(value)
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
float_value: float = float(cleaned_number_str)
|
|
56
|
+
|
|
57
|
+
if float_value.is_integer():
|
|
58
|
+
return True
|
|
59
|
+
else:
|
|
60
|
+
return False
|
|
61
|
+
except ValueError:
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@staticmethod
|
|
66
|
+
def _is_float(value: object) -> bool:
|
|
67
|
+
'''
|
|
68
|
+
Retorna True se o valor puder ser interpretado como float,
|
|
69
|
+
incluindo formatos com separadores de milhar e vírgula como separador decimal.
|
|
70
|
+
'''
|
|
71
|
+
cleaned_number_str: str = AnalysisUtils._number_cleaned(value)
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
float_value: float = float(cleaned_number_str)
|
|
75
|
+
|
|
76
|
+
if not float_value.is_integer():
|
|
77
|
+
return True
|
|
78
|
+
else:
|
|
79
|
+
return False
|
|
80
|
+
except ValueError:
|
|
81
|
+
return False
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@staticmethod
|
|
85
|
+
def _is_bool(value: object) -> bool:
|
|
86
|
+
"""
|
|
87
|
+
Retorna True se o valor puder ser interpretado como booleano.
|
|
88
|
+
Considera 'true', 'false', '1', '0' (case-insensitive), além de bool nativo.
|
|
89
|
+
"""
|
|
90
|
+
value_str = str(value).strip().lower()
|
|
91
|
+
return value_str in {'True', 'False', 'true', 'false', }
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@staticmethod
|
|
95
|
+
def _is_date(value: object) -> bool:
|
|
96
|
+
value_str: str = str(value)
|
|
97
|
+
|
|
98
|
+
for pattern, _ in DATE_REGEX_PATTERNS:
|
|
99
|
+
if re.fullmatch(pattern, value_str):
|
|
100
|
+
return True
|
|
101
|
+
|
|
102
|
+
return False
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _is_time(value: object) -> bool:
|
|
106
|
+
value_str = str(value)
|
|
107
|
+
|
|
108
|
+
for pattern, _ in TIME_REGEX_PATTERNS:
|
|
109
|
+
if re.fullmatch(pattern, value_str):
|
|
110
|
+
return True
|
|
111
|
+
|
|
112
|
+
return False
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@staticmethod
|
|
116
|
+
def _is_datetime(value: object) -> bool:
|
|
117
|
+
"""
|
|
118
|
+
Verifica se o valor corresponde a um dos formatos datetime definidos em DATE_TIME_REGEX_PATTERNS.
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
value_str = str(value)
|
|
122
|
+
|
|
123
|
+
for pattern, date_format in DATE_TIME_REGEX_PATTERNS:
|
|
124
|
+
if re.fullmatch(pattern, value_str):
|
|
125
|
+
try:
|
|
126
|
+
datetime.strptime(value_str, date_format)
|
|
127
|
+
return True
|
|
128
|
+
except ValueError:
|
|
129
|
+
continue
|
|
130
|
+
|
|
131
|
+
return False
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@staticmethod
|
|
135
|
+
def _detect_type(value: object) -> Optional[Literal['int', 'float', 'bool', 'date', 'time', 'str', 'zero']]:
|
|
136
|
+
if pd.isna(value):
|
|
137
|
+
return None
|
|
138
|
+
|
|
139
|
+
elif AnalysisUtils._is_zero(value):
|
|
140
|
+
return 'zero'
|
|
141
|
+
|
|
142
|
+
elif AnalysisUtils._is_int(value):
|
|
143
|
+
return 'int'
|
|
144
|
+
|
|
145
|
+
elif AnalysisUtils._is_float(value):
|
|
146
|
+
return 'float'
|
|
147
|
+
|
|
148
|
+
elif AnalysisUtils._is_time(value):
|
|
149
|
+
return 'time'
|
|
150
|
+
|
|
151
|
+
elif AnalysisUtils._is_date(value):
|
|
152
|
+
return 'date'
|
|
153
|
+
|
|
154
|
+
elif AnalysisUtils._is_bool(value):
|
|
155
|
+
return 'bool'
|
|
156
|
+
|
|
157
|
+
elif AnalysisUtils._is_datetime(value):
|
|
158
|
+
return 'datetime'
|
|
159
|
+
|
|
160
|
+
else:
|
|
161
|
+
return 'str'
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@staticmethod
|
|
165
|
+
def get_dominant_type(
|
|
166
|
+
non_null_rows_sample: pd.Series,
|
|
167
|
+
without_zero: bool = False
|
|
168
|
+
) -> Literal['int', 'float', 'bool', 'date', 'time', 'datetime', 'str', 'None', 'zero']:
|
|
169
|
+
|
|
170
|
+
type_frequency: pd.Series = (
|
|
171
|
+
non_null_rows_sample
|
|
172
|
+
.apply(AnalysisUtils._detect_type)
|
|
173
|
+
.value_counts()
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
if without_zero and 'zero' in type_frequency:
|
|
177
|
+
type_frequency: pd.Series = type_frequency.drop(labels='zero')
|
|
178
|
+
|
|
179
|
+
# Verifica a frequência de "float"
|
|
180
|
+
float_count = type_frequency.get('float', 0)
|
|
181
|
+
total_count = len(non_null_rows_sample)
|
|
182
|
+
|
|
183
|
+
# TODO: Rever isso para analisar toda a coluna em caso de float e int
|
|
184
|
+
# Se pelo menos 1% dos valores forem float, considera o tipo dominante como float
|
|
185
|
+
if float_count / total_count >= 0.1:
|
|
186
|
+
dominant_type = 'float'
|
|
187
|
+
else:
|
|
188
|
+
# Caso contrário, pega o tipo mais frequente
|
|
189
|
+
dominant_type = type_frequency.idxmax()
|
|
190
|
+
|
|
191
|
+
return dominant_type
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@staticmethod
|
|
195
|
+
def sample_size(
|
|
196
|
+
current_df: pd.DataFrame,
|
|
197
|
+
rows_percent: int = 2
|
|
198
|
+
) -> int:
|
|
199
|
+
if not (0 <= rows_percent <= 100):
|
|
200
|
+
raise ValueError('rows_percent deve estar entre 0 e 100')
|
|
201
|
+
|
|
202
|
+
row_count: int = len(current_df)
|
|
203
|
+
sample_size: int = max(1, int((rows_percent / 100) * row_count))
|
|
204
|
+
|
|
205
|
+
return sample_size
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
@staticmethod
|
|
209
|
+
def infer_column_types(
|
|
210
|
+
current_df: pd.DataFrame,
|
|
211
|
+
rows_percent: int = 2,
|
|
212
|
+
) -> pd.DataFrame:
|
|
213
|
+
|
|
214
|
+
sample_size: int = AnalysisUtils.sample_size(current_df, rows_percent)
|
|
215
|
+
|
|
216
|
+
columns: list[str] = current_df.columns.tolist()
|
|
217
|
+
|
|
218
|
+
inferred_types: list[str] = []
|
|
219
|
+
|
|
220
|
+
for column in columns:
|
|
221
|
+
|
|
222
|
+
non_null_rows_sample: pd.Series = (
|
|
223
|
+
current_df[column]
|
|
224
|
+
.dropna()
|
|
225
|
+
.head(sample_size)
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
if non_null_rows_sample.empty:
|
|
229
|
+
dominant_type: str = 'None'
|
|
230
|
+
else:
|
|
231
|
+
dominant_type: str = AnalysisUtils.get_dominant_type(non_null_rows_sample)
|
|
232
|
+
|
|
233
|
+
# Reanalisa as colunas numéricas caso haja dúvida
|
|
234
|
+
if dominant_type == 'zero':
|
|
235
|
+
full_non_null_column: pd.Series = current_df[column].dropna()
|
|
236
|
+
dominant_type = AnalysisUtils.get_dominant_type(
|
|
237
|
+
non_null_rows_sample=full_non_null_column,
|
|
238
|
+
without_zero=True
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
inferred_types.append(dominant_type)
|
|
242
|
+
|
|
243
|
+
df_types: pd.DataFrame = pd.DataFrame({
|
|
244
|
+
'column_name': columns,
|
|
245
|
+
'type': inferred_types
|
|
246
|
+
})
|
|
247
|
+
print(df_types)
|
|
248
|
+
|
|
249
|
+
return df_types
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
@staticmethod
|
|
253
|
+
def get_sep_delimiter(
|
|
254
|
+
file_path: str,
|
|
255
|
+
encoding: Optional[str] = 'latin1',
|
|
256
|
+
skiprows: Optional[int] = 0
|
|
257
|
+
) -> str:
|
|
258
|
+
sample_characters: int = 2048
|
|
259
|
+
|
|
260
|
+
with open(file_path, 'r', encoding=encoding) as file:
|
|
261
|
+
for _ in range(skiprows):
|
|
262
|
+
next(file) # pula as linhas iniciais
|
|
263
|
+
|
|
264
|
+
sample: str = file.read(sample_characters)
|
|
265
|
+
sniffer: csv.Sniffer = csv.Sniffer()
|
|
266
|
+
dialect: Dialect = sniffer.sniff(sample)
|
|
267
|
+
|
|
268
|
+
# with open(file_path, 'r', encoding=encoding) as file:
|
|
269
|
+
# sample: str = file.read(sample_characters)
|
|
270
|
+
# sniffer: csv.Sniffer = csv.Sniffer()
|
|
271
|
+
# dialect: Dialect = sniffer.sniff(sample)
|
|
272
|
+
|
|
273
|
+
print(f'sep_delimiter: "{dialect.delimiter}"')
|
|
274
|
+
return dialect.delimiter
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
@staticmethod
|
|
278
|
+
def get_frequency_by_column(current_df: pd.DataFrame, column: str) -> pd.DataFrame:
|
|
279
|
+
'''
|
|
280
|
+
Gera um current_df com a frequencia de valores de uma coluna
|
|
281
|
+
'''
|
|
282
|
+
current_df_frequency: pd.DataFrame = (
|
|
283
|
+
current_df[column]
|
|
284
|
+
.value_counts()
|
|
285
|
+
.reset_index()
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
current_df_frequency.columns = [column, 'frequency']
|
|
289
|
+
|
|
290
|
+
current_df_frequency: pd.DataFrame = (
|
|
291
|
+
current_df_frequency
|
|
292
|
+
.sort_values(
|
|
293
|
+
by='frequency',
|
|
294
|
+
ascending=False
|
|
295
|
+
)
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
return current_df_frequency
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
@staticmethod
|
|
302
|
+
def get_unique_values(current_df: pd.DataFrame, column: str) -> pd.DataFrame:
|
|
303
|
+
'''
|
|
304
|
+
Retorna um dataframe com os valores únicos de uma coluna
|
|
305
|
+
'''
|
|
306
|
+
df_unique = pd.DataFrame({
|
|
307
|
+
column: current_df[column].dropna().unique()
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
df_unique = df_unique.sort_values(
|
|
311
|
+
by=column,
|
|
312
|
+
ascending=True
|
|
313
|
+
).reset_index(drop=True)
|
|
314
|
+
|
|
315
|
+
return df_unique
|
|
316
|
+
|
|
317
|
+
@staticmethod
|
|
318
|
+
def df_size_info(current_df: pd.DataFrame) -> None:
|
|
319
|
+
return {
|
|
320
|
+
"rows": current_df.shape[0],
|
|
321
|
+
"columns": current_df.shape[1],
|
|
322
|
+
"RAM MB": round(current_df.memory_usage(index=True, deep=True).sum() / (1024 * 1024), 2)
|
|
323
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from csv import Dialect
|
|
2
|
+
from datetime import datetime, time
|
|
3
|
+
from pandas.api.types import CategoricalDtype
|
|
4
|
+
from typing import Literal, Optional
|
|
5
|
+
from .regex_patterns import (
|
|
6
|
+
DATE_REGEX_PATTERNS,
|
|
7
|
+
TIME_REGEX_PATTERNS,
|
|
8
|
+
DATE_TIME_REGEX_PATTERNS,
|
|
9
|
+
)
|
|
10
|
+
import csv
|
|
11
|
+
import numpy as np
|
|
12
|
+
import pandas as pd
|
|
13
|
+
import re
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
import chardet
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class FileAnalysisUtils:
|
|
20
|
+
@staticmethod
|
|
21
|
+
def get_csv_sep_delimiter(
|
|
22
|
+
file_path: str,
|
|
23
|
+
encoding: Optional[str] = 'latin1',
|
|
24
|
+
skiprows: Optional[int] = 0
|
|
25
|
+
) -> str:
|
|
26
|
+
sample_characters: int = 2048
|
|
27
|
+
|
|
28
|
+
with open(file_path, 'r', encoding=encoding) as file:
|
|
29
|
+
for _ in range(skiprows):
|
|
30
|
+
next(file) # pula as linhas iniciais
|
|
31
|
+
|
|
32
|
+
sample: str = file.read(sample_characters)
|
|
33
|
+
sniffer: csv.Sniffer = csv.Sniffer()
|
|
34
|
+
dialect: Dialect = sniffer.sniff(sample)
|
|
35
|
+
|
|
36
|
+
with open(file_path, "rb") as f:
|
|
37
|
+
rawdata = f.read(100000) # lê parte do arquivo
|
|
38
|
+
result = chardet.detect(rawdata)
|
|
39
|
+
print(result)
|
|
40
|
+
|
|
41
|
+
print(f'sep_delimiter: "{dialect.delimiter}"')
|
|
42
|
+
return dialect.delimiter
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|