Think of NumPy arrays as supercharged lists that can do math at lightning speed. While Python lists are great for general tasks, NumPy arrays are built specifically for numbers and calculations. They're the backbone of data science, machine learning, and scientific computing in Python!
Why NumPy? The Problem with Regular Lists
Let's say you have a list of temperatures in Celsius and want to convert them all to Fahrenheit.
# Using regular Python lists
celsius = [0, 10, 20, 30, 40]
# You have to use a loop
fahrenheit = []
for temp in celsius:
fahrenheit.append((temp * 9/5) + 32)
print(fahrenheit) # [32.0, 50.0, 68.0, 86.0, 104.0]
This works, but it's slow and requires writing loops for every calculation. Now imagine you have 1 million temperatures to convert!
With NumPy, the same task becomes super simple:
import numpy as np
# Using NumPy arrays
celsius = np.array([0, 10, 20, 30, 40])
# Just do the math directly - no loops!
fahrenheit = (celsius * 9/5) + 32
print(fahrenheit) # [32. 50. 68. 86. 104.]
Same result, cleaner code, and up to 50x faster! That's the power of NumPy. ⚡
Python lists: General-purpose containers (can hold anything: numbers, strings, objects). Slow for math because Python checks the type of each element during operations.
NumPy arrays: Specialized for numbers. All elements must be the same type (integers, floats, etc.). Super fast because NumPy knows the type upfront and uses optimized C code under the hood.
What is a NumPy Array?
A NumPy array is a grid of values, all of the same type. You can think of it in dimensions:
- 1D Array: Like a single row of numbers [1, 2, 3, 4]
- 2D Array: Like a table with rows and columns (a matrix)
- 3D Array: Like a stack of tables (think video frames or 3D images)
- N-D Array: NumPy can handle any number of dimensions!
Let's visualize this:
1D Array (Vector):
[10, 20, 30, 40]
2D Array (Matrix):
[[10, 20, 30],
[40, 50, 60],
[70, 80, 90]]
3D Array (Tensor):
[[[1, 2],
[3, 4]],
[[5, 6],
[7, 8]]]
Installing and Importing NumPy
First, you need to install NumPy (if you haven't already):
# In your terminal or command prompt
pip install numpy
Then import it in your Python code:
import numpy as np
# We use "np" as a shortcut - it's the standard convention
# Everyone uses "np", so stick with it!
Always import NumPy as "np" - this is the universal convention. When you see code online or in tutorials, everyone uses np.array(), np.zeros(), etc. It makes your code instantly recognizable to other programmers!
Creating NumPy Arrays - Multiple Ways
There are many ways to create arrays in NumPy. Let's explore the most common methods step by step.
Method 1: From Python Lists (Most Common)
Convert a regular Python list into a NumPy array:
import numpy as np
# 1D array from a list
arr1d = np.array([1, 2, 3, 4, 5])
print(arr1d)
# Output: [1 2 3 4 5]
# 2D array from nested lists
arr2d = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
print(arr2d)
# Output:
# [[1 2 3]
# [4 5 6]
# [7 8 9]]
Notice how NumPy displays arrays without commas between elements? That's one way to know you're looking at a NumPy array, not a list!
Method 2: Using arange() - Like Python's range()
Create arrays with sequential numbers:
import numpy as np
# Array from 0 to 9
arr = np.arange(10)
print(arr)
# Output: [0 1 2 3 4 5 6 7 8 9]
# Array from 5 to 15 (15 is excluded!)
arr = np.arange(5, 15)
print(arr)
# Output: [5 6 7 8 9 10 11 12 13 14]
# Array from 0 to 20 with step of 3
arr = np.arange(0, 20, 3)
print(arr)
# Output: [0 3 6 9 12 15 18]
# You can use floats too!
arr = np.arange(0, 1, 0.2)
print(arr)
# Output: [0. 0.2 0.4 0.6 0.8]
The end value in arange() is exclusive (not included). So np.arange(1, 5) gives you [1, 2, 3, 4], not [1, 2, 3, 4, 5]. This trips up beginners all the time!
Method 3: Using linspace() - Evenly Spaced Numbers
When you want a specific number of values evenly spaced between two points:
import numpy as np
# Get exactly 5 numbers between 0 and 10
arr = np.linspace(0, 10, 5)
print(arr)
# Output: [0. 2.5 5. 7.5 10.]
# Get 11 numbers between 0 and 1
arr = np.linspace(0, 1, 11)
print(arr)
# Output: [0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. ]
Key difference from arange(): With linspace(), the end value is included and you specify how many values you want, not the step size.
Method 4: Arrays Filled with Zeros or Ones
Create arrays pre-filled with specific values:
import numpy as np
# Array of zeros
zeros = np.zeros(5)
print(zeros)
# Output: [0. 0. 0. 0. 0.]
# 2D array of zeros (3 rows, 4 columns)
zeros_2d = np.zeros((3, 4))
print(zeros_2d)
# Output:
# [[0. 0. 0. 0.]
# [0. 0. 0. 0.]
# [0. 0. 0. 0.]]
# Array of ones
ones = np.ones(5)
print(ones)
# Output: [1. 1. 1. 1. 1.]
# 2D array of ones
ones_2d = np.ones((2, 3))
print(ones_2d)
# Output:
# [[1. 1. 1.]
# [1. 1. 1.]]
# Array filled with a specific value
sevens = np.full(5, 7)
print(sevens)
# Output: [7 7 7 7 7]
# 2D array filled with 99
nines = np.full((2, 3), 99)
print(nines)
# Output:
# [[99 99 99]
# [99 99 99]]
zeros(): Initialize arrays for calculations, create masks, or reset data.
ones(): Create multiplier arrays, normalize data, or initialize weights in machine learning.
full(): Set default values, create constant arrays for broadcasting.
Method 5: Identity Matrix
Create a special matrix with 1s on the diagonal and 0s everywhere else:
import numpy as np
# 3x3 identity matrix
identity = np.eye(3)
print(identity)
# Output:
# [[1. 0. 0.]
# [0. 1. 0.]
# [0. 0. 1.]]
Identity matrices are crucial in linear algebra and machine learning!
Method 6: Random Arrays
Generate arrays with random values:
import numpy as np
# Random values between 0 and 1
random_arr = np.random.rand(5)
print(random_arr)
# Output: [0.37454012 0.95071431 0.73199394 0.59865848 0.15601864]
# (Your values will be different!)
# 2D array of random values
random_2d = np.random.rand(3, 3)
print(random_2d)
# Random integers between 1 and 100
random_ints = np.random.randint(1, 100, size=10)
print(random_ints)
# Output: [44 47 64 67 67 9 83 21 36 87]
# (Your values will be different!)
# Random integers in a 2D array (3 rows, 4 columns)
random_ints_2d = np.random.randint(1, 50, size=(3, 4))
print(random_ints_2d)
Array Attributes - Understanding Your Arrays
Every NumPy array has properties that tell you about its structure:
import numpy as np
arr = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
# Shape: dimensions of the array (rows, columns)
print(arr.shape)
# Output: (3, 4) - 3 rows, 4 columns
# Number of dimensions
print(arr.ndim)
# Output: 2 - it's a 2D array
# Total number of elements
print(arr.size)
# Output: 12 - total of 12 numbers
# Data type of elements
print(arr.dtype)
# Output: int64 (or int32 on some systems)
# Size of each element in bytes
print(arr.itemsize)
# Output: 8 - each integer takes 8 bytes
Always check arr.shape when debugging! It's the fastest way to understand what your array looks like. Many bugs come from arrays having unexpected shapes.
Indexing and Slicing - Accessing Elements
Just like Python lists, you can access individual elements or slices of arrays.
1D Array Indexing
import numpy as np
arr = np.array([10, 20, 30, 40, 50])
# Get first element (index 0)
print(arr[0]) # Output: 10
# Get last element
print(arr[-1]) # Output: 50
# Get middle element
print(arr[2]) # Output: 30
1D Array Slicing
import numpy as np
arr = np.array([10, 20, 30, 40, 50, 60, 70])
# Get first 3 elements
print(arr[0:3])
# Output: [10 20 30]
# Get elements from index 2 to end
print(arr[2:])
# Output: [30 40 50 60 70]
# Get elements from start to index 4 (exclusive)
print(arr[:4])
# Output: [10 20 30 40]
# Get every second element
print(arr[::2])
# Output: [10 30 50 70]
# Reverse the array
print(arr[::-1])
# Output: [70 60 50 40 30 20 10]
2D Array Indexing
With 2D arrays, you need two indices: [row, column]
import numpy as np
arr = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
# Get element at row 0, column 2
print(arr[0, 2]) # Output: 3
# Get element at row 2, column 3
print(arr[2, 3]) # Output: 12
# Get first row
print(arr[0, :])
# Output: [1 2 3 4]
# Get first column
print(arr[:, 0])
# Output: [1 5 9]
# Get second row, elements from column 1 to 3
print(arr[1, 1:3])
# Output: [6 7]
In 2D arrays: arr[row, column] Think "row first, column second" - just like reading coordinates on a map!
Array Operations - The Real Power of NumPy
Here's where NumPy truly shines. You can perform mathematical operations on entire arrays without loops!
Element-wise Arithmetic
import numpy as np
a = np.array([1, 2, 3, 4])
b = np.array([10, 20, 30, 40])
# Addition
print(a + b)
# Output: [11 22 33 44]
# Subtraction
print(b - a)
# Output: [9 18 27 36]
# Multiplication
print(a * b)
# Output: [10 40 90 160]
# Division
print(b / a)
# Output: [10. 10. 10. 10.]
# Exponentiation
print(a ** 2)
# Output: [1 4 9 16]
All operations happen element-by-element automatically! No loops needed. ⚡
Scalar Operations
Perform operations with a single number on the entire array:
import numpy as np
arr = np.array([1, 2, 3, 4, 5])
# Add 10 to every element
print(arr + 10)
# Output: [11 12 13 14 15]
# Multiply every element by 2
print(arr * 2)
# Output: [2 4 6 8 10]
# Divide every element by 2
print(arr / 2)
# Output: [0.5 1. 1.5 2. 2.5]
# Square every element
print(arr ** 2)
# Output: [1 4 9 16 25]
Mathematical Functions
NumPy has built-in functions for common operations:
import numpy as np
arr = np.array([1, 4, 9, 16, 25])
# Square root of each element
print(np.sqrt(arr))
# Output: [1. 2. 3. 4. 5.]
# Exponential (e^x) of each element
print(np.exp([1, 2, 3]))
# Output: [2.71828183 7.3890561 20.08553692]
# Logarithm
print(np.log([1, 10, 100, 1000]))
# Output: [0. 2.30258509 4.60517019 6.90775528]
# Absolute value
arr2 = np.array([-1, -2, 3, -4, 5])
print(np.abs(arr2))
# Output: [1 2 3 4 5]
# Trigonometric functions
angles = np.array([0, 30, 45, 60, 90])
radians = np.radians(angles) # Convert degrees to radians
print(np.sin(radians))
Statistical Operations - Analyzing Data
NumPy makes statistical calculations incredibly easy:
import numpy as np
scores = np.array([85, 92, 78, 90, 88, 76, 95, 89])
# Sum of all elements
print(np.sum(scores))
# Output: 693
# Mean (average)
print(np.mean(scores))
# Output: 86.625
# Median (middle value)
print(np.median(scores))
# Output: 88.5
# Standard deviation (how spread out the data is)
print(np.std(scores))
# Output: 6.28...
# Minimum value
print(np.min(scores))
# Output: 76
# Maximum value
print(np.max(scores))
# Output: 95
# Index of minimum value
print(np.argmin(scores))
# Output: 5 (76 is at index 5)
# Index of maximum value
print(np.argmax(scores))
# Output: 6 (95 is at index 6)
Real-World Example: Student Grades Analysis
Let's put everything together in a practical example. We'll analyze student test scores across multiple subjects.
Step 1: Create the Data
import numpy as np
# 5 students, 4 subjects (Math, Science, English, History)
# Each row is a student, each column is a subject
grades = np.array([
[85, 90, 78, 92], # Student 1
[88, 76, 92, 85], # Student 2
[92, 88, 85, 90], # Student 3
[78, 82, 88, 75], # Student 4
[95, 91, 89, 94] # Student 5
])
print("Student Grades:")
print(grades)
print(f"Shape: {grades.shape}") # (5, 4) - 5 students, 4 subjects
Step 2: Calculate Average per Student
# Calculate average across columns (axis=1)
# This gives us each student's average across all subjects
student_averages = np.mean(grades, axis=1)
print("\nEach student's average:")
print(student_averages)
# Output: [86.25 85.25 88.75 80.75 92.25]
axis=0: Go down the rows (operate on columns). Results in one value per column.
axis=1: Go across the columns (operate on rows). Results in one value per row.
Think of it like this: axis tells you which dimension to "collapse."
Step 3: Calculate Average per Subject
# Calculate average across rows (axis=0)
# This gives us the class average for each subject
subject_averages = np.mean(grades, axis=0)
subjects = ['Math', 'Science', 'English', 'History']
print("\nClass average per subject:")
for subject, avg in zip(subjects, subject_averages):
print(f"{subject}: {avg:.1f}")
# Output:
# Math: 87.6
# Science: 85.4
# English: 86.4
# History: 87.2
Step 4: Find Top Student
# Find the student with highest average
best_student_index = np.argmax(student_averages)
best_average = student_averages[best_student_index]
print(f"\nTop student: Student {best_student_index + 1}")
print(f"Average score: {best_average:.2f}")
# Output:
# Top student: Student 5
# Average score: 92.25
Step 5: Grade Distribution
# Count how many grades are above 85
above_85 = np.sum(grades >= 85)
total_grades = grades.size
print(f"\nGrades above 85: {above_85} out of {total_grades}")
print(f"Percentage: {(above_85/total_grades)*100:.1f}%")
# Find all grades above 90
excellent_grades = grades[grades > 90]
print(f"\nExcellent grades (>90): {excellent_grades}")
# Output: [92 92 92 95 91 94]
Step 6: Add Bonus Points
# Give everyone 5 bonus points
grades_with_bonus = grades + 5
print("\nGrades after 5-point bonus:")
print(grades_with_bonus)
# Cap all grades at 100
grades_capped = np.minimum(grades_with_bonus, 100)
print("\nGrades capped at 100:")
print(grades_capped)
Reshaping Arrays - Changing Dimensions
Sometimes you need to reorganize your data into different shapes.
import numpy as np
# 1D array with 12 elements
arr = np.arange(12)
print("Original:", arr)
# Output: [0 1 2 3 4 5 6 7 8 9 10 11]
# Reshape to 3 rows, 4 columns
arr_2d = arr.reshape(3, 4)
print("\n3x4 array:")
print(arr_2d)
# Output:
# [[0 1 2 3]
# [4 5 6 7]
# [8 9 10 11]]
# Reshape to 4 rows, 3 columns
arr_2d_alt = arr.reshape(4, 3)
print("\n4x3 array:")
print(arr_2d_alt)
# Output:
# [[0 1 2]
# [3 4 5]
# [6 7 8]
# [9 10 11]]
# Let NumPy figure out one dimension (-1)
arr_auto = arr.reshape(2, -1) # 2 rows, figure out columns
print("\n2 rows, auto columns:")
print(arr_auto)
# Output:
# [[0 1 2 3 4 5]
# [6 7 8 9 10 11]]
The total number of elements must stay the same when reshaping. You can't reshape an array with 12 elements into a 3x5 array (that needs 15 elements). NumPy will give you an error: "cannot reshape array of size 12 into shape (3,5)".
Flattening Arrays
Convert any multi-dimensional array back to 1D:
import numpy as np
arr_2d = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
# Flatten to 1D
arr_flat = arr_2d.flatten()
print(arr_flat)
# Output: [1 2 3 4 5 6 7 8 9]
# Alternative: ravel() (faster but returns a view)
arr_ravel = arr_2d.ravel()
print(arr_ravel)
# Output: [1 2 3 4 5 6 7 8 9]
Boolean Indexing - Filtering with Conditions
One of NumPy's most powerful features: filter arrays using conditions.
import numpy as np
temperatures = np.array([15, 22, 18, 30, 25, 12, 28, 20])
# Create a boolean mask (True/False for each element)
hot_days = temperatures > 25
print("Hot days mask:", hot_days)
# Output: [False False False True False False True False]
# Use the mask to filter the array
print("Temperatures on hot days:", temperatures[hot_days])
# Output: [30 28]
# All in one line
cold_temps = temperatures[temperatures < 18]
print("Cold temperatures:", cold_temps)
# Output: [15 12]
# Multiple conditions with & (and) or | (or)
comfortable = temperatures[(temperatures >= 20) & (temperatures <= 25)]
print("Comfortable temperatures:", comfortable)
# Output: [22 25 20]
Use & for AND and | for OR (not "and" or "or").
Always put each condition in parentheses: (condition1) & (condition2)
Broadcasting - Arrays of Different Sizes
Broadcasting is NumPy's secret superpower. It lets you perform operations on arrays of different shapes without copying data.
Example 1: Scalar Broadcasting
import numpy as np
arr = np.array([1, 2, 3, 4, 5])
# Adding a scalar to an array
result = arr + 10
print(result)
# Output: [11 12 13 14 15]
# What actually happens:
# NumPy "broadcasts" 10 to match the array: [10, 10, 10, 10, 10]
# Then adds element-wise: [1+10, 2+10, 3+10, 4+10, 5+10]
Example 2: Adding Different Shaped Arrays
import numpy as np
# 2D array: 3 rows, 4 columns
matrix = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
# 1D array: 4 elements
row = np.array([10, 20, 30, 40])
# Add row to each row of matrix
result = matrix + row
print(result)
# Output:
# [[11 22 33 44]
# [15 26 37 48]
# [19 30 41 52]]
# NumPy broadcasts 'row' across all 3 rows automatically!
Example 3: Temperature Conversion Table
import numpy as np
# Celsius temperatures (column)
celsius = np.array([[0], [10], [20], [30], [40]])
# Conversion formulas (row)
# [Fahrenheit, Kelvin]
# F = C * 9/5 + 32
# K = C + 273.15
fahrenheit = celsius * 9/5 + 32
kelvin = celsius + 273.15
print("Celsius -> Fahrenheit:")
print(np.concatenate([celsius, fahrenheit], axis=1))
print("\nCelsius -> Kelvin:")
print(np.concatenate([celsius, kelvin], axis=1))
Stacking and Splitting Arrays
Stacking Arrays
import numpy as np
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
# Stack vertically (on top of each other)
vertical = np.vstack([a, b])
print("Vertical stack:")
print(vertical)
# Output:
# [[1 2 3]
# [4 5 6]]
# Stack horizontally (side by side)
horizontal = np.hstack([a, b])
print("\nHorizontal stack:")
print(horizontal)
# Output: [1 2 3 4 5 6]
Splitting Arrays
import numpy as np
arr = np.arange(12)
print("Original:", arr)
# Split into 3 equal parts
parts = np.split(arr, 3)
print("\nSplit into 3:")
for i, part in enumerate(parts, 1):
print(f"Part {i}: {part}")
# Split at specific indices
parts = np.split(arr, [4, 8])
print("\nSplit at indices 4 and 8:")
for i, part in enumerate(parts, 1):
print(f"Part {i}: {part}")
Common Beginner Mistakes and How to Avoid Them
Mistake 1: Modifying Original Arrays Unintentionally
import numpy as np
original = np.array([1, 2, 3, 4, 5])
view = original # This is NOT a copy!
view[0] = 999
print(original) # Output: [999 2 3 4 5] - Original changed!
import numpy as np
original = np.array([1, 2, 3, 4, 5])
copy = original.copy() # Make an actual copy
copy[0] = 999
print(original) # Output: [1 2 3 4 5] - Original unchanged!
print(copy) # Output: [999 2 3 4 5]
Mistake 2: Wrong Axis in Operations
import numpy as np
grades = np.array([[85, 90, 78],
[88, 76, 92]])
# Want average per student but use wrong axis
avg = np.mean(grades, axis=0) # This gives average per subject!
print(avg) # Output: [86.5 83. 85. ]
import numpy as np
grades = np.array([[85, 90, 78],
[88, 76, 92]])
# Correct axis for per-student average
avg = np.mean(grades, axis=1) # Across columns
print(avg) # Output: [84.33... 85.33...]
Mistake 3: Forgetting NumPy Uses Different Operators
import numpy as np
arr = np.array([10, 20, 30])
# Using 'and' instead of '&'
# filtered = arr[(arr > 15) and (arr < 25)] # ERROR!
import numpy as np
arr = np.array([10, 20, 30])
# Use '&' for element-wise AND
filtered = arr[(arr > 15) & (arr < 25)]
print(filtered) # Output: [20]
Mistake 4: Mixing Python Lists and NumPy Arrays
# Python list multiplication
python_list = [1, 2, 3]
result = python_list * 3
print(result) # Output: [1, 2, 3, 1, 2, 3, 1, 2, 3] - Repeats the list!
# NumPy array multiplication
numpy_array = np.array([1, 2, 3])
result = numpy_array * 3
print(result) # Output: [3 6 9] - Multiplies each element!
These are completely different operations! Always be clear about whether you're working with lists or arrays.
Advanced Usage Tips
Tip 1: Use Vectorization Instead of Loops
import numpy as np
arr = np.arange(1000000)
result = []
for x in arr:
result.append(x ** 2)
# Takes ~100-200ms
import numpy as np
arr = np.arange(1000000)
result = arr ** 2
# Takes ~2-5ms - 20-50x faster!
Tip 2: Use Views to Save Memory
import numpy as np
# Large array
big_array = np.arange(10000000) # 10 million elements
# Slicing creates a view (shares memory)
slice_view = big_array[1000:2000] # Fast, no copy
# Fancy indexing creates a copy
fancy_copy = big_array[[1, 5, 10, 100]] # Slower, creates new array
# When you need a copy, be explicit
actual_copy = big_array[1000:2000].copy()
Tip 3: Preallocate Arrays for Speed
import numpy as np
result = np.array([])
for i in range(1000):
result = np.append(result, i) # Very slow!
import numpy as np
result = np.zeros(1000) # Preallocate
for i in range(1000):
result[i] = i # Much faster!
# Even better: avoid loops entirely
result = np.arange(1000) # Fastest!
Tip 4: Use np.where() for Conditional Replacements
import numpy as np
scores = np.array([45, 78, 92, 65, 88, 34, 90])
# Replace all scores below 50 with 50 (minimum passing)
# np.where(condition, value_if_true, value_if_false)
adjusted = np.where(scores < 50, 50, scores)
print(adjusted)
# Output: [50 78 92 65 88 50 90]
# Another example: Letter grades
grades = np.where(scores >= 90, 'A',
np.where(scores >= 80, 'B',
np.where(scores >= 70, 'C',
np.where(scores >= 60, 'D', 'F'))))
print(grades)
# Output: ['F' 'C' 'A' 'D' 'B' 'F' 'A']
Real-World Use Cases
Use Case 1: Image Processing
Images are just 2D (grayscale) or 3D (color) arrays of pixel values!
import numpy as np
# Simulate a 5x5 grayscale image (values 0-255)
image = np.random.randint(0, 256, size=(5, 5))
print("Original image:")
print(image)
# Brighten the image (add 50 to all pixels)
brightened = np.clip(image + 50, 0, 255) # Clip to valid range
print("\nBrightened:")
print(brightened)
# Convert to black and white (threshold at 128)
bw = np.where(image > 128, 255, 0)
print("\nBlack and white:")
print(bw)
Use Case 2: Financial Data Analysis
import numpy as np
# Stock prices over 10 days
prices = np.array([100, 102, 98, 103, 107, 105, 110, 108, 112, 115])
# Calculate daily returns (percentage change)
daily_returns = (prices[1:] - prices[:-1]) / prices[:-1] * 100
print("Daily returns (%):", daily_returns)
# Moving average (3-day window)
window = 3
moving_avg = np.convolve(prices, np.ones(window)/window, mode='valid')
print("3-day moving average:", moving_avg)
# Volatility (standard deviation of returns)
volatility = np.std(daily_returns)
print(f"Volatility: {volatility:.2f}%")
Use Case 3: Scientific Computing
import numpy as np
# Calculate trajectory of a projectile
# Physics: y = v0*t*sin(θ) - 0.5*g*t²
g = 9.8 # gravity (m/s²)
v0 = 20 # initial velocity (m/s)
angle = 45 # launch angle (degrees)
# Time points from 0 to 4 seconds
t = np.linspace(0, 4, 100)
# Convert angle to radians
theta = np.radians(angle)
# Calculate height at each time point
y = v0 * t * np.sin(theta) - 0.5 * g * t**2
# Find maximum height
max_height = np.max(y)
time_at_max = t[np.argmax(y)]
print(f"Maximum height: {max_height:.2f} m")
print(f"Time to reach max height: {time_at_max:.2f} s")
# When does it hit the ground? (y ≈ 0)
ground_indices = np.where(y <= 0)[0]
if len(ground_indices) > 1:
landing_time = t[ground_indices[1]]
print(f"Landing time: {landing_time:.2f} s")
Performance Comparison: NumPy vs Pure Python
Let's see the speed difference with a real benchmark:
import numpy as np
import time
size = 1000000 # 1 million elements
# Create data
python_list = list(range(size))
numpy_array = np.arange(size)
# Python list (with loop)
start = time.time()
result = [x ** 2 for x in python_list]
python_time = time.time() - start
# NumPy array (vectorized)
start = time.time()
result = numpy_array ** 2
numpy_time = time.time() - start
print(f"Python list time: {python_time:.4f} seconds")
print(f"NumPy array time: {numpy_time:.4f} seconds")
print(f"NumPy is {python_time/numpy_time:.1f}x faster!")
# Typical output:
# Python list time: 0.1234 seconds
# NumPy array time: 0.0045 seconds
# NumPy is 27.4x faster!
Quick Summary 📝
Creating Arrays: From lists with np.array(), sequential with np.arange(), evenly spaced with np.linspace(), filled arrays with np.zeros(), np.ones(), np.full()
Array Operations: Element-wise arithmetic, scalar operations, mathematical functions, statistical operations
Indexing & Slicing: Access elements with [], slice with [start:stop:step], boolean indexing with conditions
Reshaping: reshape(), flatten(), ravel(), vstack(), hstack(), split()
Key Concepts: Broadcasting, axis parameter, vectorization, views vs copies
Final Thoughts
NumPy is the foundation of scientific Python. Once you master arrays, you'll find they appear everywhere: in Pandas DataFrames, scikit-learn models, TensorFlow tensors, and more.
Comments
Post a Comment