numpy2 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- numpy2/__init__.py +67 -0
- numpy2/converters.py +265 -0
- numpy2/core.py +360 -0
- numpy2/integrations.py +291 -0
- numpy2-1.0.0.dist-info/METADATA +547 -0
- numpy2-1.0.0.dist-info/RECORD +9 -0
- numpy2-1.0.0.dist-info/WHEEL +5 -0
- numpy2-1.0.0.dist-info/licenses/LICENSE +21 -0
- numpy2-1.0.0.dist-info/top_level.txt +1 -0
numpy2/__init__.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""
|
|
2
|
+
numpy2 - Advanced NumPy for Web Applications
|
|
3
|
+
============================================
|
|
4
|
+
|
|
5
|
+
A powerful library that bridges NumPy and web frameworks, solving critical pain points:
|
|
6
|
+
- JSON serialization of NumPy types (int64, float64, etc.)
|
|
7
|
+
- Automatic type conversion for web APIs
|
|
8
|
+
- FastAPI/Flask/Django integration
|
|
9
|
+
- Zero-configuration setup
|
|
10
|
+
- Production-ready performance
|
|
11
|
+
|
|
12
|
+
Build by: Mahesh Makvana
|
|
13
|
+
GitHub: https://github.com/maheshmakvana/numpy2
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
>>> import numpy2 as np2
|
|
17
|
+
>>> arr = np2.array([1, 2, 3])
|
|
18
|
+
>>> json_safe = np2.to_json(arr)
|
|
19
|
+
>>> fastapi_response = np2.serialize(arr) # Ready for FastAPI JSONResponse
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
__version__ = "1.0.0"
|
|
23
|
+
__author__ = "Mahesh Makvana"
|
|
24
|
+
__email__ = "mahesh@example.com"
|
|
25
|
+
__license__ = "MIT"
|
|
26
|
+
|
|
27
|
+
from .core import (
|
|
28
|
+
array,
|
|
29
|
+
ndarray,
|
|
30
|
+
to_json,
|
|
31
|
+
from_json,
|
|
32
|
+
serialize,
|
|
33
|
+
deserialize,
|
|
34
|
+
JSONEncoder,
|
|
35
|
+
JSONDecoder,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
from .converters import (
|
|
39
|
+
numpy_to_python,
|
|
40
|
+
pandas_to_json,
|
|
41
|
+
python_to_numpy,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
from .integrations import (
|
|
45
|
+
FastAPIResponse,
|
|
46
|
+
FlaskResponse,
|
|
47
|
+
DjangoResponse,
|
|
48
|
+
setup_json_encoder,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
__all__ = [
|
|
52
|
+
"array",
|
|
53
|
+
"ndarray",
|
|
54
|
+
"to_json",
|
|
55
|
+
"from_json",
|
|
56
|
+
"serialize",
|
|
57
|
+
"deserialize",
|
|
58
|
+
"JSONEncoder",
|
|
59
|
+
"JSONDecoder",
|
|
60
|
+
"numpy_to_python",
|
|
61
|
+
"pandas_to_json",
|
|
62
|
+
"python_to_numpy",
|
|
63
|
+
"FastAPIResponse",
|
|
64
|
+
"FlaskResponse",
|
|
65
|
+
"DjangoResponse",
|
|
66
|
+
"setup_json_encoder",
|
|
67
|
+
]
|
numpy2/converters.py
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"""
|
|
2
|
+
numpy2.converters - Data type conversion utilities
|
|
3
|
+
|
|
4
|
+
Handles conversions between NumPy, pandas, Python types, and JSON
|
|
5
|
+
with automatic type inference and data integrity preservation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import pandas as pd
|
|
10
|
+
from typing import Any, Dict, List, Union, Optional
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def numpy_to_python(obj: Any) -> Any:
|
|
14
|
+
"""
|
|
15
|
+
Convert NumPy types to native Python types.
|
|
16
|
+
|
|
17
|
+
SOLVES: Silent data loss from NumPy type conversions
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
obj: NumPy object to convert
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
Native Python type
|
|
24
|
+
|
|
25
|
+
Example:
|
|
26
|
+
>>> import numpy as np
|
|
27
|
+
>>> import numpy2 as np2
|
|
28
|
+
>>> val = np.int64(42)
|
|
29
|
+
>>> result = np2.numpy_to_python(val)
|
|
30
|
+
>>> type(result)
|
|
31
|
+
<class 'int'>
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
if isinstance(obj, np.ndarray):
|
|
35
|
+
return obj.tolist()
|
|
36
|
+
elif isinstance(obj, np.integer):
|
|
37
|
+
return int(obj)
|
|
38
|
+
elif isinstance(obj, np.floating):
|
|
39
|
+
if np.isnan(obj) or np.isinf(obj):
|
|
40
|
+
return None
|
|
41
|
+
return float(obj)
|
|
42
|
+
elif isinstance(obj, np.bool_):
|
|
43
|
+
return bool(obj)
|
|
44
|
+
elif isinstance(obj, np.datetime64):
|
|
45
|
+
return str(obj)
|
|
46
|
+
elif isinstance(obj, np.generic):
|
|
47
|
+
return obj.item()
|
|
48
|
+
elif isinstance(obj, (list, tuple)):
|
|
49
|
+
return type(obj)(numpy_to_python(item) for item in obj)
|
|
50
|
+
elif isinstance(obj, dict):
|
|
51
|
+
return {k: numpy_to_python(v) for k, v in obj.items()}
|
|
52
|
+
else:
|
|
53
|
+
return obj
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def pandas_to_json(
|
|
57
|
+
df: pd.DataFrame,
|
|
58
|
+
orient: str = 'records',
|
|
59
|
+
include_index: bool = False
|
|
60
|
+
) -> Dict[str, Any]:
|
|
61
|
+
"""
|
|
62
|
+
Convert pandas DataFrame to JSON-safe dictionary.
|
|
63
|
+
|
|
64
|
+
SOLVES: JSON serialization of DataFrames with NumPy columns
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
df: pandas DataFrame
|
|
68
|
+
orient: DataFrame orientation ('records', 'list', 'dict', 'split', 'tight', 'index', 'columns', 'values')
|
|
69
|
+
include_index: Include index in output
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
JSON-safe dictionary
|
|
73
|
+
|
|
74
|
+
Example:
|
|
75
|
+
>>> import pandas as pd
|
|
76
|
+
>>> import numpy2 as np2
|
|
77
|
+
>>> df = pd.DataFrame({'A': [1, 2], 'B': [3.5, 4.5]})
|
|
78
|
+
>>> result = np2.pandas_to_json(df)
|
|
79
|
+
>>> type(result)
|
|
80
|
+
<class 'dict'>
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
if not isinstance(df, pd.DataFrame):
|
|
84
|
+
raise TypeError(f"Expected pandas DataFrame, got {type(df)}")
|
|
85
|
+
|
|
86
|
+
# Convert all NumPy types to Python types
|
|
87
|
+
df_converted = df.copy()
|
|
88
|
+
|
|
89
|
+
for col in df_converted.columns:
|
|
90
|
+
if df_converted[col].dtype == 'object':
|
|
91
|
+
continue
|
|
92
|
+
if hasattr(df_converted[col].dtype, 'name'):
|
|
93
|
+
if 'int' in str(df_converted[col].dtype) or 'float' in str(df_converted[col].dtype):
|
|
94
|
+
df_converted[col] = df_converted[col].astype(str).apply(
|
|
95
|
+
lambda x: int(x) if '.' not in x else float(x)
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
result = df_converted.to_dict(orient=orient)
|
|
99
|
+
|
|
100
|
+
if include_index and orient == 'records':
|
|
101
|
+
result = [
|
|
102
|
+
{**row, '__index__': idx}
|
|
103
|
+
for idx, row in enumerate(result)
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
return result
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def python_to_numpy(
|
|
110
|
+
data: Any,
|
|
111
|
+
dtype: Optional[str] = None
|
|
112
|
+
) -> np.ndarray:
|
|
113
|
+
"""
|
|
114
|
+
Convert Python types to NumPy array.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
data: Python data (list, tuple, dict, etc.)
|
|
118
|
+
dtype: Target NumPy dtype
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
NumPy ndarray
|
|
122
|
+
|
|
123
|
+
Example:
|
|
124
|
+
>>> import numpy2 as np2
|
|
125
|
+
>>> data = [1, 2, 3]
|
|
126
|
+
>>> arr = np2.python_to_numpy(data, dtype='float32')
|
|
127
|
+
>>> arr.dtype
|
|
128
|
+
dtype('float32')
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
return np.array(data, dtype=dtype)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def infer_dtype(data: Any) -> str:
|
|
135
|
+
"""
|
|
136
|
+
Intelligently infer appropriate NumPy dtype from data.
|
|
137
|
+
|
|
138
|
+
SOLVES: Type inference problems in web APIs
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
data: Data to analyze
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
Inferred NumPy dtype string
|
|
145
|
+
|
|
146
|
+
Example:
|
|
147
|
+
>>> import numpy2 as np2
|
|
148
|
+
>>> dtype = np2.infer_dtype([1, 2, 3])
|
|
149
|
+
>>> dtype
|
|
150
|
+
'int64'
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
if isinstance(data, (list, tuple)):
|
|
154
|
+
if not data:
|
|
155
|
+
return 'float64'
|
|
156
|
+
|
|
157
|
+
# Check first element type
|
|
158
|
+
first = data[0]
|
|
159
|
+
|
|
160
|
+
if isinstance(first, bool):
|
|
161
|
+
return 'bool'
|
|
162
|
+
elif isinstance(first, int):
|
|
163
|
+
# Check range to choose appropriate int type
|
|
164
|
+
values = [x for x in data if isinstance(x, int)]
|
|
165
|
+
if all(-128 <= v < 128 for v in values):
|
|
166
|
+
return 'int8'
|
|
167
|
+
elif all(-32768 <= v < 32768 for v in values):
|
|
168
|
+
return 'int16'
|
|
169
|
+
elif all(-2147483648 <= v < 2147483648 for v in values):
|
|
170
|
+
return 'int32'
|
|
171
|
+
else:
|
|
172
|
+
return 'int64'
|
|
173
|
+
elif isinstance(first, float):
|
|
174
|
+
return 'float64'
|
|
175
|
+
elif isinstance(first, str):
|
|
176
|
+
return 'object'
|
|
177
|
+
elif isinstance(first, (list, tuple)):
|
|
178
|
+
return infer_dtype(first)
|
|
179
|
+
|
|
180
|
+
elif isinstance(data, dict):
|
|
181
|
+
return 'object'
|
|
182
|
+
|
|
183
|
+
elif isinstance(data, np.ndarray):
|
|
184
|
+
return str(data.dtype)
|
|
185
|
+
|
|
186
|
+
return 'object'
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def safe_cast(
|
|
190
|
+
value: Any,
|
|
191
|
+
target_dtype: str,
|
|
192
|
+
raise_on_error: bool = False
|
|
193
|
+
) -> Any:
|
|
194
|
+
"""
|
|
195
|
+
Safely cast value to target dtype with error handling.
|
|
196
|
+
|
|
197
|
+
SOLVES: Type conversion errors breaking web APIs
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
value: Value to cast
|
|
201
|
+
target_dtype: Target NumPy dtype
|
|
202
|
+
raise_on_error: Raise exception on failure (default: return original)
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
Casted value or original value on failure
|
|
206
|
+
|
|
207
|
+
Example:
|
|
208
|
+
>>> import numpy2 as np2
|
|
209
|
+
>>> result = np2.safe_cast("123", 'int32')
|
|
210
|
+
>>> result
|
|
211
|
+
123
|
|
212
|
+
"""
|
|
213
|
+
|
|
214
|
+
try:
|
|
215
|
+
if target_dtype in ['int8', 'int16', 'int32', 'int64']:
|
|
216
|
+
return int(value)
|
|
217
|
+
elif target_dtype in ['float16', 'float32', 'float64']:
|
|
218
|
+
return float(value)
|
|
219
|
+
elif target_dtype == 'bool':
|
|
220
|
+
return bool(value)
|
|
221
|
+
else:
|
|
222
|
+
return np.array([value], dtype=target_dtype)[0]
|
|
223
|
+
except (ValueError, TypeError) as e:
|
|
224
|
+
if raise_on_error:
|
|
225
|
+
raise
|
|
226
|
+
return value
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def batch_convert(
|
|
230
|
+
data: List[Dict[str, Any]],
|
|
231
|
+
dtype_map: Optional[Dict[str, str]] = None
|
|
232
|
+
) -> List[Dict[str, Any]]:
|
|
233
|
+
"""
|
|
234
|
+
Convert batch of records with consistent type handling.
|
|
235
|
+
|
|
236
|
+
SOLVES: Bulk data conversion issues in APIs
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
data: List of dictionaries to convert
|
|
240
|
+
dtype_map: Mapping of field names to target dtypes
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
Converted data
|
|
244
|
+
|
|
245
|
+
Example:
|
|
246
|
+
>>> import numpy2 as np2
|
|
247
|
+
>>> data = [{'id': 1, 'value': 3.14}]
|
|
248
|
+
>>> converted = np2.batch_convert(data, {'id': 'int32', 'value': 'float32'})
|
|
249
|
+
"""
|
|
250
|
+
|
|
251
|
+
if dtype_map is None:
|
|
252
|
+
dtype_map = {}
|
|
253
|
+
|
|
254
|
+
converted = []
|
|
255
|
+
for record in data:
|
|
256
|
+
new_record = {}
|
|
257
|
+
for key, value in record.items():
|
|
258
|
+
target_dtype = dtype_map.get(key)
|
|
259
|
+
if target_dtype:
|
|
260
|
+
new_record[key] = safe_cast(value, target_dtype)
|
|
261
|
+
else:
|
|
262
|
+
new_record[key] = numpy_to_python(value)
|
|
263
|
+
converted.append(new_record)
|
|
264
|
+
|
|
265
|
+
return converted
|
numpy2/core.py
ADDED
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
"""
|
|
2
|
+
numpy2.core - Core serialization and type conversion utilities
|
|
3
|
+
|
|
4
|
+
Solves NumPy JSON serialization issues:
|
|
5
|
+
- TypeError: Object of type int64 is not JSON serializable
|
|
6
|
+
- Silent data loss from NumPy type conversions
|
|
7
|
+
- Performance degradation in web APIs
|
|
8
|
+
- Framework incompatibility with NumPy dtypes
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import numpy as np
|
|
13
|
+
import pandas as pd
|
|
14
|
+
from typing import Any, Dict, List, Union, Optional
|
|
15
|
+
from decimal import Decimal
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class JSONEncoder(json.JSONEncoder):
|
|
19
|
+
"""
|
|
20
|
+
Custom JSON encoder that handles NumPy and pandas data types.
|
|
21
|
+
|
|
22
|
+
SOLVES: TypeError: Object of type int64 is not JSON serializable
|
|
23
|
+
|
|
24
|
+
Features:
|
|
25
|
+
- Automatic NumPy dtype conversion
|
|
26
|
+
- pandas Series/DataFrame support
|
|
27
|
+
- Preserves data integrity
|
|
28
|
+
- Zero configuration
|
|
29
|
+
|
|
30
|
+
Example:
|
|
31
|
+
>>> import json
|
|
32
|
+
>>> import numpy as np
|
|
33
|
+
>>> arr = np.array([1, 2, 3], dtype=np.int64)
|
|
34
|
+
>>> json.dumps(arr, cls=JSONEncoder)
|
|
35
|
+
'[1, 2, 3]'
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def default(self, obj: Any) -> Any:
|
|
39
|
+
"""Convert non-standard types to JSON-serializable format."""
|
|
40
|
+
|
|
41
|
+
# NumPy integer types
|
|
42
|
+
if isinstance(obj, np.integer):
|
|
43
|
+
return int(obj)
|
|
44
|
+
|
|
45
|
+
# NumPy float types
|
|
46
|
+
elif isinstance(obj, np.floating):
|
|
47
|
+
# Handle special float values
|
|
48
|
+
if np.isnan(obj):
|
|
49
|
+
return None # or "NaN" if preferred
|
|
50
|
+
elif np.isinf(obj):
|
|
51
|
+
return None # or "Infinity" if preferred
|
|
52
|
+
return float(obj)
|
|
53
|
+
|
|
54
|
+
# NumPy arrays
|
|
55
|
+
elif isinstance(obj, np.ndarray):
|
|
56
|
+
return obj.tolist()
|
|
57
|
+
|
|
58
|
+
# NumPy scalar types
|
|
59
|
+
elif isinstance(obj, np.generic):
|
|
60
|
+
return obj.item()
|
|
61
|
+
|
|
62
|
+
# pandas Series
|
|
63
|
+
elif isinstance(obj, pd.Series):
|
|
64
|
+
return obj.to_dict()
|
|
65
|
+
|
|
66
|
+
# pandas DataFrame
|
|
67
|
+
elif isinstance(obj, pd.DataFrame):
|
|
68
|
+
return obj.to_dict(orient='records')
|
|
69
|
+
|
|
70
|
+
# pandas Index
|
|
71
|
+
elif isinstance(obj, pd.Index):
|
|
72
|
+
return obj.tolist()
|
|
73
|
+
|
|
74
|
+
# Decimal support
|
|
75
|
+
elif isinstance(obj, Decimal):
|
|
76
|
+
return float(obj)
|
|
77
|
+
|
|
78
|
+
# datetime64
|
|
79
|
+
elif isinstance(obj, np.datetime64):
|
|
80
|
+
return str(obj)
|
|
81
|
+
|
|
82
|
+
# timedelta64
|
|
83
|
+
elif isinstance(obj, np.timedelta64):
|
|
84
|
+
return str(obj)
|
|
85
|
+
|
|
86
|
+
# complex numbers
|
|
87
|
+
elif isinstance(obj, (complex, np.complexfloating)):
|
|
88
|
+
return {"real": obj.real, "imag": obj.imag}
|
|
89
|
+
|
|
90
|
+
# Default fallback
|
|
91
|
+
return super().default(obj)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class JSONDecoder(json.JSONDecoder):
|
|
95
|
+
"""
|
|
96
|
+
Custom JSON decoder that reconstructs NumPy arrays from JSON.
|
|
97
|
+
|
|
98
|
+
SOLVES: Data type loss when converting JSON back to NumPy
|
|
99
|
+
|
|
100
|
+
Features:
|
|
101
|
+
- Intelligent type inference
|
|
102
|
+
- Preserves numeric precision
|
|
103
|
+
- Optional NumPy array reconstruction
|
|
104
|
+
|
|
105
|
+
Example:
|
|
106
|
+
>>> import json
|
|
107
|
+
>>> data = '[1, 2, 3]'
|
|
108
|
+
>>> decoder = JSONDecoder()
|
|
109
|
+
>>> decoder.decode(data)
|
|
110
|
+
[1, 2, 3]
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
def __init__(self, to_numpy: bool = False, dtype: Optional[str] = None, *args, **kwargs):
|
|
114
|
+
"""
|
|
115
|
+
Initialize decoder.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
to_numpy: If True, convert lists to numpy arrays
|
|
119
|
+
dtype: NumPy dtype to use for arrays (e.g., 'int64', 'float32')
|
|
120
|
+
"""
|
|
121
|
+
self.to_numpy = to_numpy
|
|
122
|
+
self.dtype = dtype
|
|
123
|
+
super().__init__(object_hook=self.object_hook, *args, **kwargs)
|
|
124
|
+
|
|
125
|
+
def object_hook(self, obj: Dict) -> Any:
|
|
126
|
+
"""Convert objects back to appropriate types."""
|
|
127
|
+
if self.to_numpy and isinstance(obj, dict) and "real" in obj and "imag" in obj:
|
|
128
|
+
return complex(obj["real"], obj["imag"])
|
|
129
|
+
return obj
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def to_json(obj: Any, indent: Optional[int] = None, **kwargs) -> str:
|
|
133
|
+
"""
|
|
134
|
+
Convert NumPy arrays and pandas objects to JSON string.
|
|
135
|
+
|
|
136
|
+
SOLVES: TypeError: Object of type int64 is not JSON serializable
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
obj: NumPy array, pandas object, or standard Python object
|
|
140
|
+
indent: JSON indentation level
|
|
141
|
+
**kwargs: Additional arguments for json.dumps
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
JSON string representation
|
|
145
|
+
|
|
146
|
+
Example:
|
|
147
|
+
>>> import numpy as np
|
|
148
|
+
>>> import numpy2 as np2
|
|
149
|
+
>>> arr = np.array([1, 2, 3], dtype=np.int64)
|
|
150
|
+
>>> json_str = np2.to_json(arr)
|
|
151
|
+
>>> print(json_str)
|
|
152
|
+
[1, 2, 3]
|
|
153
|
+
"""
|
|
154
|
+
return json.dumps(obj, cls=JSONEncoder, indent=indent, **kwargs)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def from_json(
|
|
158
|
+
json_str: str,
|
|
159
|
+
to_numpy: bool = False,
|
|
160
|
+
dtype: Optional[str] = None
|
|
161
|
+
) -> Any:
|
|
162
|
+
"""
|
|
163
|
+
Deserialize JSON string to Python objects (optionally NumPy arrays).
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
json_str: JSON string to deserialize
|
|
167
|
+
to_numpy: Convert to numpy arrays
|
|
168
|
+
dtype: Target numpy dtype
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
Deserialized Python object or NumPy array
|
|
172
|
+
|
|
173
|
+
Example:
|
|
174
|
+
>>> import numpy2 as np2
|
|
175
|
+
>>> json_str = '[1, 2, 3]'
|
|
176
|
+
>>> arr = np2.from_json(json_str, to_numpy=True, dtype='int64')
|
|
177
|
+
>>> type(arr)
|
|
178
|
+
<class 'numpy.ndarray'>
|
|
179
|
+
"""
|
|
180
|
+
decoder = JSONDecoder(to_numpy=to_numpy, dtype=dtype)
|
|
181
|
+
result = decoder.decode(json_str)
|
|
182
|
+
|
|
183
|
+
if to_numpy and isinstance(result, list):
|
|
184
|
+
result = np.array(result, dtype=dtype)
|
|
185
|
+
|
|
186
|
+
return result
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def serialize(obj: Any, include_metadata: bool = False) -> Dict[str, Any]:
|
|
190
|
+
"""
|
|
191
|
+
Serialize NumPy/pandas objects to JSON-safe dictionary.
|
|
192
|
+
|
|
193
|
+
SOLVES: Web framework incompatibility with NumPy dtypes
|
|
194
|
+
|
|
195
|
+
Perfect for FastAPI JSONResponse, Flask jsonify, Django JsonResponse
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
obj: Object to serialize
|
|
199
|
+
include_metadata: Include shape, dtype, and other metadata
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
JSON-safe dictionary
|
|
203
|
+
|
|
204
|
+
Example:
|
|
205
|
+
>>> import numpy as np
|
|
206
|
+
>>> import numpy2 as np2
|
|
207
|
+
>>> arr = np.array([[1, 2], [3, 4]], dtype=np.int32)
|
|
208
|
+
>>> result = np2.serialize(arr, include_metadata=True)
|
|
209
|
+
>>> result
|
|
210
|
+
{
|
|
211
|
+
'data': [[1, 2], [3, 4]],
|
|
212
|
+
'shape': [2, 2],
|
|
213
|
+
'dtype': 'int32',
|
|
214
|
+
'size': 4
|
|
215
|
+
}
|
|
216
|
+
"""
|
|
217
|
+
|
|
218
|
+
if isinstance(obj, np.ndarray):
|
|
219
|
+
result = {
|
|
220
|
+
'data': obj.tolist(),
|
|
221
|
+
}
|
|
222
|
+
if include_metadata:
|
|
223
|
+
result.update({
|
|
224
|
+
'shape': list(obj.shape),
|
|
225
|
+
'dtype': str(obj.dtype),
|
|
226
|
+
'size': int(obj.size),
|
|
227
|
+
'ndim': int(obj.ndim),
|
|
228
|
+
})
|
|
229
|
+
return result
|
|
230
|
+
|
|
231
|
+
elif isinstance(obj, pd.DataFrame):
|
|
232
|
+
result = {
|
|
233
|
+
'data': obj.to_dict(orient='records'),
|
|
234
|
+
}
|
|
235
|
+
if include_metadata:
|
|
236
|
+
result.update({
|
|
237
|
+
'shape': list(obj.shape),
|
|
238
|
+
'columns': list(obj.columns),
|
|
239
|
+
'index': obj.index.tolist(),
|
|
240
|
+
})
|
|
241
|
+
return result
|
|
242
|
+
|
|
243
|
+
elif isinstance(obj, pd.Series):
|
|
244
|
+
result = {
|
|
245
|
+
'data': obj.to_dict(),
|
|
246
|
+
}
|
|
247
|
+
if include_metadata:
|
|
248
|
+
result.update({
|
|
249
|
+
'name': obj.name,
|
|
250
|
+
'dtype': str(obj.dtype),
|
|
251
|
+
'index': obj.index.tolist(),
|
|
252
|
+
})
|
|
253
|
+
return result
|
|
254
|
+
|
|
255
|
+
elif isinstance(obj, dict):
|
|
256
|
+
# Recursively serialize nested structures
|
|
257
|
+
return {k: serialize(v, include_metadata) if isinstance(v, (np.ndarray, pd.DataFrame, pd.Series)) else v
|
|
258
|
+
for k, v in obj.items()}
|
|
259
|
+
|
|
260
|
+
elif isinstance(obj, (list, tuple)):
|
|
261
|
+
return [serialize(item, include_metadata) if isinstance(item, (np.ndarray, pd.DataFrame, pd.Series)) else item
|
|
262
|
+
for item in obj]
|
|
263
|
+
|
|
264
|
+
else:
|
|
265
|
+
return json.loads(json.dumps(obj, cls=JSONEncoder))
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def deserialize(
|
|
269
|
+
data: Dict[str, Any],
|
|
270
|
+
to_numpy: bool = True,
|
|
271
|
+
dtype: Optional[str] = None
|
|
272
|
+
) -> Union[np.ndarray, pd.DataFrame, Any]:
|
|
273
|
+
"""
|
|
274
|
+
Deserialize JSON-safe dictionary back to NumPy/pandas objects.
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
data: Dictionary with 'data' key and optional metadata
|
|
278
|
+
to_numpy: Convert to NumPy array (if False, returns list)
|
|
279
|
+
dtype: Target dtype for conversion
|
|
280
|
+
|
|
281
|
+
Returns:
|
|
282
|
+
NumPy array, pandas DataFrame, or raw data
|
|
283
|
+
|
|
284
|
+
Example:
|
|
285
|
+
>>> import numpy2 as np2
|
|
286
|
+
>>> serialized = {
|
|
287
|
+
... 'data': [[1, 2], [3, 4]],
|
|
288
|
+
... 'shape': [2, 2],
|
|
289
|
+
... 'dtype': 'int32'
|
|
290
|
+
... }
|
|
291
|
+
>>> arr = np2.deserialize(serialized)
|
|
292
|
+
>>> type(arr)
|
|
293
|
+
<class 'numpy.ndarray'>
|
|
294
|
+
"""
|
|
295
|
+
|
|
296
|
+
if not isinstance(data, dict):
|
|
297
|
+
return data
|
|
298
|
+
|
|
299
|
+
if 'data' not in data:
|
|
300
|
+
return data
|
|
301
|
+
|
|
302
|
+
array_data = data['data']
|
|
303
|
+
|
|
304
|
+
if to_numpy:
|
|
305
|
+
target_dtype = dtype or data.get('dtype', None)
|
|
306
|
+
return np.array(array_data, dtype=target_dtype)
|
|
307
|
+
|
|
308
|
+
return array_data
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def array(
|
|
312
|
+
data: Any,
|
|
313
|
+
dtype: Optional[str] = None,
|
|
314
|
+
**kwargs
|
|
315
|
+
) -> np.ndarray:
|
|
316
|
+
"""
|
|
317
|
+
Create NumPy array with automatic type handling.
|
|
318
|
+
|
|
319
|
+
Wrapper around numpy.array with better type inference for web data.
|
|
320
|
+
|
|
321
|
+
Args:
|
|
322
|
+
data: Array data
|
|
323
|
+
dtype: Data type
|
|
324
|
+
**kwargs: Additional arguments for numpy.array
|
|
325
|
+
|
|
326
|
+
Returns:
|
|
327
|
+
NumPy ndarray
|
|
328
|
+
"""
|
|
329
|
+
return np.array(data, dtype=dtype, **kwargs)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
class ndarray:
|
|
333
|
+
"""
|
|
334
|
+
Enhanced NumPy ndarray wrapper with web-friendly methods.
|
|
335
|
+
|
|
336
|
+
Adds convenience methods for serialization without modifying
|
|
337
|
+
the original NumPy array.
|
|
338
|
+
"""
|
|
339
|
+
|
|
340
|
+
def __init__(self, data: Any, dtype: Optional[str] = None):
|
|
341
|
+
self._array = np.array(data, dtype=dtype)
|
|
342
|
+
|
|
343
|
+
def to_json(self, indent: Optional[int] = None) -> str:
|
|
344
|
+
"""Convert to JSON string."""
|
|
345
|
+
return to_json(self._array, indent=indent)
|
|
346
|
+
|
|
347
|
+
def to_dict(self, include_metadata: bool = False) -> Dict:
|
|
348
|
+
"""Convert to JSON-safe dictionary."""
|
|
349
|
+
return serialize(self._array, include_metadata=include_metadata)
|
|
350
|
+
|
|
351
|
+
@property
|
|
352
|
+
def numpy(self) -> np.ndarray:
|
|
353
|
+
"""Get underlying NumPy array."""
|
|
354
|
+
return self._array
|
|
355
|
+
|
|
356
|
+
def __repr__(self) -> str:
|
|
357
|
+
return f"numpy2.ndarray({self._array})"
|
|
358
|
+
|
|
359
|
+
def __str__(self) -> str:
|
|
360
|
+
return str(self._array)
|