File size: 20,705 Bytes
93d2194
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
================================================================================
  NUMPY - USER GUIDE (Android Python STB)
  Generated by RIMI
================================================================================
  Covers: what numpy is, install/verify, arrays, dtypes, math, indexing,
          reshaping, linear algebra, random, file I/O, numpy + Pillow,
          printing in the terminal, and common pitfalls.

  Written for: Python 3.12.2 (RIMI build) on Android
  Version:     numpy 2.5.2
  Scripts dir: /storage/emulated/0/PythonSTB/Scripts/
  Installed:   /data/user/0/com.pythonstb.rimi/files/python/lib/python3.12/site-packages/
================================================================================


--------------------------------------------------------------------------------
1) WHAT IS NUMPY?
--------------------------------------------------------------------------------

NumPy is the fundamental library for fast numerical computing in Python.
It adds:
  - n-dimensional arrays (ndarray) - faster and more compact than Python lists
  - element-wise math without writing loops
  - linear algebra (matrix multiply, inverse, solve, eig, ...)
  - random numbers, FFT, sorting, statistics
  - a bridge to C/Python extensions (OpenCV, Pillow, pandas, scipy, ...)

Typical speedup vs plain Python loops: 10x - 100x or more.

This Android build of numpy is compiled with OpenBLAS, so matrix operations
are optimized (BLAS/LAPACK) on both arm64 and x86_64.

    import numpy as np
    print(np.__version__)          # 2.5.2
    print(np.show_config())        # shows BLAS/LAPACK backend + build info


--------------------------------------------------------------------------------
2) INSTALL / VERIFY
--------------------------------------------------------------------------------

Install (already done, but if you ever reinstall):
    pip install numpy              # or install the wheel file directly

Quick smoke test - run in your app:
    import numpy as np
    a = np.arange(12).reshape(3, 4)
    print(a)
    print("sum:", a.sum(), "max:", a.max(), "shape:", a.shape, "dtype:", a.dtype)
    print("blas:", np.__config__.show("build") if hasattr(np.__config__, "show") else "n/a")

Expected output pattern:
    [[ 0  1  2  3]
     [ 4  5  6  7]
     [ 8  9 10 11]]
    sum: 66  max: 11  shape: (3, 4)  dtype: int64


--------------------------------------------------------------------------------
3) ARRAYS - CREATION BASICS
--------------------------------------------------------------------------------

    import numpy as np

    # from a list
    a = np.array([1, 2, 3])                     # 1-D, dtype int64
    b = np.array([[1, 2, 3], [4, 5, 6]])        # 2-D shape (2, 3)
    c = np.array([1.0, 2.0, 3.0])               # float64

    # zeros / ones / full / identity
    np.zeros((3, 4))
    np.ones((2, 2))
    np.full((2, 3), 7.5)
    np.eye(4)                                   # 4x4 identity
    np.identity(3)

    # ranges
    np.arange(10)                # 0..9
    np.arange(0, 1, 0.1)         # 0.0, 0.1, ... 0.9
    np.linspace(0, 1, 5)         # 5 evenly spaced points 0..1
    np.logspace(1, 3, 3)         # 10, 100, 1000

    # random
    rng = np.random.default_rng(seed=42)        # reproducible
    rng.random((3, 3))                          # uniform [0,1)
    rng.integers(0, 10, size=(2, 5))            # integers 0..9
    rng.normal(0, 1, size=(4,))                 # normal dist
    rng.permutation(10)                         # shuffled 0..9

    # important attributes
    a = np.array([[1, 2, 3], [4, 5, 6]])
    a.shape          # (2, 3)
    a.ndim           # 2
    a.size           # 6
    a.dtype          # dtype('int64')
    a.itemsize       # bytes per element
    a.nbytes         # total bytes


--------------------------------------------------------------------------------
4) DTYPES (data types)
--------------------------------------------------------------------------------

    np.int8    np.int16   np.int32   np.int64     # signed integers
    np.uint8   np.uint16  np.uint32  np.uint64    # unsigned integers
    np.float32 np.float64                        # floats (f32 is half memory)
    np.complex64 np.complex128                   # complex
    np.bool_                                      # boolean
    np.str_     np.bytes_                         # strings (avoid for math)
    'f4','f8','i1','i2','i4','i8','u1','u2','u4','u8'   # short aliases

    a = np.array([1, 2, 3], dtype=np.uint8)
    b = a.astype(np.float32)      # convert
    c = a.astype('f4')

Rules of thumb:
  - use np.float64 (default) for general math
  - use np.float32 or np.uint8 for big arrays / images (half the memory)
  - beware overflow: np.array([200], np.uint8) + 100 wraps to 44
  - beware int division: np.array([5]) // 2 == 2 (floor), np.array([5]) / 2 == 2.5


--------------------------------------------------------------------------------
5) INDEXING AND SLICING
--------------------------------------------------------------------------------

    a = np.arange(12).reshape(3, 4)
    # [[ 0  1  2  3]
    #  [ 4  5  6  7]
    #  [ 8  9 10 11]]

    a[0]            # row 0: [0 1 2 3]
    a[0, 2]         # scalar 2
    a[:, 1]         # column 1: [1 5 9]
    a[1:, :2]       # rows 1..2, cols 0..1
    a[-1]           # last row
    a[::2]          # every other row

    # boolean masking
    a[a > 5]                          # 1-D array of values > 5
    a[(a > 2) & (a < 8)]              # combine masks with & |
    a[a % 2 == 0]                     # even values

    # fancy indexing with arrays
    a[[0, 2]]                         # rows 0 and 2
    idx = np.array([3, 1])
    a[:, idx]                         # columns 3 and 1

    # assignment with masks
    a[a < 5] = 0                      # zero out everything below 5
    a[:, 0] = -1                      # set first column


--------------------------------------------------------------------------------
6) SHAPES - RESHAPE / FLATTEN / TRANSPOSE / BROADCAST
--------------------------------------------------------------------------------

    a = np.arange(24)

    a.reshape(4, 6)       # same data, new shape
    a.reshape(2, 3, 4)    # 3-D
    a.reshape(-1, 6)      # -1 = auto: (4, 6)
    a.ravel()             # flatten to 1-D (may copy)
    a.flatten()           # always a copy, 1-D
    a.T                   # transpose
    a.reshape(4, 6).T.shape          # (6, 4)

    # add a new axis
    v = np.array([1, 2, 3])
    v[np.newaxis, :].shape            # (1, 3)
    v[:, np.newaxis].shape            # (3, 1)

    # BROADCASTING: shapes line up from the right
    m = np.ones((3, 4))
    m + 1                     # scalar broadcast
    m * np.array([10, 20, 30, 40])     # row vector broadcast over rows
    m + np.array([[1], [2], [3]])      # column vector broadcast over cols
    # rule: dimensions must be equal or one of them must be 1


--------------------------------------------------------------------------------
7) MATH - ELEMENT-WISE AND REDUCTIONS
--------------------------------------------------------------------------------

    a = np.array([1., 2., 3., 4.])

    a + 1, a - 1, a * 2, a / 2, a ** 2, -a          # element-wise
    np.sqrt(a), np.abs(a), np.exp(a), np.log(a)
    np.sin(a), np.cos(a), np.tan(a), np.arctan(a)
    np.round(a), np.floor(a), np.ceil(a), np.clip(a, 1.5, 3.5)
    np.sign(a), np.mod(a, 2), np.power(a, 3)
    np.maximum(a, 2), np.minimum(a, 3)

    # reductions (default: over all elements)
    a.sum()   a.mean()   a.min()   a.max()   a.std()   a.var()
    a.prod()  a.argmax() a.argmin() a.cumsum() a.cumprod()
    np.median(a)   np.percentile(a, 50)   np.ptp(a)          # peak-to-peak

    # along an axis
    m = np.arange(6).reshape(2, 3)
    m.sum(axis=0)        # per column: [3 5 7]
    m.sum(axis=1)        # per row:    [3 12]
    m.max(axis=0), m.min(axis=1)

    # comparisons return boolean arrays
    (a > 2)            # array([False, False,  True,  True])
    np.any(a > 2)      # True
    np.all(a > 2)      # False
    np.count_nonzero(a > 2)   # 2


--------------------------------------------------------------------------------
8) LINEAR ALGEBRA (OpenBLAS accelerated)
--------------------------------------------------------------------------------

    a = np.array([[1., 2.], [3., 4.]])
    b = np.array([[5., 6.], [7., 8.]])

    a @ b                    # matrix multiply (preferred)
    np.matmul(a, b)          # same
    a.dot(b)                 # same (older style)
    a * b                    # ELEMENT-WISE, NOT matrix multiply

    np.linalg.inv(a)         # inverse
    np.linalg.det(a)         # determinant
    np.linalg.solve(a, np.array([1., 2.]))   # solve a x = b
    np.linalg.eig(a)         # eigenvalues + eigenvectors
    np.linalg.norm(a)        # Frobenius norm
    np.linalg.qr(a), np.linalg.svd(a)
    np.linalg.pinv(a)        # pseudo-inverse
    np.linalg.matrix_power(a, 3)

    # vector ops
    v = np.array([1., 2., 3.])
    w = np.array([4., 5., 6.])
    np.dot(v, w)             # dot product 32.0
    np.cross(v, w)           # cross product
    np.inner(v, w)           # inner product

    # useful on the device: transform a 3D point / homography
    M = np.array([[1., 0., 10.], [0., 1., 20.], [0., 0., 1.]])
    p = np.array([5., 6., 1.])
    out = M @ p


--------------------------------------------------------------------------------
9) STACKING, SPLITTING, CONCATENATING
--------------------------------------------------------------------------------

    a = np.array([1, 2, 3])
    b = np.array([4, 5, 6])

    np.concatenate((a, b))         # [1 2 3 4 5 6]
    np.stack((a, b))               # shape (2, 3)
    np.vstack((a, b))              # vertical: shape (2, 3)
    np.hstack((a, b))              # horizontal: [1 2 3 4 5 6]
    np.dstack((a, b))              # depth: shape (1, 3, 2)

    m1 = np.ones((2, 2))
    m2 = np.zeros((2, 2))
    np.vstack((m1, m2))            # (4, 2)
    np.hstack((m1, m2))            # (2, 4)

    # split
    x = np.arange(10)
    np.split(x, 2)                 # two arrays of 5
    np.array_split(x, 3)           # uneven split
    np.hsplit(m1, 2), np.vsplit(m1, 2)


--------------------------------------------------------------------------------
10) RANDOM NUMBERS
--------------------------------------------------------------------------------

    rng = np.random.default_rng(2026)      # always seed for reproducibility
    rng.random((2, 3))                     # [0,1) floats
    rng.integers(1, 7, size=10)            # die rolls 1..6
    rng.normal(loc=0, scale=1, size=(3, 3))
    rng.uniform(0, 10, size=5)
    rng.choice(np.arange(5), size=10, replace=True)
    rng.shuffle(np.arange(10))             # in place
    rng.standard_normal((4,))

    # old style (np.random.rand etc.) also works, but default_rng is preferred.


--------------------------------------------------------------------------------
11) SAVE / LOAD DATA (files)
--------------------------------------------------------------------------------

    a = np.arange(12).reshape(3, 4)

    # numpy binary format (fast, compact, one array per file)
    np.save("/storage/emulated/0/Download/a.npy", a)
    b = np.load("/storage/emulated/0/Download/a.npy")

    # compressed multi-array archive
    np.savez("/storage/emulated/0/Download/data.npz", x=a, y=a * 2)
    d = np.load("/storage/emulated/0/Download/data.npz")
    d["x"], d["y"]
    # or np.savez_compressed(...) for smaller files

    # text (human readable)
    np.savetxt("/storage/emulated/0/Download/a.csv", a, delimiter=",")
    c = np.loadtxt("/storage/emulated/0/Download/a.csv", delimiter=",")
    # note: loadtxt returns float64; use dtype= to control
    np.savetxt("/storage/emulated/0/Download/a.tsv", a, delimiter="\t",
               fmt="%.2f")

    # plain binary (raw, no header)
    a.tofile("/storage/emulated/0/Download/a.bin")
    np.fromfile("/storage/emulated/0/Download/a.bin", dtype=np.int64)


--------------------------------------------------------------------------------
12) NUMPY + PILLOW (images are just arrays)
--------------------------------------------------------------------------------

    from PIL import Image
    import numpy as np

    # PIL image -> numpy array  (H, W, C)
    im = Image.open("/storage/emulated/0/Download/photo.jpg").convert("RGB")
    arr = np.asarray(im)
    print(arr.shape)          # (height, width, 3)
    print(arr.dtype)          # uint8

    # numpy array -> PIL image
    img2 = Image.fromarray(arr)
    img2.save("/storage/emulated/0/Download/out.jpg", quality=95)

    # grayscale -> (H, W)
    gray = np.asarray(im.convert("L"))
    # alpha -> (H, W, 4)
    rgba = np.asarray(im.convert("RGBA"))

    # process with numpy, then convert back
    arr2 = arr[:, ::-1]                       # mirror
    arr3 = np.clip(arr.astype(np.int16) + 50, 0, 255).astype(np.uint8)  # brighten
    arr4 = 255 - arr                          # invert
    arr5 = arr.copy(); arr5[..., 0] = 255     # force red channel to max
    Image.fromarray(arr2).save("/storage/emulated/0/Download/mirror.png")
    Image.fromarray(arr4).save("/storage/emulated/0/Download/invert.png")

    # crop = slice
    crop = arr[100:200, 50:150]
    Image.fromarray(crop).save("/storage/emulated/0/Download/crop.png")

    # resize with numpy (nearest) - better to use PIL resize normally
    small = arr[::4, ::4]                     # nearest-neighbour downsample

    # build a gradient image
    h, w = 200, 200
    yy, xx = np.mgrid[0:h, 0:w]
    grad = np.stack([
        (xx * 255 // max(1, w - 1)).astype(np.uint8),
        (yy * 255 // max(1, h - 1)).astype(np.uint8),
        ((xx + yy) * 255 // max(1, (w + h) - 2)).astype(np.uint8),
    ], axis=2)
    Image.fromarray(grad).save("/storage/emulated/0/Download/gradient.png")

    IMPORTANT:
      np.asarray(im) may share memory with the PIL image. If you modify arr
      in place (arr[...] = ...), the image changes too. Use arr.copy() when
      you need an independent buffer.
      uint8 arithmetic overflows (255+1 -> 0). Cast to int16 first for math:
      arr.astype(np.int16).


--------------------------------------------------------------------------------
13) PRINTING ARRAYS / MATRICES IN THE TERMINAL
--------------------------------------------------------------------------------

    a = np.arange(12).reshape(3, 4)

    print(a)                     # default pretty print
    print(a.tolist())            # as nested Python lists

    # control the output
    import numpy as np
    np.set_printoptions(
        precision=2,             # decimals for floats
        threshold=20,            # max elements before "..."
        edgeitems=3,
        linewidth=120,
        suppress=True,           # avoid scientific notation for small numbers
        formatter={"float_kind": lambda v: f"{v:7.2f}"},
    )
    print(a)

    # a small helper to show an array as a grid of numbers
    def print_matrix(m):
        m = np.asarray(m)
        for row in m:
            print(" ".join(f"{v:8.3f}" for v in row))

    print_matrix(np.random.default_rng(1).random((3, 5)))

    # print a 2D array as colored blocks (with ANSI)
    def print_heatmap(m, cols=40):
        m = np.asarray(m, dtype=np.float64)
        if m.ndim != 2:
            m = m.reshape(m.shape[0], -1)
        h, w = m.shape
        # resample columns to terminal width (nearest)
        if w > cols:
            m = m[:, ::max(1, w // cols)][:, :cols]
            h, w = m.shape
        lo, hi = m.min(), m.max()
        rng = (hi - lo) or 1.0
        out = []
        for row in m:
            line = ""
            for v in row:
                t = (v - lo) / rng            # 0..1
                r = int(255 * t)
                g = int(255 * (1 - t))
                line += f"\x1b[48;2;{r};{g};0m "
            out.append(line + "\x1b[0m")
        print("\n".join(out))

    # example: heatmap of a sinc function
    import numpy as np
    x = np.linspace(-6, 6, 80)
    y = np.linspace(-6, 6, 40)
    yy, xx = np.meshgrid(y, x, indexing="ij")
    z = np.sinc(np.sqrt(xx ** 2 + yy ** 2))
    print_heatmap(z, cols=60)

    # ASCII density plot from a 2D array
    RAMP = " .:-=+*#%@"
    def print_ascii_grid(m, cols=60):
        m = np.asarray(m, dtype=np.float64)
        if w := m.shape[1] > cols:
            m = m[:, ::w // cols + 1]
        lo, hi = m.min(), m.max()
        rng = (hi - lo) or 1.0
        for row in m:
            print("".join(RAMP[int((v - lo) / rng * (len(RAMP) - 1))] for v in row))

    print_ascii_grid(z, cols=70)


--------------------------------------------------------------------------------
14) USEFUL EVERYDAY SNIPPETS
--------------------------------------------------------------------------------

Statistics of a numeric column:
    data = np.array([1., 2., 3., 4., 100.])
    print("mean %.2f  std %.2f  median %.2f  min %.0f  max %.0f" % (
        data.mean(), data.std(), np.median(data), data.min(), data.max()))

Normalize to [0, 1]:
    x = np.array([3., 1., 2., 0.])
    n = (x - x.min()) / (x.max() - x.min())

Standard score (z-score):
    z = (x - x.mean()) / x.std()

One-hot encode categories:
    cats = np.array([0, 2, 1, 2, 0])
    onehot = np.eye(3)[cats]

Extract the diagonal of a matrix:
    np.diag(np.arange(9).reshape(3, 3))        # [0 4 8]

Histogram:
    values, edges = np.histogram(rng.normal(size=1000), bins=20)

Find the index of the maximum in each row:
    m = rng.random((5, 8))
    m.argmax(axis=1)

Clip and cast for image math:
    arr.astype(np.float32) * 1.2 + 10  -> clip -> uint8

Simple FIR smoothing:
    kernel = np.ones(5) / 5
    smooth = np.convolve(signal, kernel, mode="same")

Measure elapsed time:
    import time
    t0 = time.perf_counter()
    ... work ...
    print("elapsed %.3f s" % (time.perf_counter() - t0))


--------------------------------------------------------------------------------
15) NUMPY + PANDAS / OTHER PACKAGES
--------------------------------------------------------------------------------

numpy is the foundation for many packages already on the device:

    import numpy as np
    import pandas as pd
    df = pd.DataFrame({"a": [1, 2, 3], "b": [4.0, 5.0, 6.0]})
    print(df)
    arr = df.to_numpy()          # DataFrame -> numpy array
    # pandas is built on numpy; everything in this guide applies.

    # OpenCV (if installed) also exchanges buffers directly:
    # cv2.cvtColor(img_np, cv2.COLOR_BGR2RGB)


--------------------------------------------------------------------------------
16) PITFALLS & NOTES ON THIS BUILD
--------------------------------------------------------------------------------

- Version is 2.5.2. numpy 2.x changed some 1.x behaviors:
    * np.array(None) no longer allowed
    * np.find_common_type removed
    * copy keyword defaults changed (np.array(..., copy=None) is common)
  Code written for numpy 1.x may need small fixes.
- uint8 overflow: cast to a wider dtype before math on images.
- Integer division: use // for floor, / for true (float) division.
- Broadcasting: shapes must be equal or one must be 1; (3,1) * (1,4) -> (3,4).
- np.asarray may share memory with the source (PIL image); use .copy() to
  detach.
- OpenBLAS is compiled in - @ and np.linalg.* are fast; no action needed.
- Wheels here are tagged cp312-cp312-linux_aarch64 / linux_x86_64
  (this Android build uses the "linux" platform tag for numpy). Make sure you
  install the wheel that matches the device ABI (arm64 phone vs x86_64 emulator).
- Temp files are not needed: save directly to /storage/emulated/0/Download/
  or any folder the app can write.
- For very large arrays, watch memory: a float64 array of 10M elements uses
  80 MB. Use float32 or appropriate dtypes when possible.
- Reinstall safety: keep a copy of the wheel file
  (numpy-2.5.2-cp312-cp312-linux_aarch64.whl or _x86_64) in
  /storage/emulated/0/Download/ so you can reinstall if needed.

================================================================================
  END OF GUIDE
  Generated by RIMI
================================================================================