Intermediate

Advanced Operations on Arrays with NumPy

Array copy vs view, the matrix library and linear algebra for complex calculations with NumPy.

Level: Intermediate / Advanced
Versions: Python 3.12 · NumPy 1.26 · Google Colab


Table of Contents

  1. Course Overview
  2. Exploring Array Copy and View in NumPy
  3. Working with Matrices Using the NumPy Matrix Library
  4. Performing Complex Calculations Using NumPy Linear Algebra
  5. Conceptual Diagrams
  6. Summary and Resources

1. Course Overview

This course covers advanced array operations with NumPy, going beyond simple element manipulation. Three major topics are addressed:

ModuleTopicDuration
1Exploring Array Copy and View13m 23s
2Working with Matrices (matrix library)18m 45s
3Linear Algebra with numpy.linalg14m 43s

Running scenario: A fictional company called Globomantics wants to build financial portfolios, compute risk/return metrics, and apply optimization techniques — all in NumPy.


2. Exploring Array Copy and View in NumPy

2.1 Business Context and Prerequisites

In data science projects, memory management is critical. Understanding the difference between copy and view allows you to:

  • Preserve the integrity of original data
  • Optimize memory consumption
  • Avoid subtle bugs caused by unintended data mutation

Prerequisites: Python basics (lists, numpy.array), basic memory management concepts.

2.2 Concept: Copy vs View

Analogy: You are preparing a certification with a friend.

Option A — Array COPY (deep copy)
┌─────────────────────────────────────────────────────┐
│  You photocopy the relevant pages of the book       │
│  → Your friend receives an INDEPENDENT copy         │
│  → Their annotations do NOT affect your original    │
└─────────────────────────────────────────────────────┘

Option B — Array VIEW (shallow copy)
┌─────────────────────────────────────────────────────┐
│  You lend the book itself                           │
│  → You SHARE the same content                       │
│  → Their annotations appear in your book too        │
└─────────────────────────────────────────────────────┘

When to use COPY:

  • Preserving original data (guaranteed integrity)
  • Data augmentation in machine learning / computer vision
  • Parallel processing in distributed environments
  • Debugging and state snapshots

When to use VIEW:

  • Resource-sensitive applications (mobile devices, embedded systems)
  • Real-time streaming (always working on the most recent data)
  • Slicing large arrays without duplicating memory
Memory visualization:

COPY:
original_array  ──→ [Memory Block A: 1, 2, 3, 4, 5]
copied_array    ──→ [Memory Block B: 1, 2, 3, 4, 5]   ← independent copy

VIEW:
original_array  ──→ [Memory Block A: 1, 2, 3, 4, 5]
                              ▲
view_array      ─────────────┘                          ← same memory

2.3 Demo: Array Copy (deep copy)

import numpy as np

# Creating the original array
source_array = np.array([10, 20, 30, 40, 50])

# Deep copy — numpy.copy()
cloned_array = source_array.copy()

print("Original:", source_array)   # [10 20 30 40 50]
print("Clone:   ", cloned_array)   # [10 20 30 40 50]

# Modifying the original
source_array[0] = 99

print("After modification:")
print("Original:", source_array)   # [99 20 30 40 50]
print("Clone:   ", cloned_array)   # [10 20 30 40 50]  ← unchanged

# .base attribute — who owns the memory?
print("source_array.base:", source_array.base)  # None (owns its memory)
print("cloned_array.base:", cloned_array.base)  # None (owns its memory)

Key result: cloned_array.base returns None → the copy owns its own memory block.

2.4 Demo: Array View (shallow copy)

import numpy as np

source_array = np.array([10, 20, 30, 40, 50])

# Shallow copy — numpy.view()
view_array = source_array.view()

print("Original:", source_array)  # [10 20 30 40 50]
print("View:    ", view_array)    # [10 20 30 40 50]

# Modifying the original
source_array[0] = 99

print("After modification:")
print("Original:", source_array)  # [99 20 30 40 50]
print("View:    ", view_array)    # [99 20 30 40 50]  ← reflects the change!

# .base attribute
print("source_array.base:", source_array.base)  # None
print("view_array.base:  ", view_array.base)    # [99 20 30 40 50] → shared memory

Key result: view_array.base returns the original array → they share the same memory block.

2.5 Copy vs View Reference Table

Criterionarray.copy()array.view()
TypeDeep copyShallow copy
MemoryNew block allocatedSame memory block
Changes propagatedNoYes
array.baseNoneReference to original
PerformanceSlower (allocation)Faster
Typical useData preservationEfficient slicing
ML use caseData augmentationFeature slicing

3. Working with Matrices Using the NumPy Matrix Library

3.1 Data Science Use Cases

Matrices are fundamental in data science because they natively represent tabular data:

Dataset (features × samples):
         feature_1  feature_2  feature_3
sample_1 [  0.5        1.2        0.8  ]
sample_2 [  1.1        0.3        2.1  ]
sample_3 [  0.9        1.8        1.5  ]

Key applications:

  • Feature engineering: matrix multiplication, inversion, eigenvalues, eigenvectors
  • Dimensionality reduction: SVD (Singular Value Decomposition)
  • Deep learning: neuron weights and biases = matrices; forward/backward propagation = matrix multiplications
  • NLP: numerical text representations (TF-IDF matrices, word embeddings)

3.2 Matrix Creation Functions

numpy.matlib — function categories:

┌─────────────────────────────────────────────────────────┐
│  CREATION                                               │
│  ├── mat(data)       → matrix from array-like           │
│  └── asmatrix(data)  → convert to matrix                │
├─────────────────────────────────────────────────────────┤
│  INITIALIZATION                                         │
│  ├── zeros(shape)    → matrix of zeros                  │
│  ├── ones(shape)     → matrix of ones                   │
│  ├── eye(n)          → n×n identity matrix              │
│  └── empty(shape)    → uninitialized matrix (random)    │
└─────────────────────────────────────────────────────────┘

Important: empty() does not produce zeros — it returns random values (memory residuals). Always use zeros() when zero initialization is required.

3.3 Demo: Creating Matrices

import numpy.matlib
import numpy as np

# 3×3 zero matrix of type int
zeros_mat = numpy.matlib.zeros((3, 3), dtype=int)
print(zeros_mat)
# [[0 0 0]
#  [0 0 0]
#  [0 0 0]]

# 3×3 ones matrix of type int
ones_mat = numpy.matlib.ones((3, 3), dtype=int)
print(ones_mat)
# [[1 1 1]
#  [1 1 1]
#  [1 1 1]]

# 2×2 empty matrix of type string
empty_str = numpy.matlib.empty((2, 2), dtype=str)
print(empty_str)
# [[''] ['']]
# [[''] ['']]

# 2×2 empty matrix of type int — WARNING: unpredictable values!
empty_int = numpy.matlib.empty((2, 2), dtype=int)
print(empty_int)
# Unpredictable values (e.g.: [[140234 0] [0 140234]])

# 3×3 identity matrix
identity = numpy.matlib.eye(3, dtype=int)
print(identity)
# [[1 0 0]
#  [0 1 0]
#  [0 0 1]]

3.4 Demo: Matrix Manipulation (stack, split, insert, delete)

import numpy as np

# Two base matrices
even_matrix = np.matrix([[2, 4, 6],
                          [8, 10, 12]])   # shape: (2, 3)

odd_matrix  = np.matrix([[1, 3, 5],
                          [7, 9, 11]])   # shape: (2, 3)

# --- STACKING ---

# hstack: horizontal concatenation (axis 1)
h_stacked = np.hstack((even_matrix, odd_matrix))
print("hstack:", h_stacked.shape)  # (2, 6)
# [[ 2  4  6  1  3  5]
#  [ 8 10 12  7  9 11]]

# vstack: vertical concatenation (axis 0)
v_stacked = np.vstack((even_matrix, odd_matrix))
print("vstack:", v_stacked.shape)  # (4, 3)
# [[ 2  4  6]
#  [ 8 10 12]
#  [ 1  3  5]
#  [ 7  9 11]]

# --- SPLITTING ---

# hsplit: horizontal split (back to original matrices)
h_split = np.hsplit(h_stacked, 2)
print("hsplit[0]:", h_split[0])  # even_matrix
print("hsplit[1]:", h_split[1])  # odd_matrix

# vsplit: vertical split
v_split = np.vsplit(v_stacked, 2)
print("vsplit[0]:", v_split[0])  # even_matrix
print("vsplit[1]:", v_split[1])  # odd_matrix
hstack vs vstack visualization:

even_matrix         odd_matrix
[2  4  6]           [1  3  5]
[8  10 12]          [7  9  11]

hstack → [2  4  6  | 1  3  5 ]    ← axis 1 (columns)
          [8 10 12  | 7  9 11 ]

vstack → [2   4   6 ]
          [8  10  12 ]             ← axis 0 (rows)
          [1   3   5 ]
          [7   9  11 ]

3.5 Demo: Arithmetic Operations on Matrices

import numpy as np

A = np.matrix([[2, 3, 4],
               [5, 6, 7]])

B = np.matrix([[8, 9, 10],
               [11, 12, 13]])

# Element-wise addition
print(np.add(A, B))
# [[10 12 14]
#  [16 18 20]]

# Element-wise subtraction
print(np.subtract(A, B))
# [[-6 -6 -6]
#  [-6 -6 -6]]

# Element-wise multiplication (Hadamard product)
print(np.multiply(A, B))
# [[16 27 40]
#  [55 72 91]]

# Matrix multiplication (dot product)
# Requires compatible dimensions: (m×n) · (n×p) = (m×p)
A_sq = np.matrix([[1, 2],
                  [3, 4]])  # (2×2)
B_sq = np.matrix([[5, 6],
                  [7, 8]])  # (2×2)

print(np.dot(A_sq, B_sq))
# [[19 22]
#  [43 50]]

# Manual verification:
# [0,0] = 1*5 + 2*7 = 5 + 14 = 19  ✓
# [0,1] = 1*6 + 2*8 = 6 + 16 = 22  ✓
Dot product — visualization:

    A            B           A·B
[1  2]   ×   [5  6]   =   [19  22]
[3  4]       [7  8]       [43  50]

Calculation [0,0]: row 0 of A · column 0 of B
               [1, 2] · [5, 7] = 1×5 + 2×7 = 19

3.6 Matrix Operations Reference Table

OperationNumPy FunctionDescription
Additionnp.add(A, B)Element-wise, identical shapes required
Subtractionnp.subtract(A, B)Element-wise
Element-wise multiplynp.multiply(A, B)Hadamard product
Dot productnp.dot(A, B)Classic matrix multiplication
Horizontal stacknp.hstack((A, B))Concatenate on axis 1
Vertical stacknp.vstack((A, B))Concatenate on axis 0
Horizontal splitnp.hsplit(M, n)Split into n equal parts on axis 1
Vertical splitnp.vsplit(M, n)Split into n equal parts on axis 0
Zerosnp.matlib.zeros((r,c))Initialize with zeros
Onesnp.matlib.ones((r,c))Initialize with ones
Identitynp.matlib.eye(n)Diagonal = 1, rest = 0
Emptynp.matlib.empty((r,c))Uninitialized — random values

4. Performing Complex Calculations Using NumPy Linear Algebra

4.1 Finance Use Cases

numpy.linalg is at the core of modern financial applications:

DomainApplicationlinalg Functions
Portfolio optimizationMean-variance (Markowitz), CAPMsolve(), inv(), eig()
Risk managementVaR, CVaRMatrix operations
Monte CarloScenario simulationcholesky(), svd()
Option pricingBlack-Scholes, stochastic modelssolve(), norm()
Credit scoringLogistic regression, PCAeig(), svd()
Algo tradingTime series analysislstsq()

4.2 Categories of linalg Functions

numpy.linalg — organization:

┌────────────────────────────────────────────────────────┐
│  1. MATRIX & VECTOR PRODUCTS                           │
│     dot(), multi_dot(), vdot(), inner(), outer()       │
├────────────────────────────────────────────────────────┤
│  2. DECOMPOSITIONS                                     │
│     svd()         → Singular Value Decomposition       │
│     cholesky()    → Cholesky decomposition             │
│     qr()          → QR decomposition                   │
├────────────────────────────────────────────────────────┤
│  3. EIGENVALUES                                        │
│     eig()         → eigenvalues + eigenvectors         │
│     eigvals()     → eigenvalues only                   │
├────────────────────────────────────────────────────────┤
│  4. NORMS & METRICS                                    │
│     matrix_rank() → matrix rank                        │
│     trace()       → diagonal sum                       │
│     det()         → determinant                        │
│     norm()        → vector/matrix norm                 │
├────────────────────────────────────────────────────────┤
│  5. SOLVING EQUATIONS                                  │
│     solve()       → solves Ax = b                      │
│     lstsq()       → least squares                      │
│     inv()         → matrix inverse                     │
└────────────────────────────────────────────────────────┘

4.3 Demo: Rank, Trace, Determinant and Eigenvalues

import numpy as np

# 3×3 demo matrix
# Row 3 = sum of first two rows → rank = 2
M = np.array([[1,  2,  3],
              [4,  5,  6],
              [5,  7,  9]])  # row 3 = row 1 + row 2

# --- RANK ---
rank = np.linalg.matrix_rank(M)
print("Rank:", rank)  # 2

# Visual explanation:
# Row 1: [1, 2, 3]
# Row 2: [4, 5, 6]
# Row 3: [5, 7, 9] = [1+4, 2+5, 3+6] → linearly dependent
# → only 2 linearly independent rows → rank = 2

# --- TRACE ---
# Matrix with identifiable diagonal
M2 = np.array([[1,  2,  3],
               [4,  5,  6],
               [7,  8, 10]])
# Diagonal: 1 + 5 + 10 = 16
trace_val = np.trace(M2)
print("Trace:", trace_val)  # 16

# --- DETERMINANT ---
det_val = np.linalg.det(M2)
print("Determinant:", round(det_val, 4))  # -3.0 (approx.)

# --- EIGENVALUES & EIGENVECTORS ---
eigenvalues, eigenvectors = np.linalg.eig(M2)
print("Eigenvalues:", eigenvalues)
print("Eigenvectors:\n", eigenvectors)
Visualization — Diagonal and Trace:

    M2 = [[ 1   2   3 ]
          [ 4  [5]  6 ]   ← diagonal
          [ 7   8  [10]]

    Trace = 1 + 5 + 10 = 16

Eigenvalues:
┌──────────────────────────────────────────────────┐
│  For each eigenvalue λ:  M·v = λ·v               │
│  v = corresponding eigenvector                   │
│                                                  │
│  Utility in Data Science:                        │
│  • PCA: eigenvalues → explained variance         │
│  • Stability of dynamic systems                  │
│  • Spectral analysis of graphs                   │
└──────────────────────────────────────────────────┘

4.4 Demo: Solving Linear Equations

Problem: Solve Y = m·X to find m, where X and Y are known matrices.

import numpy as np

# Known data
matrix_X = np.array([[3, 1],
                     [2, 4]])   # coefficients

matrix_Y = np.array([7, 12])   # results

# ─── APPROACH 1: Manual (2 steps) ───────────────────────────────────────────

# Step 1: Compute the inverse of X
X_inverse = np.linalg.inv(matrix_X)
print("Inverse of X:\n", X_inverse)

# Step 2: Dot product of X⁻¹ and Y → solves for m
solution_manual = np.dot(X_inverse, matrix_Y)
print("Solution (manual):", solution_manual)

# ─── APPROACH 2: numpy.linalg.solve() — recommended ─────────────────────────
solution_solve = np.linalg.solve(matrix_X, matrix_Y)
print("Solution (solve):", solution_solve)

# Both approaches produce the same result
# Verification: X · solution ≈ Y
print("Verification X·m:", np.dot(matrix_X, solution_solve))  # should ≈ Y
Mathematical resolution — visualization:

    Y = m · X
    
    [ 7]   =   m   ·   [3  1]
    [12]               [2  4]
    
    → m = X⁻¹ · Y
    
    X⁻¹ = 1/det(X) · adj(X)
    det(X) = 3×4 - 1×2 = 10
    
    X⁻¹ = (1/10) · [[ 4  -1]
                     [-2   3]]
    
    m = X⁻¹ · Y = ...

4.5 linalg Reference Table

FunctionSyntaxReturnsUsage
matrix_ranknp.linalg.matrix_rank(M)IntegerNumber of linearly independent rows/columns
tracenp.trace(M)ScalarDiagonal sum
detnp.linalg.det(M)ScalarDeterminant (0 = singular matrix)
invnp.linalg.inv(M)MatrixInverse of M (requires det ≠ 0)
eignp.linalg.eig(M)(values, vectors)Eigenvalues and eigenvectors
eigvalsnp.linalg.eigvals(M)ArrayEigenvalues only
solvenp.linalg.solve(A, b)ArraySolves Ax = b (recommended)
lstsqnp.linalg.lstsq(A, b)TupleLeast squares (overdetermined system)
svdnp.linalg.svd(M)(U, s, Vh)Singular Value Decomposition
choleskynp.linalg.cholesky(M)MatrixCholesky decomposition
normnp.linalg.norm(M)ScalarNorm (Frobenius by default)
dotnp.dot(A, B)Array/MatrixMatrix product

5. Conceptual Diagrams

5.1 Copy vs View — Memory Flow

flowchart TD
    A[source_array\nMemory Block A] -->|array.copy| B[cloned_array\nMemory Block B\nnew allocation]
    A -->|array.view| C[view_array\nPoints to Memory A]
    
    A2[Modify source_array] --> D{Type?}
    D -->|copy| E["cloned_array\nNOT modified\n.base → None"]
    D -->|view| F["view_array\nMODIFIED\n.base → source_array"]
    
    style B fill:#4CAF50,color:#fff
    style C fill:#FF9800,color:#fff
    style E fill:#4CAF50,color:#fff
    style F fill:#FF9800,color:#fff

5.2 Matrix Stacking Operations

flowchart LR
    A["Matrix A\n2×3"] --> H["hstack\nnp.hstack(A,B)"]
    B["Matrix B\n2×3"] --> H
    H --> HR["Result\n2×6\naxis=1"]

    A2["Matrix A\n2×3"] --> V["vstack\nnp.vstack(A,B)"]
    B2["Matrix B\n2×3"] --> V
    V --> VR["Result\n4×3\naxis=0"]

    style HR fill:#2196F3,color:#fff
    style VR fill:#9C27B0,color:#fff

5.3 Linear Algebra Pipeline — System Solving

flowchart TD
    A["System of equations\nAx = b"] --> B{Method?}
    B -->|"2 steps"| C["1. X_inv = np.linalg.inv(A)\n2. x = np.dot(X_inv, b)"]
    B -->|"1 step (recommended)"| D["x = np.linalg.solve(A, b)"]
    C --> E["Solution x"]
    D --> E
    E --> F["Verification\nnp.dot(A, x) ≈ b"]

    style D fill:#4CAF50,color:#fff
    style E fill:#2196F3,color:#fff

5.4 numpy.linalg Operations Hierarchy

mindmap
  root((numpy.linalg))
    Products
      dot
      multi_dot
      inner
      outer
    Decompositions
      svd
      cholesky
      qr
    Eigenvalues
      eig
      eigvals
    Metrics
      matrix_rank
      trace
      det
      norm
    Equations
      solve
      lstsq
      inv

6. Summary and Resources

What You Learned

ModuleKey ConceptsFunctions Used
Copy & ViewDeep copy vs shallow copy, base attribute, memory managementarray.copy(), array.view(), .base
Matrix LibraryCreation, initialization, stack, split, arithmetic operationszeros(), ones(), eye(), hstack(), vstack(), hsplit(), vsplit(), add(), subtract(), multiply(), dot()
Linear AlgebraRank, trace, determinant, eigenvalues, equation solvingmatrix_rank(), trace(), det(), eig(), inv(), solve()

Key Takeaways

  1. array.copy() → deep copy, new memory block, .base = None
  2. array.view() → shallow copy, shared memory, .base = original array
  3. np.matlib.empty() does not produce zeros — use zeros() when needed
  4. Dot product ≠ element-wise multiply: np.dot(A, B) vs np.multiply(A, B)
  5. np.linalg.solve(A, b) is preferred over inv(A) · b (numerically more stable)
  6. Rank of a matrix = number of linearly independent rows/columns
  7. Eigenvalues are fundamental in PCA and dynamic systems analysis

Resources

ResourceLink
Official NumPy documentationnumpy.org/doc
NumPy tutorialsnumpy.org/learn
Practice environmentGoogle Colab

Search Terms

operations · arrays · numpy · python · foundations · data · analysis · engineering · analytics · copy · matrix · view · array · linear · matrices · reference · algebra · cases · functions · linalg · resources · solving

Interested in this course?

Contact us to book it or get a custom training plan for your team.