statslibx 0.1.7__tar.gz → 0.2.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.
Files changed (27) hide show
  1. statslibx-0.2.0/MANIFEST.in +1 -0
  2. {statslibx-0.1.7 → statslibx-0.2.0}/PKG-INFO +19 -5
  3. {statslibx-0.1.7 → statslibx-0.2.0}/README.md +17 -3
  4. {statslibx-0.1.7 → statslibx-0.2.0}/pyproject.toml +5 -2
  5. {statslibx-0.1.7 → statslibx-0.2.0}/statslibx/__init__.py +12 -8
  6. statslibx-0.2.0/statslibx/computacional.py +126 -0
  7. statslibx-0.2.0/statslibx/datasets/__init__.py +260 -0
  8. {statslibx-0.1.7 → statslibx-0.2.0}/statslibx/descriptive.py +80 -15
  9. statslibx-0.2.0/statslibx/inferential.py +1541 -0
  10. {statslibx-0.1.7 → statslibx-0.2.0}/statslibx/preprocessing/__init__.py +12 -5
  11. {statslibx-0.1.7 → statslibx-0.2.0}/statslibx/utils.py +183 -163
  12. {statslibx-0.1.7 → statslibx-0.2.0}/statslibx.egg-info/PKG-INFO +19 -5
  13. {statslibx-0.1.7 → statslibx-0.2.0}/statslibx.egg-info/SOURCES.txt +2 -0
  14. {statslibx-0.1.7 → statslibx-0.2.0}/statslibx.egg-info/top_level.txt +1 -0
  15. statslibx-0.1.7/statslibx/datasets/__init__.py +0 -71
  16. statslibx-0.1.7/statslibx/inferential.py +0 -1041
  17. {statslibx-0.1.7 → statslibx-0.2.0}/setup.cfg +0 -0
  18. {statslibx-0.1.7 → statslibx-0.2.0}/statslibx/cli.py +0 -0
  19. {statslibx-0.1.7 → statslibx-0.2.0}/statslibx/datasets/course_completion.csv +0 -0
  20. {statslibx-0.1.7 → statslibx-0.2.0}/statslibx/datasets/iris.csv +0 -0
  21. {statslibx-0.1.7 → statslibx-0.2.0}/statslibx/datasets/penguins.csv +0 -0
  22. {statslibx-0.1.7 → statslibx-0.2.0}/statslibx/datasets/sp500_companies.csv +0 -0
  23. {statslibx-0.1.7 → statslibx-0.2.0}/statslibx/datasets/titanic.csv +0 -0
  24. {statslibx-0.1.7 → statslibx-0.2.0}/statslibx/io.py +0 -0
  25. {statslibx-0.1.7 → statslibx-0.2.0}/statslibx.egg-info/dependency_links.txt +0 -0
  26. {statslibx-0.1.7 → statslibx-0.2.0}/statslibx.egg-info/entry_points.txt +0 -0
  27. {statslibx-0.1.7 → statslibx-0.2.0}/statslibx.egg-info/requires.txt +0 -0
@@ -0,0 +1 @@
1
+ recursive-include statslibx/datasets *.csv
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: statslibx
3
- Version: 0.1.7
4
- Summary: StatsLibx - Librería de estadística descriptiva e inferencial
3
+ Version: 0.2.0
4
+ Summary: StatsLibx - Librería de estadística descriptiva, inferencial y computacional
5
5
  Author-email: Emmanuel Ascendra Perez <ascendraemmanuel@gmail.com>
6
6
  License: MIT
7
7
  Classifier: Development Status :: 3 - Alpha
@@ -24,11 +24,13 @@ Provides-Extra: advanced
24
24
  Requires-Dist: scikit-learn>=1.0; extra == "advanced"
25
25
  Requires-Dist: statsmodels>=0.13; extra == "advanced"
26
26
 
27
- # 📦 Descripción para PyPI (Plantilla Profesional)
27
+ # 📦 StatsLibX
28
28
 
29
29
  StatsLibX es un paquete de Python diseñado para proporcionar una solución sencilla, eficiente y flexible para manejar volumenes de datos.
30
30
 
31
- Este proyecto surge con la idea de ofrecer una alternativa moderna, intuitiva y ligera que permita a desarrolladores y entusiastas integrar la **estadistica descriptiva e inferencial** sin complicaciones, con multiples funcionalidades y utilidades pensadas para el futuro.
31
+ Este proyecto surge con la idea de ofrecer una alternativa moderna, intuitiva y ligera que permita a desarrolladores y entusiastas integrar la **estadistica descriptiva, inferencial y computacional (En desarrollo)** sin complicaciones, con multiples funcionalidades y utilidades pensadas para el futuro.
32
+
33
+ GitHub del Proyecto: [https://github.com/GhostAnalyst30/StatsLibX](https://github.com/GhostAnalyst30/StatsLibX)
32
34
 
33
35
  ## ✨ Características principales
34
36
 
@@ -45,16 +47,28 @@ Este proyecto surge con la idea de ofrecer una alternativa moderna, intuitiva y
45
47
  ## 🚀 Ejemplo rápido
46
48
  ```python
47
49
  from statslibx import DescriptiveStats, InferentialStats, UtilsStats
50
+ from statslibx.datasets import load_iris()
51
+
52
+ data = load_iris()
48
53
 
49
54
  stats = DescriptiveStats(data) # InferentialStats(data), UtilsStats()
50
- stats.help()
55
+
56
+ stats.summary()
51
57
  ```
58
+ Para ver mas funciones: [https://github.com/GhostAnalyst30/StatsLibX/blob/main/how_use_statslibx.ipynb](https://github.com/GhostAnalyst30/StatsLibX/blob/main/how_use_statslibx.ipynb)
52
59
 
53
60
  ## 📦 Instalación
54
61
  ```bash
55
62
  pip install statslibx
56
63
  ```
57
64
 
65
+ ## 👩‍💻 ¡Usalo en la terminal! (De forma preliminar)
66
+ ```bash
67
+ statslibx describe .\archive.csv # Devuelve una descripcion de la data
68
+ statslibx quality .\archive.csv # Devuelve la calidad de los datos
69
+ statslibx preview .\archive.csv # Devuelve una visualizacion de los datos
70
+ ```
71
+
58
72
  🤝 Contribuciones
59
73
 
60
74
  ¡Todas las mejoras e ideas son bienvenidas!
@@ -1,8 +1,10 @@
1
- # 📦 Descripción para PyPI (Plantilla Profesional)
1
+ # 📦 StatsLibX
2
2
 
3
3
  StatsLibX es un paquete de Python diseñado para proporcionar una solución sencilla, eficiente y flexible para manejar volumenes de datos.
4
4
 
5
- Este proyecto surge con la idea de ofrecer una alternativa moderna, intuitiva y ligera que permita a desarrolladores y entusiastas integrar la **estadistica descriptiva e inferencial** sin complicaciones, con multiples funcionalidades y utilidades pensadas para el futuro.
5
+ Este proyecto surge con la idea de ofrecer una alternativa moderna, intuitiva y ligera que permita a desarrolladores y entusiastas integrar la **estadistica descriptiva, inferencial y computacional (En desarrollo)** sin complicaciones, con multiples funcionalidades y utilidades pensadas para el futuro.
6
+
7
+ GitHub del Proyecto: [https://github.com/GhostAnalyst30/StatsLibX](https://github.com/GhostAnalyst30/StatsLibX)
6
8
 
7
9
  ## ✨ Características principales
8
10
 
@@ -19,16 +21,28 @@ Este proyecto surge con la idea de ofrecer una alternativa moderna, intuitiva y
19
21
  ## 🚀 Ejemplo rápido
20
22
  ```python
21
23
  from statslibx import DescriptiveStats, InferentialStats, UtilsStats
24
+ from statslibx.datasets import load_iris()
25
+
26
+ data = load_iris()
22
27
 
23
28
  stats = DescriptiveStats(data) # InferentialStats(data), UtilsStats()
24
- stats.help()
29
+
30
+ stats.summary()
25
31
  ```
32
+ Para ver mas funciones: [https://github.com/GhostAnalyst30/StatsLibX/blob/main/how_use_statslibx.ipynb](https://github.com/GhostAnalyst30/StatsLibX/blob/main/how_use_statslibx.ipynb)
26
33
 
27
34
  ## 📦 Instalación
28
35
  ```bash
29
36
  pip install statslibx
30
37
  ```
31
38
 
39
+ ## 👩‍💻 ¡Usalo en la terminal! (De forma preliminar)
40
+ ```bash
41
+ statslibx describe .\archive.csv # Devuelve una descripcion de la data
42
+ statslibx quality .\archive.csv # Devuelve la calidad de los datos
43
+ statslibx preview .\archive.csv # Devuelve una visualizacion de los datos
44
+ ```
45
+
32
46
  🤝 Contribuciones
33
47
 
34
48
  ¡Todas las mejoras e ideas son bienvenidas!
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "statslibx"
7
- version = "0.1.7"
8
- description = "StatsLibx - Librería de estadística descriptiva e inferencial"
7
+ version = "0.2.0"
8
+ description = "StatsLibx - Librería de estadística descriptiva, inferencial y computacional"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
11
11
  license = { text = "MIT" }
@@ -38,5 +38,8 @@ advanced = ["scikit-learn>=1.0", "statsmodels>=0.13"]
38
38
  [project.scripts]
39
39
  statslibx = "statslibx.cli:main"
40
40
 
41
+ [tool.setuptools]
42
+ include-package-data = true
43
+
41
44
  [tool.setuptools.packages.find]
42
45
  where = ["."]
@@ -1,30 +1,31 @@
1
1
  """
2
2
  StatsLibx - Librería de Estadística para Python
3
3
  Autor: Emmanuel Ascendra
4
- Versión: 0.1.6
4
+ Versión: 0.2.0
5
5
  """
6
6
 
7
- __version__ = "0.1.6"
7
+ __version__ = "0.2.0"
8
8
  __author__ = "Emmanuel Ascendra"
9
9
 
10
10
  # Importar las clases principales
11
11
  from .descriptive import DescriptiveStats, DescriptiveSummary
12
12
  from .inferential import InferentialStats, TestResult
13
+ from .computacional import ComputationalStats
13
14
  from .utils import UtilsStats
14
15
  from .preprocessing import Preprocessing
15
- from .datasets import load_dataset
16
+ from .datasets import load_dataset, generate_dataset
16
17
 
17
18
  # Definir qué se expone cuando se hace: from statslib import *
18
19
  __all__ = [
19
20
  # Clases principales
20
21
  'DescriptiveStats',
21
- 'InferentialStats',
22
- 'LinearRegressionResult',
23
- 'DescriptiveSummary',
24
- 'TestResult',
22
+ 'InferentialStats',
23
+ 'ProbabilityStats',
24
+ 'ComputationalStats',
25
25
  'UtilsStats',
26
26
  'Preprocessing',
27
- 'load_dataset'
27
+ 'load_dataset',
28
+ 'generate_dataset'
28
29
  ]
29
30
 
30
31
  # Mensaje de bienvenida (opcional)
@@ -36,6 +37,9 @@ def welcome():
36
37
  print(f"\nClases disponibles:")
37
38
  print(f" - DescriptiveStats: Estadística descriptiva")
38
39
  print(f" - InferentialStats: Estadística inferencial")
40
+ print(f" - ComputacionalStats: En desarrollo")
39
41
  print(f" - UtilsStats: Utilidades Extras")
42
+ print(f"\nMódulos disponibles:")
43
+ print(f" - Datasets: Carga de Datasets")
40
44
  print(f" - Preprocessing: Preprocesamiento de datos")
41
45
  print(f"\nPara más información: help(statslibx)")
@@ -0,0 +1,126 @@
1
+ from typing import Union, Optional, Literal
2
+ import numpy as np
3
+ import pandas as pd
4
+ import polars as pl
5
+ import os
6
+
7
+ class ComputationalStats:
8
+ """
9
+ Class for computational statistics
10
+ """
11
+
12
+ def __init__(self, data: Union[pd.DataFrame, np.ndarray],
13
+ sep: str = None,
14
+ decimal: str = None,
15
+ thousand: str = None,
16
+ backend: Literal['pandas', 'polars'] = 'pandas'):
17
+ """
18
+ # Initialize DataFrame
19
+
20
+ ## **Parameters:**
21
+
22
+ - **data** : Data to analyze
23
+ - **sep** : Column separator
24
+ - **decimal** : Decimal separator
25
+ - **thousand** : Thousand separator
26
+ - **backend** : 'pandas' or 'polars' for processing
27
+ (Proximamente estara habilitado polars para big data)
28
+
29
+ **Examples:**
30
+
31
+ ``Example 1:
32
+ stats = DescriptiveStats(data)
33
+ ``
34
+ """
35
+
36
+ if isinstance(data, str) and os.path.exists(data):
37
+ data = ComputationalStats.from_file(data).data
38
+
39
+ if isinstance(data, pl.DataFrame):
40
+ raise TypeError(
41
+ "Polars aún no soportado. Use pandas.DataFrame."
42
+ )
43
+
44
+
45
+ if isinstance(data, np.ndarray):
46
+ if data.ndim == 1:
47
+ data = pd.DataFrame({'var': data})
48
+ else:
49
+ data = pd.DataFrame(data, columns=[f'var_{i}' for i in range(data.shape[1])]) \
50
+ if isinstance(data, pd.DataFrame) else pl.DataFrame(data, )
51
+
52
+ self.data = data
53
+ self.backend = backend
54
+ self._numeric_cols = data.select_dtypes(include=[np.number]).columns.tolist()
55
+ self.sep = sep
56
+ self.decimal = decimal
57
+ self.thousand = thousand
58
+
59
+ @classmethod
60
+ def from_file(self, path: str):
61
+ """
62
+ Carga automática de archivos y devuelve instancia de Intelligence.
63
+ Soporta CSV, Excel, TXT, JSON, Parquet, Feather, TSV.
64
+ Automatic file upload and returns Intelligence instance.
65
+ Supports CSV, Excel, TXT, JSON, Parquet, Feather, TSV.
66
+
67
+ Parametros / Parameters:
68
+ ------------------------
69
+ path : str
70
+ Ruta del archivo
71
+ File path
72
+ """
73
+ if not os.path.exists(path):
74
+ raise FileNotFoundError(f"Archivo no encontrado / File not found: {path}")
75
+
76
+ ext = os.path.splitext(path)[1].lower()
77
+
78
+ if ext == ".csv":
79
+ df = pd.read_csv(path, sep=self.sep, decimal=self.decimal, thousand=self.thousand)
80
+
81
+ elif ext in [".xlsx", ".xls"]:
82
+ df = pd.read_excel(path, decimal=self.decimal, thousand=self.thousand)
83
+
84
+ elif ext in [".txt", ".tsv"]:
85
+ df = pd.read_table(path, sep=self.sep, decimal=self.decimal, thousand=self.thousand)
86
+
87
+ elif ext == ".json":
88
+ df = pd.read_json(path)
89
+
90
+ elif ext == ".parquet":
91
+ df = pd.read_parquet(path)
92
+
93
+ elif ext == ".feather":
94
+ df = pd.read_feather(path)
95
+
96
+ else:
97
+ raise ValueError(f"Formato no soportado / Unsupported format: {ext}")
98
+
99
+ return ComputationalStats(df)
100
+
101
+ def monte_carlo(self, function, n: int = 100, return_simulations: bool = False, **kwargs) -> pd.DataFrame:
102
+ """
103
+ Realiza simulaciones de Monte Carlo para una función y devuelve un DataFrame con las simulaciones y sus resultados.
104
+ """
105
+ samples = []
106
+
107
+ for _ in range(n):
108
+ sample = function(**kwargs)
109
+ samples.append(float(sample))
110
+
111
+ mean = sum(samples) / n
112
+ variance = sum((x - mean)**2 for x in samples) / n
113
+ std = variance**0.5
114
+
115
+ if return_simulations:
116
+ return {
117
+ "mean": float(mean),
118
+ "std": float(std),
119
+ "samples": samples
120
+ }
121
+
122
+ else:
123
+ return {
124
+ "mean": float(mean),
125
+ "std": float(std)
126
+ }
@@ -0,0 +1,260 @@
1
+ from typing import Optional, Union, Literal, List, Tuple
2
+ import io
3
+ import pkgutil
4
+
5
+ import pandas as pd
6
+ import polars as pl
7
+ import numpy as np
8
+ from numpy.typing import NDArray
9
+
10
+
11
+ _SUPPORTED_BACKENDS = ("pandas", "polars")
12
+
13
+
14
+ def _validate_columns(
15
+ df: Union[pd.DataFrame, pl.DataFrame],
16
+ X_columns: List[str],
17
+ y_column: str
18
+ ) -> None:
19
+ columns = set(df.columns)
20
+ missing = set(X_columns + [y_column]) - columns
21
+ if missing:
22
+ raise ValueError(f"Columnas no encontradas en el dataset: {missing}")
23
+
24
+
25
+ def _X_y(
26
+ df: Union[pd.DataFrame, pl.DataFrame],
27
+ X_columns: List[str],
28
+ y_column: str
29
+ ) -> Tuple[NDArray, NDArray]:
30
+ """
31
+ Extrae X e y como arrays numpy desde pandas o polars.
32
+ """
33
+ _validate_columns(df, X_columns, y_column)
34
+
35
+ if isinstance(df, pd.DataFrame):
36
+ X = df[X_columns].to_numpy()
37
+ y = df[y_column].to_numpy().ravel()
38
+ return X, y
39
+
40
+ elif isinstance(df, pl.DataFrame):
41
+ X = df.select(X_columns).to_numpy()
42
+ y = df.select(y_column).to_numpy().ravel()
43
+ return X, y
44
+
45
+ else:
46
+ raise TypeError(
47
+ "Backend no soportado. Use pandas.DataFrame o polars.DataFrame."
48
+ )
49
+
50
+
51
+ import io
52
+ import pkgutil
53
+ import pandas as pd
54
+ import polars as pl
55
+ from typing import Literal, Optional, Tuple, List, Union
56
+ from numpy.typing import NDArray
57
+
58
+ _SUPPORTED_BACKENDS = {"pandas", "polars"}
59
+
60
+ def load_dataset(
61
+ name: str,
62
+ backend: Literal["pandas", "polars"] = "pandas",
63
+ return_X_y: Optional[Tuple[List[str], str]] = None,
64
+ save: Optional[bool] = False,
65
+ filename: Optional[str] = None
66
+ ) -> Union[pd.DataFrame, pl.DataFrame, Tuple[NDArray, NDArray]]:
67
+ """
68
+ Carga un dataset interno del paquete.
69
+
70
+ Datasets disponibles:
71
+ - iris.csv
72
+ - penguins.csv
73
+ - sp500_companies.csv
74
+ - titanic.csv
75
+ - course_completion.csv
76
+
77
+ Parámetros
78
+ ----------
79
+ name : str
80
+ Nombre del archivo CSV.
81
+ backend : {'pandas', 'polars'}, default='pandas'
82
+ Backend de DataFrame a utilizar.
83
+ return_X_y : tuple[list[str], str], optional
84
+ Si se especifica, devuelve (X, y) como arrays numpy,
85
+
86
+ Retorna
87
+ -------
88
+ DataFrame o (X, y)
89
+ """
90
+
91
+ if backend not in _SUPPORTED_BACKENDS:
92
+ raise ValueError(
93
+ f"Backend '{backend}' no soportado. "
94
+ f"Use uno de {_SUPPORTED_BACKENDS}."
95
+ )
96
+
97
+ df = None
98
+
99
+ # ---------- 1️⃣ Intentar cargar desde el paquete ----------
100
+ try:
101
+ data_bytes = pkgutil.get_data("statslibx.datasets", name)
102
+ if data_bytes is not None:
103
+ df = (
104
+ pd.read_csv(io.BytesIO(data_bytes))
105
+ if backend == "pandas"
106
+ else pl.read_csv(io.BytesIO(data_bytes))
107
+ )
108
+ except FileNotFoundError:
109
+ pass # seguimos al siguiente intento
110
+
111
+ # ---------- 2️⃣ Intentar cargar desde ruta local ----------
112
+ if df is None:
113
+ try:
114
+ df = (
115
+ pd.read_csv(name)
116
+ if backend == "pandas"
117
+ else pl.read_csv(name)
118
+ )
119
+ except FileNotFoundError:
120
+ raise FileNotFoundError(
121
+ f"Dataset '{name}' no encontrado "
122
+ f"ni en statslibx.datasets ni en la ruta actual."
123
+ )
124
+
125
+ # ---------- 3️⃣ Devolver X, y si se solicita ----------
126
+ if return_X_y is not None:
127
+ X_columns, y_column = return_X_y
128
+ return _X_y(df, X_columns, y_column)
129
+
130
+ return df
131
+
132
+ # =========================
133
+ # Datasets específicos
134
+ # =========================
135
+
136
+ def load_iris(
137
+ backend: Literal["pandas", "polars"] = "pandas",
138
+ return_X_y: Optional[Tuple[List[str], str]] = None
139
+ ):
140
+ return load_dataset(
141
+ "iris.csv",
142
+ backend=backend,
143
+ return_X_y=return_X_y
144
+ )
145
+
146
+
147
+ def load_penguins(
148
+ backend: Literal["pandas", "polars"] = "pandas",
149
+ return_X_y: Optional[Tuple[List[str], str]] = None
150
+ ):
151
+ return load_dataset(
152
+ "penguins.csv",
153
+ backend=backend,
154
+ return_X_y=return_X_y
155
+ )
156
+
157
+
158
+ from typing import Optional
159
+
160
+ def generate_dataset(n_rows, schema, seed=None, save: Optional[bool] = False, filename: Optional[str] = None):
161
+ if seed is not None:
162
+ if not isinstance(seed, int):
163
+ raise TypeError("seed debe ser un entero o None")
164
+ np.random.seed(seed)
165
+ else:
166
+ np.random.seed(42)
167
+
168
+ if not isinstance(schema, dict):
169
+ raise TypeError("schema debe ser un diccionario")
170
+
171
+
172
+
173
+ data = {}
174
+
175
+ for col, config in schema.items():
176
+ if "dist" not in config:
177
+ raise ValueError(f"La columna '{col}' no tiene 'dist' definido")
178
+
179
+ dist = config["dist"]
180
+ dtype = config.get("type", "float")
181
+ nround = config.get("round", 0)
182
+
183
+ # ---------- DISTRIBUCIONES ----------
184
+ if dist == "normal":
185
+ values = np.random.normal(
186
+ loc=config.get("mean", 0),
187
+ scale=config.get("std", 1),
188
+ size=n_rows
189
+ )
190
+
191
+ elif dist == "uniform":
192
+ values = np.random.uniform(
193
+ low=config.get("low", 0),
194
+ high=config.get("high", 1),
195
+ size=n_rows
196
+ )
197
+
198
+ elif dist == "exponential":
199
+ values = np.random.exponential(
200
+ scale=config.get("scale", 1),
201
+ size=n_rows
202
+ )
203
+
204
+ elif dist == "lognormal":
205
+ values = np.random.lognormal(
206
+ mean=config.get("mean", 0),
207
+ sigma=config.get("std", 1),
208
+ size=n_rows
209
+ )
210
+
211
+ elif dist == "poisson":
212
+ values = np.random.poisson(
213
+ lam=config.get("lam", 1),
214
+ size=n_rows
215
+ )
216
+
217
+ elif dist == "binomial":
218
+ values = np.random.binomial(
219
+ n=config.get("n", 1),
220
+ p=config.get("p", 0.5),
221
+ size=n_rows
222
+ )
223
+
224
+ elif dist == "categorical":
225
+ if "choices" not in config:
226
+ raise ValueError(f"'choices' es requerido para categorical ({col})")
227
+ values = np.random.choice(
228
+ config["choices"],
229
+ size=n_rows
230
+ )
231
+ data[col] = values
232
+ continue
233
+
234
+ else:
235
+ raise ValueError(f"Distribución no soportada: {dist}")
236
+
237
+ # ---------- CASTEO DE TIPO ----------
238
+ if dtype == "int":
239
+ values = np.round(values).astype(int)
240
+ elif dtype == "float":
241
+ values = values.astype(float)
242
+ else:
243
+ raise ValueError(f"Tipo no soportado: {dtype}")
244
+
245
+ # ---------- REDONDEO ----------
246
+ if nround > 0:
247
+ values = np.round(values, nround)
248
+ else:
249
+ values = np.round(values, 2)
250
+
251
+ data[col] = values
252
+
253
+ if save and filename:
254
+ df = pd.DataFrame(data)
255
+ df.to_csv(f"{filename}.csv", index=False)
256
+ else:
257
+ df = pd.DataFrame(data)
258
+ df.to_csv("dataset.csv", index=False)
259
+
260
+ return pd.DataFrame(data)