{"slug": "python-numpy-library", "title": "Python NumPy Library", "summary": "NumPy, the foundational open-source Python library for numerical computation, provides the N-dimensional array (ndarray) and underpins major data-science tools such as Pandas, SciPy, scikit-learn, and TensorFlow. The library offers a broad suite of array creation methods, properties, and manipulation techniques, including reshaping, flattening, and copy-versus-view semantics, as demonstrated in a practical tutorial.", "body_md": "NumPy (Numerical Python) is a foundational open-source Python library for numerical and mathematical computation.\n\nIt introduces the N-dimensional array (`ndarray`\n\n), a high-performance data structure for storing and manipulating large datasets efficiently. NumPy forms the computational foundation of the Python data-science ecosystem; major libraries such as Pandas, SciPy, scikit-learn, and TensorFlow build directly upon it.\n\nThis tutorial is designed to provide a concise yet practical overview of NumPy and to support day-to-day technical work through clear, task-oriented examples.\n\nNumPy provides a broad suite of tools for numerical computation, including:\n\n*Using pip*\n\n```\npip install numpy\n```\n\n*Using conda*\n\n```\nconda install numpy\n```\n\n*Using poetry*\n\n```\npoetry add numpy\n```\n\nThe following test script can be used to confirm that NumPy has been installed correctly.\n\n``` python\nimport numpy as np\n\n# Check NumPy version\nprint(f\"NumPy version: {np.__version__}\")\nNumPy version: 2.5.1\n```\n\n`Ndarray`\n\ncreation\nThis section demonstrates several standard methods for creating NumPy arrays.\n\n*One-dimensional ndarray*\n\n```\narr1d = np.array([1, 2, 3, 4, 5])\nprint(\"From list:\", arr1d)\nFrom list: [1 2 3 4 5]\n```\n\n*Two-dimensional ndarray*\n\n```\narr2d= np.array([[1, 2, 3], [4, 5, 6]])\nprint(\"\\n2D array:\\n\", arr2d)\n2D array:\n [[1 2 3]\n [4 5 6]]\n```\n\n*Zero-filled and one-filled arrays*\n\n```\nzeros = np.zeros(5)\nprint(\"\\nZeros:\", zeros)\n\nones = np.ones((3, 3))\nprint(\"\\nOnes:\\n\", ones)\nZeros: [0. 0. 0. 0. 0.]\n\nOnes:\n [[1. 1. 1.]\n [1. 1. 1.]\n [1. 1. 1.]]\n```\n\n*Range-based and evenly spaced sequences*\n\n```\nrange_arr = np.arange(0, 10, 2)\nprint(\"\\nRange (0 to 10, step 2):\", range_arr)\n\nlinspace_arr = np.linspace(0, 10, 5)\nprint(\"\\nLinspace (0 to 10, 5 points):\", linspace_arr)\nRange (0 to 10, step 2): [0 2 4 6 8]\n\nLinspace (0 to 10, 5 points): [ 0.   2.5  5.   7.5 10. ]\n```\n\n*Identity and uninitialised arrays*\n\n```\n# create 2D identity matrix\nidentity = np.eye(3)\nprint(\"\\nIdentity matrix:\\n\", identity)\n\n# create 2D array with random garbage values, it is faster than random.rand() and random.randn()\nempty = np.empty((2, 2))\nprint(\"\\nEmpty array shape:\", empty.shape)\nIdentity matrix:\n [[1. 0. 0.]\n [0. 1. 0.]\n [0. 0. 1.]]\n\nEmpty array shape: (2, 2)\n```\n\n*Randomly generated arrays*\n\n```\n# create 2D array with random values between 0 and 1\nrandom_arr = np.random.rand(3, 3)\nprint(\"\\nRandom array (0-1):\\n\", random_arr)\n\n# create 2D array with random integers between 1 and 10\nrandom_int = np.random.randint(1, 10, size=(2, 3))\nprint(\"\\nRandom integers (1-10):\\n\", random_int)\nRandom array (0-1):\n [[0.55874991 0.81386435 0.31782834]\n [0.39704509 0.89016825 0.82541621]\n [0.10668708 0.15977588 0.65121931]]\n\nRandom integers (1-10):\n [[2 2 9]\n [8 4 4]]\n```\n\n`Ndarray`\n\nproperties and attributes\n*Reference array*\n\n```\narr = np.array([[1, 2, 3], [4, 5, 6]])\n```\n\n*Array properties*\n\n```\nprint(\"Shape:\", arr.shape)\nprint(\"Dimensions:\", arr.ndim)\nprint(\"Size (total elements):\", arr.size)\nprint(\"Data type:\", arr.dtype)\nprint(\"Item size (bytes):\", arr.itemsize)\nprint(\"Strides:\", arr.strides)\nShape: (2, 3)\nDimensions: 2\nSize (total elements): 6\nData type: int64\nItem size (bytes): 8\nStrides: (24, 8)\n```\n\n*Reshaping arrays*\n\n```\nreshaped = arr.reshape(3, 2)\nprint(\"\\nReshaped to (3, 2):\\n\", reshaped)\nReshaped to (3, 2):\n [[1 2]\n [3 4]\n [5 6]]\n```\n\n*Flattening arrays*\n\n```\nflattened = arr.flatten()\nprint(\"\\nFlattened:\", flattened)\nFlattened: [1 2 3 4 5 6]\n```\n\n*Copy versus view*\n\n```\narr_copy = arr.copy()\narr_view = arr.view()\narr_copy[0, 0] = 999\nprint(\"\\nOriginal:\", arr[0, 0])\nprint(\"Copy modified:\", arr_copy[0, 0])\narr_view[0, 0] = 888\nprint(\"Original after view modified:\", arr[0, 0])\nprint(\"View modified:\", arr_view[0, 0])\nOriginal: 1\nCopy modified: 999\nOriginal after view modified: 888\nView modified: 888\n```\n\nA view is typically faster than creating a copy, but it shares underlying data with the original ndarray. Consequently, modifying values through a view also modifies the original array. Views are particularly useful when adjusting shape or data-type representations without duplicating data.\n\n*Changing the shape of a view*\n\n```\n# 1. Create a flat 1D original array\noriginal = np.array([10, 20, 30, 40, 50, 60])\n\n# 2. Create a view and change its dimensions to a 2x3 matrix\nmatrix_view = original.reshape(2, 3)\n\n# 3. Check the shapes\nprint(\"Original Shape:\", original.shape)\nprint(\"View Shape:    \", matrix_view.shape)\nprint(\"\\nOriginal Array:\\n\", original)\nprint(\"\\nMatrix View:\\n\", matrix_view)\nOriginal Shape: (6,)\nView Shape:     (2, 3)\n\nOriginal Array:\n [10 20 30 40 50 60]\n\nMatrix View:\n [[10 20 30]\n [40 50 60]]\n```\n\n*Changing the data type representation of a view*\n\n```\n# 1. Create a flat 1D original array\noriginal = np.array([10, 20, 30, 40, 50, 60])\n\n# 2. View the exact same memory bytes as 16-bit integers\n# Because 16-bit is half the size of 64-bit, each number splits into four!\nmatrix_view = original.view(np.int16)\n\n# 3. Check the shapes\nprint(\"Original dtype:\", original.dtype)\nprint(\"View dtype:    \", matrix_view.dtype)\nprint(\"\\nOriginal Array:\\n\", original)\nprint(\"\\nMatrix View:\\n\", matrix_view)\nOriginal dtype: int64\nView dtype:     int16\n\nOriginal Array:\n [10 20 30 40 50 60]\n\nMatrix View:\n [10  0  0  0 20  0  0  0 30  0  0  0 40  0  0  0 50  0  0  0 60  0  0  0]\n```\n\n`where`\n\nconditions\n*Reference arrays*\n\n```\narr = np.arange(20)\narr_2d = np.arange(24).reshape(4, 6)\n\nprint(\"Original 1D:\", arr)\nprint(\"\\n2D array:\\n\", arr_2d)\nOriginal 1D: [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19]\n\n2D array:\n [[ 0  1  2  3  4  5]\n [ 6  7  8  9 10 11]\n [12 13 14 15 16 17]\n [18 19 20 21 22 23]]\n```\n\n*Basic indexing*\n\n```\nprint(\"\\nElement at index 5:\", arr[5])\nprint(\"Element at [0, 2]:\", arr_2d[0, 2])\nprint(\"First row:\", arr_2d[0])\nprint(\"Last column:\", arr_2d[:, -1])\nElement at index 5: 5\nElement at [0, 2]: 2\nFirst row: [0 1 2 3 4 5]\nLast column: [ 5 11 17 23]\n```\n\n*Boolean indexing*\n\n```\nmask = (arr > 10) & (arr < 15)\nprint(\"\\nArr > 10 and < 15:\", arr[mask])\nArr > 10 and < 15: [11 12 13 14]\n```\n\n*Fancy indexing through explicit index selection*\n\n```\nindices = [0, 5, 10, 15]\nprint(\"arr[[0, 5, 10, 15]]:\", arr[indices])\narr[[0, 5, 10, 15]]: [ 0  5 10 15]\n```\n\n*Slicing operations*\n\n```\nprint(\"\\narr[5:10]:\", arr[5:10])\nprint(\"arr[::2]:\", arr[::2])  # Every 2nd element\nprint(\"arr[::-1]:\", arr[::-1])  # Reversed\narr[5:10]: [5 6 7 8 9]\narr[::2]: [ 0  2  4  6  8 10 12 14 16 18]\narr[::-1]: [19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0]\n```\n\n*Two-dimensional slicing*\n\n```\nprint(\"\\narr_2d[1:3, 2:5]:\\n\", arr_2d[1:3, 2:5])\nprint(\"\\narr_2d[:, 1]:\", arr_2d[:, 1])  # All rows, column 1\narr_2d[1:3, 2:5]:\n [[ 8  9 10]\n [14 15 16]]\n\narr_2d[:, 1]: [ 1  7 13 19]\n```\n\n*Conditional selection with where*\n\n```\nresult = np.where(arr > 10, arr, 0)\nprint(\"\\nWhere arr > 10:\", result)\nWhere arr > 10: [ 0  0  0  0  0  0  0  0  0  0  0 11 12 13 14 15 16 17 18 19]\n```\n\nUnlike Python lists, NumPy applies operations across entire ndarrays.\n\n*Reference arrays*\n\n```\na = np.array([1, 2, 3, 4, 5])\nb = np.array([10, 20, 30, 40, 50])\n```\n\n*Basic arithmetic operations*\n\n```\nprint(\"a + b:\", a + b)\nprint(\"a - b:\", a - b)\nprint(\"a * b:\", a * b)\nprint(\"b / a:\", b / a)\nprint(\"a ** 2:\", a ** 2)\na + b: [11 22 33 44 55]\na - b: [ -9 -18 -27 -36 -45]\na * b: [ 10  40  90 160 250]\nb / a: [10. 10. 10. 10. 10.]\na ** 2: [ 1  4  9 16 25]\n```\n\n*Universal functions*\n\n```\nprint(\"\\nSquare root:\", np.sqrt(a))\nprint(\"Absolute value:\", np.abs(np.array([-1, -2, 3])))\nprint(\"Exponential:\", np.exp(np.array([1, 2, 3])))\nprint(\"Logarithm:\", np.log(np.array([1, 2.718, 10])))\nSquare root: [1.         1.41421356 1.73205081 2.         2.23606798]\nAbsolute value: [1 2 3]\nExponential: [ 2.71828183  7.3890561  20.08553692]\nLogarithm: [0.         0.99989632 2.30258509]\n```\n\n*Trigonometric functions*\n\n```\nangles = np.array([0, np.pi/4, np.pi/2, np.pi])\n\nprint(\"\\nSine:\", np.sin(angles))\nprint(\"Cosine:\", np.cos(angles))\nprint(\"Tangent:\", np.tan(angles))\nSine: [0.00000000e+00 7.07106781e-01 1.00000000e+00 1.22464680e-16]\nCosine: [ 1.00000000e+00  7.07106781e-01  6.12323400e-17 -1.00000000e+00]\nTangent: [ 0.00000000e+00  1.00000000e+00  1.63312394e+16 -1.22464680e-16]\n```\n\n*Rounding functions*\n\n```\ndecimals = np.array([1.234, 5.678, 2.567])\n\nprint(\"\\nCeiling:\", np.ceil(decimals))\nprint(\"Floor:\", np.floor(decimals))\nprint(\"Round:\", np.round(decimals, 2))\nCeiling: [2. 6. 3.]\nFloor: [1. 5. 2.]\nRound: [1.23 5.68 2.57]\n```\n\n*Reference arrays*\n\n```\narr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])\narr_2d = np.arange(1, 13).reshape(3, 4)\n```\n\n*Basic descriptive statistics*\n\n```\nprint(\"Sum:\", np.sum(arr))\nprint(\"Mean:\", np.mean(arr))\nprint(\"Median:\", np.median(arr))\nprint(\"Std Dev:\", np.std(arr))\nprint(\"Variance:\", np.var(arr))\nSum: 55\nMean: 5.5\nMedian: 5.5\nStd Dev: 2.8722813232690143\nVariance: 8.25\n```\n\n*Axis-wise statistics (2D example)*\n\n```\nprint(\"\\n2D array:\\n\", arr_2d)\nprint(\"\\nSum along axis 0 (columns):\", np.sum(arr_2d, axis=0))\nprint(\"Sum along axis 1 (rows):\", np.sum(arr_2d, axis=1))\nprint(\"Mean along axis 0:\", np.mean(arr_2d, axis=0))\nprint(\"Mean along axis 1:\", np.mean(arr_2d, axis=1))\n2D array:\n [[ 1  2  3  4]\n [ 5  6  7  8]\n [ 9 10 11 12]]\n\nSum along axis 0 (columns): [15 18 21 24]\nSum along axis 1 (rows): [10 26 42]\nMean along axis 0: [5. 6. 7. 8.]\nMean along axis 1: [ 2.5  6.5 10.5]\n```\n\n*Minimum and maximum functions*\n\n```\nprint(\"\\nMin:\", np.min(arr))\nprint(\"Max:\", np.max(arr))\nprint(\"Argmin (index):\", np.argmin(arr))\nprint(\"Argmax (index):\", np.argmax(arr))\nMin: 1\nMax: 10\nArgmin (index): 0\nArgmax (index): 9\n```\n\n*Percentiles*\n\n```\nprint(\"\\n25th percentile:\", np.percentile(arr, 25))\nprint(\"50th percentile (median):\", np.percentile(arr, 50))\nprint(\"75th percentile:\", np.percentile(arr, 75))\n25th percentile: 3.25\n50th percentile (median): 5.5\n75th percentile: 7.75\n```\n\n*Cumulative operations*\n\n```\nprint(\"\\nCumulative sum:\", np.cumsum(arr[:5]))\nprint(\"Cumulative product:\", np.cumprod(np.array([1, 2, 3, 4])))\nprint(\"Cumulative max:\", np.maximum.accumulate(np.array([1, 3, 2, 5, 4])))\nCumulative sum: [ 1  3  6 10 15]\nCumulative product: [ 1  2  6 24]\nCumulative max: [1 3 3 5 5]\n```\n\n*Reference arrays*\n\n```\na = np.array([1, 2, 3])\nb = np.array([4, 5, 6])\nc = np.array([[6, 7, 8, ], [9, 10, 11]])\n```\n\n*Concatenation*\n\n```\nconcat = np.concatenate([a, b])\nprint(\"Concatenate:\", concat)\nConcatenate: [1 2 3 4 5 6]\n```\n\n`stack`\n\n, `hstack`\n\n, and `vstack`\n\n```\nstacked = np.stack([a, b])\nprint(\"\\nStack:\\n\", stacked)\n\n# horizontal\nhstacked = np.hstack([a, b])\nprint(\"\\nHStack:\", hstacked)\n\n# vertical\nvstacked = np.vstack([[a], [b]])\nprint(\"\\nVStack:\\n\", vstacked)\nStack:\n [[1 2 3]\n [4 5 6]]\n\nHStack: [1 2 3 4 5 6]\n\nVStack:\n [[1 2 3]\n [4 5 6]]\n```\n\n*Splitting arrays*\n\n```\narr = np.arange(10)\nsplit_result = np.array_split(arr, 3)\nprint(\"\\nArray_split into 3 parts:\")\nfor i, part in enumerate(split_result):\n    print(f\"  Part {i}: {part}\")\nArray_split into 3 parts:\n  Part 0: [0 1 2 3]\n  Part 1: [4 5 6]\n  Part 2: [7 8 9]\n```\n\n*Transposition*\n\n```\nprint(\"\\nOriginal:\\n\", c)\nprint(\"Transposed:\\n\", c.T)\nOriginal:\n [[ 6  7  8]\n [ 9 10 11]]\nTransposed:\n [[ 6  9]\n [ 7 10]\n [ 8 11]]\n```\n\n*Unique values*\n\n```\narr_with_dupes = np.array([1, 2, 2, 3, 3, 3, 4])\nprint(\"\\nUnique values:\", np.unique(arr_with_dupes))\nUnique values: [1 2 3 4]\n```\n\n*Sorting*\n\n```\narr_unsorted = np.array([3, 1, 4, 1, 5, 9, 2, 6])\nprint(\"Sorted:\", np.sort(arr_unsorted))\nprint(\"Argsort (indices):\", np.argsort(arr_unsorted))\nSorted: [1 1 2 3 4 5 6 9]\nArgsort (indices): [1 3 6 0 2 4 7 5]\n```\n\n*Dot product and matrix multiplication*\n\n```\n# 1d ndarrays\na = np.array([1, 2, 3])\nb = np.array([4, 5, 6])\n\ndot_product = np.dot(a, b)\nprint(\"Dot product:\", dot_product)  # 1*4 + 2*5 + 3*6 = 32\n\n# 2d ndarrays\nmat_a = np.array([[1, 5], [3, 4]])\nmat_b = np.array([[5, 6], [7, 8]])\n\nmatrix_product = np.dot(mat_a, mat_b)\nprint(\"\\nMatrix product:\\n\", matrix_product)\n\n# Using the @ operator for matrix multiplication\nmatrix_product_operator = mat_a @ mat_b\nprint(\"\\nMatrix product using @ operator:\\n\", matrix_product_operator)\nDot product: 32\n\nMatrix product:\n [[40 46]\n [43 50]]\n\nMatrix product using @ operator:\n [[40 46]\n [43 50]]\n```\n\n*Trace (sum of diagonal elements)*\n\n```\nprint(\"Trace:\", np.trace(mat_a))\nTrace: 5\n```\n\n`linalg`\n\n: linear algebra submodule\n\n```\n# Determinant\ndet = np.linalg.det(mat_a)\nprint(\"\\nDeterminant:\", det)\n\n# Inverse\ninv = np.linalg.inv(mat_a)\nprint(\"\\nInverse:\\n\", inv)\n\n# Eigenvalues and eigenvectors\neigenvalues, eigenvectors = np.linalg.eig(mat_a)\nprint(\"\\nEigenvalues:\", eigenvalues)\nprint(\"Eigenvectors:\\n\", eigenvectors)\n\n# Rank\nprint(\"\\nRank:\", np.linalg.matrix_rank(mat_a))\n\n# Norm\nprint(\"\\nNorm (default):\", np.linalg.norm(a))\nprint(\"Norm (L2):\", np.linalg.norm(a, ord=2))\nprint(\"Norm (L1):\", np.linalg.norm(a, ord=1))\nDeterminant: -11.000000000000002\n\nInverse:\n [[-0.36363636  0.45454545]\n [ 0.27272727 -0.09090909]]\n\nEigenvalues: [-1.65331193+0.j  6.65331193+0.j]\nEigenvectors:\n [[-0.88333068+0.j -0.66249905+0.j]\n [ 0.46875037+0.j -0.74906275+0.j]]\n\nRank: 2\n\nNorm (default): 3.7416573867739413\nNorm (L2): 3.7416573867739413\nNorm (L1): 6.0\n```\n\nBroadcasting enables operations on ndarrays with different shapes, provided that their dimensions are compatible.\n\n*Array and scalar broadcasting*\n\n```\narr = np.array([1, 2, 3, 4, 5])\nresult = arr + 10\nprint(\"Array + scalar:\", result)\nArray + scalar: [11 12 13 14 15]\n```\n\n*One-dimensional and two-dimensional ndarray broadcasting*\n\n```\narr_1d = np.array([1, 2, 3])\narr_2d = np.array([[10], [20], [30]])\n\nresult = arr_1d + arr_2d\nprint(\"\\n1D + 2D (broadcasting):\")\nprint(\"Shape (3,) + (3, 1) = (3, 3)\")\nprint(result)\n1D + 2D (broadcasting):\nShape (3,) + (3, 1) = (3, 3)\n[[11 12 13]\n [21 22 23]\n [31 32 33]]\n```\n\n*Operations across dimensions*\n\n```\nmatrix = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\ncolumn = np.array([10, 20, 30])\n\nprint(\"\\nSubtract column from matrix:\")\nprint(matrix - column)\nSubtract column from matrix:\n[[ -9 -18 -27]\n [ -6 -15 -24]\n [ -3 -12 -21]]\n```\n\nBroadcasting rules:\n\n*Broadcasting rules: examples*\n\n``` php\nShape (5,) broadcasts with (3, 5) -> (3, 5)\nShape (3, 1) broadcasts with (3, 4) -> (3, 4)\nShape (1, 5) broadcasts with (3, 5) -> (3, 5)\n```\n\n*Set seed for reproducibility*\n\n```\nnp.random.seed(1000)  # For reproducibility\n```\n\n*Uniform distribution on [0, 1)*\n\n```\nuniform = np.random.rand(5)\nprint(\"Uniform [0, 1):\", uniform)\nUniform [0, 1): [0.65358959 0.11500694 0.95028286 0.4821914  0.87247454]\n```\n\n*Random integers*\n\n```\nints = np.random.randint(1, 10, size=5)\nprint(\"Random integers [1, 10):\", ints)\nRandom integers [1, 10): [9 5 5 5 3]\n```\n\n*Normal (Gaussian) distribution*\n\n```\nnormal = np.random.randn(5)\nprint(\"Normal distribution:\", normal)\nNormal distribution: [ 0.57363145 -0.74841131 -0.4122031  -0.07400906 -0.92893693]\n```\n\n*Normal distribution with custom mean and standard deviation*\n\n```\ncustom_normal = np.random.normal(loc=100, scale=15, size=5)\nprint(\"\\nNormal (μ=100, σ=15):\", custom_normal)\nNormal (μ=100, σ=15): [120.85092205 117.92603993 110.61013587 114.8944316  102.09195908]\n```\n\n*Exponential distribution*\n\n```\nexponential = np.random.exponential(scale=2.0, size=5)\nprint(\"Exponential (λ=0.5):\", exponential)\nExponential (λ=0.5): [4.69093541 0.02095277 0.1549649  0.56109307 0.28613573]\n```\n\n*Random choice from an ndarray*\n\n```\narr = np.arange(10)\nchoices = np.random.choice(arr, size=5, replace=False)\nprint(\"\\nRandom choice (no replace):\", choices)\nRandom choice (no replace): [4 2 8 0 3]\n```\n\n*In-place shuffling*\n\n```\narr = np.arange(10)\nnp.random.shuffle(arr)\nprint(\"Shuffled:\", arr)\nShuffled: [4 9 5 1 3 6 2 0 8 7]\n```\n\n*Shuffling with a copied permutation*\n\n```\narr = np.arange(10)\nshuffled = np.random.permutation(arr)\nprint(\"Permutation:\", shuffled)\nPermutation: [2 8 7 1 4 0 5 6 9 3]\n```\n\n*Binomial distribution*\n\n```\nbinomial = np.random.binomial(n=10, p=0.5, size=5)\nprint(\"\\nBinomial (n=10, p=0.5):\", binomial)\nBinomial (n=10, p=0.5): [6 4 5 5 5]\n```\n\n*Setup code*\n\n``` python\nimport os\nimport tempfile\nimport numpy as np\n\n# Create sample array\narr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n\n# Create temp directory for demo\ntemp_dir = tempfile.mkdtemp()\n\nprint(f\"original ndarray: {arr}\")\nprint(f\"Temporary directory created at: {temp_dir}\")\noriginal ndarray: [[1 2 3]\n [4 5 6]\n [7 8 9]]\nTemporary directory created at: /tmp/tmpr0nzny9i\n```\n\n*Save in .npy format (binary)*\n\n```\nnpy_path = os.path.join(temp_dir, 'array.npy')\nnp.save(npy_path, arr)\nprint(f\"Saved .npy file to {npy_path}\")\nSaved .npy file to /tmp/tmpr0nzny9i/array.npy\n```\n\n*Load .npy file*\n\n```\nloaded_arr = np.load(npy_path)\nprint(\"Loaded from .npy:\\n\", loaded_arr)\nLoaded from .npy:\n [[1 2 3]\n [4 5 6]\n [7 8 9]]\n```\n\n*Save multiple arrays as .npz (compressed)*\n\n```\nnpz_path = os.path.join(temp_dir, 'arrays.npz')\narr2 = np.array([10, 20, 30, 40])\nnp.savez(npz_path, array1=arr, array2=arr2)\nprint(f\"\\nSaved .npz file to {npz_path}\")\nSaved .npz file to /tmp/tmpr0nzny9i/arrays.npz\n```\n\n*Load .npz file*\n\n```\nloaded = np.load(npz_path)\nprint(\"Loaded from .npz:\")\nprint(\"  array1:\\n\", loaded['array1'])\nprint(\"  array2:\", loaded['array2'])\nLoaded from .npz:\n  array1:\n [[1 2 3]\n [4 5 6]\n [7 8 9]]\n  array2: [10 20 30 40]\n```\n\n*Save as a text file (CSV-like format)*\n\n```\ntxt_path = os.path.join(temp_dir, 'array.txt')\nnp.savetxt(txt_path, arr, delimiter=',', fmt='%d')\nprint(f\"\\nSaved text file to {txt_path}\")\nSaved text file to /tmp/tmpr0nzny9i/array.txt\n```\n\n*Load from a text file*\n\n```\nloaded_txt = np.loadtxt(txt_path, delimiter=',')\nprint(\"Loaded from text:\\n\", loaded_txt)\nLoaded from text:\n [[1. 2. 3.]\n [4. 5. 6.]\n [7. 8. 9.]]\n```\n\n*Apply a function to each element*\n\n```\narr = np.array([1, 2, 3, 4, 5])\nsquared = np.vectorize(lambda x: x**2)(arr)\nprint(\"Vectorized function (square):\", squared)\nVectorized function (square): [ 1  4  9 16 25]\n```\n\n*Piecewise operations*\n\n```\narr = np.array([1, 2, 3, 4, 5])\nresult = np.piecewise(arr, [arr < 3, arr >= 3], [lambda x: x**2, lambda x: x*10])\nprint(\"\\nPiecewise (x<3: x², x≥3: 10x):\", result)\nPiecewise (x<3: x², x≥3: 10x): [ 1  4 30 40 50]\n```\n\n*Apply operations along an axis*\n\n```\nmatrix = np.array([[1, 2, 3], [4, 5, 6]])\nsums0 = np.apply_along_axis(np.sum, axis=0, arr=matrix)\nsums1 = np.apply_along_axis(np.sum, axis=1, arr=matrix)\n\nprint(\"\\nApply sum along axis 0:\", sums0)\nprint(\"Apply sum along axis 1:\", sums1)\nApply sum along axis 0: [5 7 9]\nApply sum along axis 1: [ 6 15]\n```\n\n*Repeat and tile*\n\n```\narr = np.array([1, 2, 3])\nprint(\"\\nRepeat (each element 2 times):\", np.repeat(arr, 2))\nprint(\"Tile (whole array 2 times):\", np.tile(arr, 2))\nRepeat (each element 2 times): [1 1 2 2 3 3]\nTile (whole array 2 times): [1 2 3 1 2 3]\n```\n\n*Reduction operations*\n\n```\narr = np.array([1, 2, 3, 4, 5])\nresult = np.add.reduce(arr)  # Sum\nprint(\"\\nReduce with add (sum):\", result)\nReduce with add (sum): 15\n```\n\n`searchsorted`\n\n(binary search)\n\n```\nsorted_arr = np.array([1, 3, 5, 7, 9])\nindices = np.searchsorted(sorted_arr, [2, 4, 6, 8])\nprint(\"\\nSearchsorted indices:\", indices)\nSearchsorted indices: [1 2 3 4]\n```\n\n*Extract diagonal elements*\n\n```\nmatrix = np.arange(9).reshape(3, 3)\ndiagonal0 = np.diag(matrix, k=0)  # Main diagonal\ndiagonal1 = np.diag(matrix, k=1)  # Diagonal above main\nprint(\"\\nDiagonal of matrix:\\n\", matrix)\nprint(\"Diagonal elements:\", diagonal0)\nprint(\"Diagonal above main:\", diagonal1)\nDiagonal of matrix:\n [[0 1 2]\n [3 4 5]\n [6 7 8]]\nDiagonal elements: [0 4 8]\nDiagonal above main: [1 5]\n```\n\n*Create a diagonal matrix*\n\n```\ndiag_matrix = np.diag([1, 2, 3])\nprint(\"\\nDiagonal matrix from [1, 2, 3]:\\n\", diag_matrix)\nDiagonal matrix from [1, 2, 3]:\n [[1 0 0]\n [0 2 0]\n [0 0 3]]\n```\n\n*Count and display non-zero values*\n\n```\narr = np.array([0, 1, 0, 2, 3, 0])\nprint(\"\\nNonzero count:\", np.count_nonzero(arr))\nprint(\"Nonzero indices:\", np.nonzero(arr))\nNonzero count: 3\nNonzero indices: (array([1, 3, 4]),)\n```\n\n*Setup code*\n\n``` python\nimport time\n```\n\n*Avoid Python loops by using vectorisation*\n\n```\narr = np.arange(1_000_000)\n\n# Slow: Python loop\nstart = time.time()\nresult = np.array([x**2 for x in arr])\nloop_time = time.time() - start\nprint(f\"Python loop: {loop_time:.6f} seconds\")\n\n# Fast: NumPy vectorization\nstart = time.time()\nresult = arr ** 2\nvectorized_time = time.time() - start\nprint(f\"NumPy vectorized: {vectorized_time:.6f} seconds\")\nprint(f\"Speedup: {loop_time/vectorized_time:.1f}x faster\\n\")\n=== Performance: Vectorization ===\nPython loop: 0.238411 seconds\nNumPy vectorized: 0.001625 seconds\nSpeedup: 146.7x faster\n```\n\n*Use in-place operations where appropriate*\n\n```\narr = np.arange(5)\nprint(\"Original:\", arr)\narr += 10  # In-place (more memory efficient)\nprint(\"After += 10:\", arr)\nOriginal: [0 1 2 3 4]\nAfter += 10: [10 11 12 13 14]\n```\n\n*Data types and memory usage*\n\n```\narr_float64 = np.arange(1000, dtype=np.float64)\narr_float32 = np.arange(1000, dtype=np.float32)\narr_int32 = np.arange(1000, dtype=np.int32)\n\nprint(f\"Float64: {arr_float64.nbytes} bytes\")\nprint(f\"Float32: {arr_float32.nbytes} bytes\")\nprint(f\"Int32: {arr_int32.nbytes} bytes\")\nFloat64: 8000 bytes\nFloat32: 4000 bytes\nInt32: 4000 bytes\n```\n\n*Memory efficiency: views versus copies*\n\n```\noriginal = np.arange(10)\nview = original[:]  # This is a view, shares memory\ncopy = original[:].copy()  # This is a copy\n\nprint(f\"View shares memory: {view.base is original}\")\nprint(f\"Copy doesn't share memory: {copy.base is original}\")\nView shares memory: True\nCopy doesn't share memory: False\n```\n\n*Useful diagnostics for debugging*\n\n```\narr = np.random.randn(3, 4, 5)\n\nprint(f\"Shape: {arr.shape}\")\nprint(f\"Ndim: {arr.ndim}\")\nprint(f\"Dtype: {arr.dtype}\")\nprint(f\"Size: {arr.size}\")\nprint(f\"Memory: {arr.nbytes} bytes\")\nShape: (3, 4, 5)\nNdim: 3\nDtype: float64\nSize: 60\nMemory: 480 bytes\n```\n\n*Check for NaN and infinite values*\n\n```\narr = np.array([1, 2, np.nan, 4, np.inf, -np.inf])\n\nprint(f\"Array: {arr}\")\nprint(f\"Has NaN: {np.isnan(arr).any()}\")\nprint(f\"Has Inf: {np.isinf(arr).any()}\")\nprint(f\"Is finite: {np.isfinite(arr)}\")\nArray: [  1.   2.  nan   4.  inf -inf]\nHas NaN: True\nHas Inf: True\nIs finite: [ True  True False  True False False]\n```\n\n*Type casting*\n\n```\narr = np.array([1.5, 2.7, 3.2])\n\nprint(f\"Original (float): {arr}\")\nprint(f\"As int: {arr.astype(int)}\")\nprint(f\"As str: {arr.astype(str)}\")\nOriginal (float): [1.5 2.7 3.2]\nAs int: [1 2 3]\nAs str: ['1.5' '2.7' '3.2']\n```\n\nNumPy is a core component of scientific computing in Python. This tutorial has outlined how NumPy arrays differ from Python lists, how they can be created and inspected, and how indexing, arithmetic, and statistical operations can be performed efficiently.\n\nA key strength of NumPy lies in its speed and vectorised programming model. Rather than relying on explicit Python loops for every calculation, practitioners can apply concise operations to complete datasets. This capability makes NumPy an essential tool for data analysis, machine learning, and numerical modelling.\n\nTo develop proficiency, practise regularly with arrays of different shapes, slicing patterns, and reshaping strategies. Comparing NumPy workflows with equivalent pure-Python approaches is particularly useful for understanding performance and expressiveness benefits. These foundations also support more advanced work with libraries such as Pandas, SciPy, and TensorFlow.\n\nThis article has presented a concise, practice-oriented reference for fundamental NumPy workflows. For continued development, readers are encouraged to extend these examples to domain-specific datasets and to evaluate computational trade-offs in realistic analytical pipelines.\n\nDid this article help you? Let me know in the comments below, and don't forget to drop a like if you enjoyed the read! Thank you.", "url": "https://wpnews.pro/news/python-numpy-library", "canonical_source": "https://dev.to/michal_puzanov_1a085094b3/python-numpy-library-2heb", "published_at": "2026-08-04 09:45:14+00:00", "updated_at": "2026-08-04 10:13:14.360439+00:00", "lang": "en", "topics": ["developer-tools"], "entities": ["NumPy", "Pandas", "SciPy", "scikit-learn", "TensorFlow"], "alternates": {"html": "https://wpnews.pro/news/python-numpy-library", "markdown": "https://wpnews.pro/news/python-numpy-library.md", "text": "https://wpnews.pro/news/python-numpy-library.txt", "jsonld": "https://wpnews.pro/news/python-numpy-library.jsonld"}}