jiboia-gpu 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_gpu-0.1.0/LICENSE +24 -0
- jiboia_gpu-0.1.0/PKG-INFO +70 -0
- jiboia_gpu-0.1.0/README.md +32 -0
- jiboia_gpu-0.1.0/jiboia_gpu/__init__.py +70 -0
- jiboia_gpu-0.1.0/jiboia_gpu/analysis_utils.py +274 -0
- jiboia_gpu-0.1.0/jiboia_gpu/file_analysis_utils.py +179 -0
- jiboia_gpu-0.1.0/jiboia_gpu/gpu_analysis_utils.py +298 -0
- jiboia_gpu-0.1.0/jiboia_gpu/gpu_file_analysis_utils.py +227 -0
- jiboia_gpu-0.1.0/jiboia_gpu/gpu_normalize_boolean_utils.py +138 -0
- jiboia_gpu-0.1.0/jiboia_gpu/gpu_normalize_date_utils.py +857 -0
- jiboia_gpu-0.1.0/jiboia_gpu/gpu_normalize_datetime_utils.py +418 -0
- jiboia_gpu-0.1.0/jiboia_gpu/gpu_normalize_null_utils.py +236 -0
- jiboia_gpu-0.1.0/jiboia_gpu/gpu_normalize_numeric_utils.py +433 -0
- jiboia_gpu-0.1.0/jiboia_gpu/gpu_normalize_string_utils.py +530 -0
- jiboia_gpu-0.1.0/jiboia_gpu/gpu_normalize_time_utils.py +419 -0
- jiboia_gpu-0.1.0/jiboia_gpu/regex_patterns.py +152 -0
- jiboia_gpu-0.1.0/jiboia_gpu.egg-info/PKG-INFO +70 -0
- jiboia_gpu-0.1.0/jiboia_gpu.egg-info/SOURCES.txt +21 -0
- jiboia_gpu-0.1.0/jiboia_gpu.egg-info/dependency_links.txt +1 -0
- jiboia_gpu-0.1.0/jiboia_gpu.egg-info/requires.txt +1 -0
- jiboia_gpu-0.1.0/jiboia_gpu.egg-info/top_level.txt +1 -0
- jiboia_gpu-0.1.0/pyproject.toml +25 -0
- jiboia_gpu-0.1.0/setup.cfg +4 -0
jiboia_gpu-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
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.
|
|
22
|
+
|
|
23
|
+
USE OF THIS SOFTWARE IS AT YOUR OWN RISK. THE AUTHORS PROVIDE NO WARRANTIES,
|
|
24
|
+
AND ARE NOT LIABLE FOR ANY DAMAGES, DIRECT OR INDIRECT, ARISING FROM ITS USE.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jiboia_gpu
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: jiboia_gpu 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
|
+
USE OF THIS SOFTWARE IS AT YOUR OWN RISK. THE AUTHORS PROVIDE NO WARRANTIES,
|
|
29
|
+
AND ARE NOT LIABLE FOR ANY DAMAGES, DIRECT OR INDIRECT, ARISING FROM ITS USE.
|
|
30
|
+
|
|
31
|
+
Project-URL: Homepage, https://github.com/lmuffato/jiboia_gpu
|
|
32
|
+
Project-URL: Bug_Tracker, https://github.com/lmuffato/jiboia_gpu/issues
|
|
33
|
+
Requires-Python: >=3.9
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
License-File: LICENSE
|
|
36
|
+
Requires-Dist: cudf-cu12>=25.8
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
|
|
39
|
+
# Jiboia
|
|
40
|
+
|
|
41
|
+
**jiboia-gpu** is a Python package to **normalize and optimize DataFrames automatically** efficiently using the Nvidia GPU in the RAPIDS ecosystem.
|
|
42
|
+
|
|
43
|
+
Key features:
|
|
44
|
+
- **String normalization**:
|
|
45
|
+
- Removes extra spaces.
|
|
46
|
+
- Strips leading and trailing spaces.
|
|
47
|
+
- Detects data pollution (e.g., columns that should be numeric but contain strings).
|
|
48
|
+
- **Type conversion**:
|
|
49
|
+
- Numeric strings and floats ending in `.0` → integers (`int8`, `int16`, `int32`, …).
|
|
50
|
+
- Converts floats and integers to the most memory-efficient type.
|
|
51
|
+
- Converts strings in various date formats to `datetime` (`yyyy?mm?dd`, `dd?mm?yyyy`, `yyyymmd`, `dd?mm?yy`).
|
|
52
|
+
- Converts time strings (`hhmm UTC`, `hh:mm:ss`, `hh:mm:ss.s`) to `timedelta`.
|
|
53
|
+
- **Null standardization** → converts different null representations to `pd.NA`.
|
|
54
|
+
- **Automatic CSV detection**:
|
|
55
|
+
- Detects delimiter.
|
|
56
|
+
- Detects encoding.
|
|
57
|
+
- **Memory optimization**:
|
|
58
|
+
- Provides memory usage information for DataFrames.
|
|
59
|
+
- Converts columns to the most compact types possible.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Example Usage
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from jiboia-gpu import jiboia_gpu as jb
|
|
67
|
+
|
|
68
|
+
pd.normalize_category("data_frame_cudf")
|
|
69
|
+
|
|
70
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Jiboia
|
|
2
|
+
|
|
3
|
+
**jiboia-gpu** is a Python package to **normalize and optimize DataFrames automatically** efficiently using the Nvidia GPU in the RAPIDS ecosystem.
|
|
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
|
+
from jiboia-gpu import jiboia_gpu as jb
|
|
29
|
+
|
|
30
|
+
pd.normalize_category("data_frame_cudf")
|
|
31
|
+
|
|
32
|
+
```
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from .gpu_normalize_string_utils import GPUNormalizeStringUtils
|
|
2
|
+
from .gpu_normalize_null_utils import GPUNormalizeNullUtils
|
|
3
|
+
from .gpu_analysis_utils import GPUAnalysisUtils
|
|
4
|
+
from .gpu_normalize_date_utils import GPUNormalizeDateUtils
|
|
5
|
+
from .gpu_normalize_time_utils import GPUNormalizeTimeUtils
|
|
6
|
+
from .gpu_normalize_datetime_utils import GPUNormalizeDateTimeUtils
|
|
7
|
+
from .gpu_normalize_boolean_utils import GPUNormalizeBooleanUtils
|
|
8
|
+
from .gpu_normalize_numeric_utils import GPUNormalizeNumericUtils
|
|
9
|
+
from .gpu_file_analysis_utils import GPUFileAnalysisUtils
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class JiboiaGPU:
|
|
13
|
+
# STRING
|
|
14
|
+
normalize_str = staticmethod(GPUNormalizeStringUtils.normalize_str)
|
|
15
|
+
to_category = staticmethod(GPUNormalizeStringUtils.to_category)
|
|
16
|
+
normalize_category = staticmethod(GPUNormalizeStringUtils.normalize_category)
|
|
17
|
+
normalize_unique_values = staticmethod(GPUNormalizeStringUtils.normalize_unique_values)
|
|
18
|
+
|
|
19
|
+
# NULL
|
|
20
|
+
normalize_na = staticmethod(GPUNormalizeNullUtils.normalize_na)
|
|
21
|
+
|
|
22
|
+
# BOOL
|
|
23
|
+
normalize_bool = staticmethod(GPUNormalizeBooleanUtils.normalize_bool)
|
|
24
|
+
|
|
25
|
+
# NUMBER
|
|
26
|
+
normalize_number = staticmethod(GPUNormalizeNumericUtils.normalize_number)
|
|
27
|
+
is_probably_number = staticmethod(GPUNormalizeNumericUtils.is_probably_number)
|
|
28
|
+
|
|
29
|
+
# DATE
|
|
30
|
+
normalize_date = staticmethod(GPUNormalizeDateUtils.normalize_date)
|
|
31
|
+
normalize_datetime = staticmethod(GPUNormalizeDateUtils.normalize_datetime)
|
|
32
|
+
is_date_column_by_samples = staticmethod(GPUNormalizeDateUtils.is_date_column_by_samples)
|
|
33
|
+
is_date = staticmethod(GPUNormalizeDateUtils.is_date)
|
|
34
|
+
to_date_iso = staticmethod(GPUNormalizeDateUtils.to_date_iso)
|
|
35
|
+
to_datetime = staticmethod(GPUNormalizeDateUtils.to_datetime)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# TIME
|
|
39
|
+
normalize_time = staticmethod(GPUNormalizeTimeUtils.normalize_time)
|
|
40
|
+
is_time = staticmethod(GPUNormalizeTimeUtils.is_time)
|
|
41
|
+
|
|
42
|
+
# TIMEDELTA
|
|
43
|
+
normalize_timedelta = staticmethod(GPUNormalizeTimeUtils.normalize_timedelta)
|
|
44
|
+
|
|
45
|
+
# DATETIME
|
|
46
|
+
create_datetime_column_from_date_and_time = staticmethod(GPUNormalizeDateTimeUtils.create_datetime_column_from_date_and_time)
|
|
47
|
+
create_day_week_column = staticmethod(GPUNormalizeDateTimeUtils.create_day_week_column)
|
|
48
|
+
create_month_br_category_column = staticmethod(GPUNormalizeDateTimeUtils.create_month_br_category_column)
|
|
49
|
+
create_day_week_br_category_column = staticmethod(GPUNormalizeDateTimeUtils.create_day_week_br_category_column)
|
|
50
|
+
create_hour_category_column = staticmethod(GPUNormalizeDateTimeUtils.create_hour_category_column)
|
|
51
|
+
|
|
52
|
+
# ANALYSIS
|
|
53
|
+
drop_columns = staticmethod(GPUAnalysisUtils.drop_columns)
|
|
54
|
+
cudf_size_info = staticmethod(GPUAnalysisUtils.cudf_size_info)
|
|
55
|
+
pandasdf_size_info = staticmethod(GPUAnalysisUtils.pandasdf_size_info)
|
|
56
|
+
get_gpu_memory_info = staticmethod(GPUAnalysisUtils.get_gpu_memory_info)
|
|
57
|
+
is_vram_use_limit = staticmethod(GPUAnalysisUtils.is_vram_use_limit)
|
|
58
|
+
get_index_samples = staticmethod(GPUAnalysisUtils.get_index_samples)
|
|
59
|
+
infer_by_sample = staticmethod(GPUAnalysisUtils.infer_by_sample)
|
|
60
|
+
|
|
61
|
+
# FILE
|
|
62
|
+
get_csv_info = staticmethod(GPUFileAnalysisUtils.get_csv_info)
|
|
63
|
+
convert_csv_to_utf8 = staticmethod(GPUFileAnalysisUtils.convert_csv_to_utf8)
|
|
64
|
+
convert_all_csvs_to_utf8 = staticmethod(GPUFileAnalysisUtils.convert_all_csvs_to_utf8)
|
|
65
|
+
read_csv_files = staticmethod(GPUFileAnalysisUtils.read_csv_files)
|
|
66
|
+
|
|
67
|
+
# instância única para ser usada como namespace
|
|
68
|
+
jiboia_gpu = JiboiaGPU()
|
|
69
|
+
|
|
70
|
+
__all__ = ["jiboia_gpu", "JiboiaGPU"]
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import cudf
|
|
2
|
+
import cupy as cp
|
|
3
|
+
import pandas as pd
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def print_text_red(text: str) -> str:
|
|
7
|
+
return f"\033[1;31m{text}\033[0m"
|
|
8
|
+
|
|
9
|
+
def print_text_yellow(text: str) -> str:
|
|
10
|
+
return f"\033[1;33m{text}\033[0m"
|
|
11
|
+
|
|
12
|
+
def print_text_green(text: str) -> str:
|
|
13
|
+
return f"\033[1;32m{text}\033[0m"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def print_job_drop_column_done(
|
|
17
|
+
columns_to_delete: list[str]
|
|
18
|
+
) -> None:
|
|
19
|
+
colored_names: list[str] = [
|
|
20
|
+
print_text_yellow(name) for name in columns_to_delete
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
msg: str = ", ".join(colored_names)
|
|
24
|
+
|
|
25
|
+
print(
|
|
26
|
+
print_text_green("Done!"),
|
|
27
|
+
"column",
|
|
28
|
+
msg,
|
|
29
|
+
"was",
|
|
30
|
+
print_text_red("dropped")
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class GPUAnalysisUtils:
|
|
35
|
+
@staticmethod
|
|
36
|
+
def drop_columns(
|
|
37
|
+
current_df: cudf.DataFrame,
|
|
38
|
+
column_names: list[str],
|
|
39
|
+
) -> None:
|
|
40
|
+
|
|
41
|
+
columns_to_delete: list = []
|
|
42
|
+
|
|
43
|
+
for column_name in column_names:
|
|
44
|
+
|
|
45
|
+
if column_name in current_df.columns:
|
|
46
|
+
columns_to_delete.append(column_name)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
if len(columns_to_delete) >= 1:
|
|
50
|
+
|
|
51
|
+
current_df.drop(
|
|
52
|
+
columns=columns_to_delete,
|
|
53
|
+
inplace=True
|
|
54
|
+
)
|
|
55
|
+
print_job_drop_column_done(columns_to_delete)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@staticmethod
|
|
59
|
+
def cudf_size_info(current_df: cudf.DataFrame, print_info: bool = False) -> None:
|
|
60
|
+
|
|
61
|
+
rows: int = current_df.shape[0]
|
|
62
|
+
columns: int = current_df.shape[1]
|
|
63
|
+
vram_size_mb: float = round(current_df.memory_usage(index=True, deep=True).sum() / (1024 * 1024), 2)
|
|
64
|
+
|
|
65
|
+
cudf_info: dict[str, any] = {
|
|
66
|
+
"rows": rows,
|
|
67
|
+
"columns": columns,
|
|
68
|
+
"VRAM size Mb": vram_size_mb
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if print_info:
|
|
72
|
+
print(
|
|
73
|
+
print_text_green("Done!"),
|
|
74
|
+
"rows:",
|
|
75
|
+
print_text_yellow(rows),
|
|
76
|
+
"columns:",
|
|
77
|
+
print_text_yellow(columns),
|
|
78
|
+
"VRAM size Mb:",
|
|
79
|
+
print_text_yellow(vram_size_mb),
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
return cudf_info
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@staticmethod
|
|
86
|
+
def get_gpu_memory_info(device_id: int = 0) -> dict[str, int]:
|
|
87
|
+
free_bytes, total_bytes = cp.cuda.runtime.memGetInfo()
|
|
88
|
+
return {
|
|
89
|
+
"free_mb": round(free_bytes / (1024 * 1024), 2),
|
|
90
|
+
"total": round(total_bytes / (1024 * 1024), 2),
|
|
91
|
+
"used_mb": round((total_bytes - free_bytes) / (1024 * 1024), 2),
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@staticmethod
|
|
95
|
+
def is_vram_use_limit(device_id: int = 0) -> dict[str, int]:
|
|
96
|
+
free_bytes, total_bytes = cp.cuda.runtime.memGetInfo()
|
|
97
|
+
vram_percent_in_use: float = round(((total_bytes - free_bytes) / total_bytes) * 100, 1) >= 90
|
|
98
|
+
|
|
99
|
+
if vram_percent_in_use >= 90:
|
|
100
|
+
return True
|
|
101
|
+
|
|
102
|
+
return False
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@staticmethod
|
|
106
|
+
def pandasdf_size_info(current_df: pd.DataFrame) -> None:
|
|
107
|
+
return {
|
|
108
|
+
"rows": current_df.shape[0],
|
|
109
|
+
"columns": current_df.shape[1],
|
|
110
|
+
"RAM MB": round(current_df.memory_usage(index=True, deep=True).sum() / (1024 * 1024), 2)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def print_converted_column_type(
|
|
115
|
+
column_name: str,
|
|
116
|
+
column_type: str
|
|
117
|
+
) -> None:
|
|
118
|
+
print(
|
|
119
|
+
"\033[1;32mDone!\033[0m",
|
|
120
|
+
"column",
|
|
121
|
+
# f"{column_name}",
|
|
122
|
+
f"\033[1;33m{column_name}\033[0m",
|
|
123
|
+
"converted to", f"\033[1;33m{column_type}\033[0m"
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
# @staticmethod
|
|
127
|
+
# def get_frequency_by_column(current_df: pd.DataFrame, column: str) -> pd.DataFrame:
|
|
128
|
+
# '''
|
|
129
|
+
# Gera um current_df com a frequencia de valores de uma coluna
|
|
130
|
+
# '''
|
|
131
|
+
# current_df_frequency: pd.DataFrame = (
|
|
132
|
+
# current_df[column]
|
|
133
|
+
# .value_counts()
|
|
134
|
+
# .reset_index()
|
|
135
|
+
# )
|
|
136
|
+
|
|
137
|
+
# current_df_frequency.columns = [column, 'frequency']
|
|
138
|
+
|
|
139
|
+
# current_df_frequency: pd.DataFrame = (
|
|
140
|
+
# current_df_frequency
|
|
141
|
+
# .sort_values(
|
|
142
|
+
# by='frequency',
|
|
143
|
+
# ascending=False
|
|
144
|
+
# )
|
|
145
|
+
# )
|
|
146
|
+
|
|
147
|
+
# return current_df_frequency
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
# @staticmethod
|
|
151
|
+
# def get_unique_values(current_df: pd.DataFrame, column: str) -> pd.DataFrame:
|
|
152
|
+
# '''
|
|
153
|
+
# Retorna um dataframe com os valores únicos de uma coluna
|
|
154
|
+
# '''
|
|
155
|
+
# df_unique = pd.DataFrame({
|
|
156
|
+
# column: current_df[column].dropna().unique()
|
|
157
|
+
# })
|
|
158
|
+
|
|
159
|
+
# df_unique = df_unique.sort_values(
|
|
160
|
+
# by=column,
|
|
161
|
+
# ascending=True
|
|
162
|
+
# ).reset_index(drop=True)
|
|
163
|
+
|
|
164
|
+
# return df_unique
|
|
165
|
+
|
|
166
|
+
@staticmethod
|
|
167
|
+
def get_index_samples(
|
|
168
|
+
series: cudf.Series,
|
|
169
|
+
n_parts: int = 10,
|
|
170
|
+
n_samples: int = 10
|
|
171
|
+
) -> list[int]:
|
|
172
|
+
"""
|
|
173
|
+
Verifica de forma performática na GPU se algum valor nas amostras de uma Series
|
|
174
|
+
corresponde a um padrão de data.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
s (cudf.Series): A Series de strings que será analisada.
|
|
178
|
+
n_parts (int): O número de partes em que a Series será dividida para amostragem.
|
|
179
|
+
n_samples (int): O número de amostras a serem coletadas de cada parte.
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
bool: True se um padrão de data for encontrado nas amostras, False caso contrário.
|
|
183
|
+
"""
|
|
184
|
+
|
|
185
|
+
series_size = len(series)
|
|
186
|
+
|
|
187
|
+
# print(series.name)
|
|
188
|
+
# return
|
|
189
|
+
|
|
190
|
+
if ((n_parts * n_samples) >= series_size):
|
|
191
|
+
raise ValueError("The total number of samples requested exceeds or equals the series size. Please provide a smaller value for n_parts or n_samples.")
|
|
192
|
+
|
|
193
|
+
if (series_size // n_parts == 0):
|
|
194
|
+
raise ValueError("The number of parts is greater than the series size. Please provide a smaller value for n_parts.")
|
|
195
|
+
|
|
196
|
+
# Gera todos os índices de amostragem DE UMA VEZ na GPU
|
|
197
|
+
step_pass = series_size // n_parts
|
|
198
|
+
|
|
199
|
+
# Índices iniciais de cada bloco (ex: 0, 1000, 2000, ...)
|
|
200
|
+
start_indices = cp.arange(n_parts) * step_pass
|
|
201
|
+
|
|
202
|
+
# Offsets dentro de cada bloco (ex: 0, 1, 2, ... n_samples-1)
|
|
203
|
+
sample_offsets = cp.arange(n_samples)
|
|
204
|
+
|
|
205
|
+
all_indices = (start_indices[:, None] + sample_offsets).flatten()
|
|
206
|
+
|
|
207
|
+
# Garante que os índices não ultrapassem o tamanho da Series
|
|
208
|
+
all_indices = all_indices[all_indices < series_size]
|
|
209
|
+
|
|
210
|
+
return all_indices
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
@staticmethod
|
|
214
|
+
def infer_by_sample(
|
|
215
|
+
series: cudf.Series,
|
|
216
|
+
regex_patern: str,
|
|
217
|
+
n_parts: int = 10,
|
|
218
|
+
n_samples: int = 10
|
|
219
|
+
) -> bool:
|
|
220
|
+
"""
|
|
221
|
+
Verifica de forma performática na GPU se algum valor nas amostras de uma Series
|
|
222
|
+
corresponde a um padrão de data.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
s (cudf.Series): A Series de strings que será analisada.
|
|
226
|
+
n_parts (int): O número de partes em que a Series será dividida para amostragem.
|
|
227
|
+
n_samples (int): O número de amostras a serem coletadas de cada parte.
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
bool: True se um padrão de data for encontrado nas amostras, False caso contrário.
|
|
231
|
+
"""
|
|
232
|
+
series_size = len(series)
|
|
233
|
+
|
|
234
|
+
if series_size == 0:
|
|
235
|
+
return False
|
|
236
|
+
|
|
237
|
+
# Coluna sem dados
|
|
238
|
+
if series.notna().sum() == 0:
|
|
239
|
+
return False
|
|
240
|
+
|
|
241
|
+
if series.dtype not in ["object", "string"]:
|
|
242
|
+
return False
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
# print(series.name)
|
|
246
|
+
# return
|
|
247
|
+
|
|
248
|
+
if ((n_parts * n_samples) >= series_size):
|
|
249
|
+
raise ValueError("The total number of samples requested exceeds or equals the series size. Please provide a smaller value for n_parts or n_samples.")
|
|
250
|
+
|
|
251
|
+
if (series_size // n_parts == 0):
|
|
252
|
+
raise ValueError("The number of parts is greater than the series size. Please provide a smaller value for n_parts.")
|
|
253
|
+
|
|
254
|
+
# Gera todos os índices de amostragem DE UMA VEZ na GPU
|
|
255
|
+
step_pass = series_size // n_parts
|
|
256
|
+
|
|
257
|
+
# Índices iniciais de cada bloco (ex: 0, 1000, 2000, ...)
|
|
258
|
+
start_indices = cp.arange(n_parts) * step_pass
|
|
259
|
+
|
|
260
|
+
# Offsets dentro de cada bloco (ex: 0, 1, 2, ... n_samples-1)
|
|
261
|
+
sample_offsets = cp.arange(n_samples)
|
|
262
|
+
|
|
263
|
+
all_indices = (start_indices[:, None] + sample_offsets).flatten()
|
|
264
|
+
|
|
265
|
+
# Garante que os índices não ultrapassem o tamanho da Series
|
|
266
|
+
all_indices = all_indices[all_indices < series_size]
|
|
267
|
+
|
|
268
|
+
# 3. Seleção de todas as amostras em uma única operação
|
|
269
|
+
samples = series.iloc[all_indices]
|
|
270
|
+
|
|
271
|
+
if (samples.str.contains(regex_patern).sum() == samples.notna().sum()):
|
|
272
|
+
return True
|
|
273
|
+
|
|
274
|
+
return False
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import csv
|
|
2
|
+
import chardet
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def print_text_red(text: str) -> str:
|
|
7
|
+
return f"\033[1;31m{text}\033[0m"
|
|
8
|
+
|
|
9
|
+
def print_text_yellow(text: str) -> str:
|
|
10
|
+
return f"\033[1;33m{text}\033[0m"
|
|
11
|
+
|
|
12
|
+
def print_text_green(text: str) -> str:
|
|
13
|
+
return f"\033[1;32m{text}\033[0m"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class GPUFileAnalysisUtils:
|
|
17
|
+
@staticmethod
|
|
18
|
+
def get_csv_info(
|
|
19
|
+
file_path: str,
|
|
20
|
+
encoding: None|str = None,
|
|
21
|
+
skiprows: None|int = 0,
|
|
22
|
+
sample_characters: int = 2048,
|
|
23
|
+
sample_bytes: int = 100000
|
|
24
|
+
) -> dict[str, any]:
|
|
25
|
+
"""
|
|
26
|
+
Detecta o delimitador e o encoding de um arquivo CSV.
|
|
27
|
+
|
|
28
|
+
:param file_path: caminho do arquivo CSV
|
|
29
|
+
:param encoding: encoding forçado (se None, tenta detectar com chardet)
|
|
30
|
+
:param skiprows: linhas iniciais a ignorar
|
|
31
|
+
:param sample_characters: n de caracteres usados para sniffing
|
|
32
|
+
:param sample_bytes: n de bytes usados para detectar encoding
|
|
33
|
+
:return: (delimiter, encoding_detectado)
|
|
34
|
+
:exemplo_de_retorno:
|
|
35
|
+
{
|
|
36
|
+
'encoding': 'utf-8',
|
|
37
|
+
'confidence': 0.99,
|
|
38
|
+
'language': 'pt',
|
|
39
|
+
'delimiter': ','
|
|
40
|
+
}
|
|
41
|
+
"""
|
|
42
|
+
csv_info: dict[str, any] = {
|
|
43
|
+
"encoding": encoding
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# detecta encoding se não informado
|
|
47
|
+
if encoding is None:
|
|
48
|
+
with open(file_path, "rb") as f:
|
|
49
|
+
rawdata = f.read(sample_bytes)
|
|
50
|
+
enc_info = chardet.detect(rawdata)
|
|
51
|
+
encoding = enc_info["encoding"] or "utf-8"
|
|
52
|
+
|
|
53
|
+
csv_info = {**csv_info, **enc_info}
|
|
54
|
+
|
|
55
|
+
# detecta delimitador
|
|
56
|
+
with open(file_path, "r", encoding=encoding, errors="ignore") as file:
|
|
57
|
+
for _ in range(skiprows):
|
|
58
|
+
next(file, None) # ignora linhas iniciais
|
|
59
|
+
|
|
60
|
+
sample = file.read(sample_characters)
|
|
61
|
+
sniffer = csv.Sniffer()
|
|
62
|
+
try:
|
|
63
|
+
dialect = sniffer.sniff(sample)
|
|
64
|
+
delimiter = dialect.delimiter
|
|
65
|
+
except csv.Error:
|
|
66
|
+
delimiter = ";"
|
|
67
|
+
|
|
68
|
+
csv_info["delimiter"] = delimiter
|
|
69
|
+
|
|
70
|
+
print(
|
|
71
|
+
print_text_green("Done!"),
|
|
72
|
+
"file:",
|
|
73
|
+
print_text_yellow(file_path),
|
|
74
|
+
"delimiter:",
|
|
75
|
+
print_text_yellow(f'"{csv_info["delimiter"]}"'),
|
|
76
|
+
"encoding:",
|
|
77
|
+
print_text_yellow(csv_info["encoding"]),
|
|
78
|
+
)
|
|
79
|
+
return csv_info
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@staticmethod
|
|
83
|
+
def convert_csv_to_utf8(file_path: str, output_path: str = None) -> str:
|
|
84
|
+
"""
|
|
85
|
+
Converte um arquivo CSV para UTF-8.
|
|
86
|
+
|
|
87
|
+
:param file_path: caminho do arquivo original
|
|
88
|
+
:param output_path: caminho do arquivo convertido (se None, gera com _utf8.csv)
|
|
89
|
+
:return: caminho do arquivo convertido em UTF-8
|
|
90
|
+
"""
|
|
91
|
+
file_path = Path(file_path)
|
|
92
|
+
|
|
93
|
+
# se não passar output_path, cria automático
|
|
94
|
+
if output_path is None:
|
|
95
|
+
output_path = file_path.with_name(file_path.stem + "_utf8.csv")
|
|
96
|
+
|
|
97
|
+
# detecta encoding
|
|
98
|
+
with open(file_path, "rb") as f:
|
|
99
|
+
rawdata = f.read(100000)
|
|
100
|
+
enc_info = chardet.detect(rawdata)
|
|
101
|
+
source_encoding = enc_info["encoding"] or "latin-1"
|
|
102
|
+
print(f"Encoding detectado: {source_encoding} (confiança={enc_info['confidence']:.2f})")
|
|
103
|
+
|
|
104
|
+
# lê no encoding original e salva em UTF-8
|
|
105
|
+
with open(file_path, "r", encoding=source_encoding, errors="ignore") as f_in:
|
|
106
|
+
data = f_in.read()
|
|
107
|
+
|
|
108
|
+
with open(output_path, "w", encoding="utf-8") as f_out:
|
|
109
|
+
f_out.write(data)
|
|
110
|
+
|
|
111
|
+
print(f"Arquivo convertido salvo em: {output_path}")
|
|
112
|
+
return str(output_path)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
import chardet
|
|
116
|
+
from pathlib import Path
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@staticmethod
|
|
120
|
+
def convert_all_csvs_to_utf8(folder_path: str, new_folder: bool = False) -> None:
|
|
121
|
+
"""
|
|
122
|
+
Converte todos os arquivos CSV em uma pasta para UTF-8.
|
|
123
|
+
Detecta o encoding de cada arquivo automaticamente.
|
|
124
|
+
|
|
125
|
+
:param folder_path: caminho da pasta contendo os CSVs
|
|
126
|
+
:param new_folder: se True, cria uma subpasta 'utf8_converted',
|
|
127
|
+
se False, salva os arquivos na mesma pasta com sufixo '_utf8'
|
|
128
|
+
"""
|
|
129
|
+
folder = Path(folder_path)
|
|
130
|
+
|
|
131
|
+
if new_folder:
|
|
132
|
+
output_folder = folder / "utf8_converted"
|
|
133
|
+
output_folder.mkdir(exist_ok=True)
|
|
134
|
+
else:
|
|
135
|
+
output_folder = folder # salva na mesma pasta
|
|
136
|
+
|
|
137
|
+
for csv_file in folder.glob("*.csv"):
|
|
138
|
+
|
|
139
|
+
# Detecta encoding
|
|
140
|
+
with open(csv_file, "rb") as f:
|
|
141
|
+
rawdata = f.read(100000) # lê parte do arquivo
|
|
142
|
+
result = chardet.detect(rawdata)
|
|
143
|
+
source_encoding = result["encoding"] or "latin-1"
|
|
144
|
+
confidence = result["confidence"]
|
|
145
|
+
# print(f"{csv_file.name}: encoding detectado = {source_encoding} (confiança={confidence:.2f})")
|
|
146
|
+
|
|
147
|
+
print(
|
|
148
|
+
"file:",
|
|
149
|
+
csv_file,
|
|
150
|
+
"encoding:",
|
|
151
|
+
print_text_yellow(result["encoding"]),
|
|
152
|
+
"confidence:",
|
|
153
|
+
print_text_yellow(confidence),
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# Se já estiver em UTF-8, pula
|
|
157
|
+
if source_encoding.lower() == 'utf-8':
|
|
158
|
+
continue
|
|
159
|
+
|
|
160
|
+
# Lê e converte para UTF-8
|
|
161
|
+
with open(csv_file, "r", encoding=source_encoding, errors="ignore") as f_in:
|
|
162
|
+
data = f_in.read()
|
|
163
|
+
|
|
164
|
+
if new_folder:
|
|
165
|
+
output_file = output_folder / csv_file.name
|
|
166
|
+
else:
|
|
167
|
+
output_file = output_folder / f"{csv_file.stem}_utf8.csv"
|
|
168
|
+
|
|
169
|
+
with open(output_file, "w", encoding="utf-8") as f_out:
|
|
170
|
+
f_out.write(data)
|
|
171
|
+
|
|
172
|
+
# print(f"Arquivo convertido salvo em: {output_file}")
|
|
173
|
+
print(
|
|
174
|
+
print_text_green("Done!"),
|
|
175
|
+
"file:",
|
|
176
|
+
print_text_yellow(output_file),
|
|
177
|
+
"converted to",
|
|
178
|
+
print_text_yellow("utf-8"),
|
|
179
|
+
)
|