Home / Computer Science / Class XI / Introduction to NumPy
Introduction to NumPy — CUET Computer Science hero
Class XI 💻 Computer Science Ch 17 of 19

Introduction to NumPy

CUET unit: Introduction to NumPy

📌 Snapshot

  • How NumPy arrays work in Python: creating them, slicing, the axis rule and statistics, with worked examples.
  • Why an array beats a list for numbers, and what each attribute (ndim, shape, size, dtype, itemsize) tells you.
  • Arithmetic, transpose, sort, concatenate, reshape and split, with the exact errors NTA likes to test.
  • Loading and saving CSV data with loadtxt, genfromtxt and savetxt, including how missing values behave.

📖 Detailed Notes

Written and reviewed by Rohit Singh, Computer Science & Commerce faculty at UniDrill (MCA, Osmania University · 6 years teaching). Based on NCERT Informatics Practices Class XI, Chapter 6.

Why can't a plain list do this?

Suppose you have the marks of 40 students and you want to add 5 grace marks to everyone. With a Python list you must write a loop. With a NumPy array you write marks + 5 and every element changes at once. That one idea, do the same thing to every element without a loop, is why NumPy sits underneath almost all data work in Python, including Pandas in Class XII.

NumPy stands for Numerical Python. It is a library for data analysis and scientific computing, and its core object is the ndarray (n-dimensional array), usually just called an array. You install it with pip install numpy and load it with import numpy as np. The alias np is a convention, not a rule, but the package name itself must be written in lowercase.

Array versus list

An array stores many values of the same data type under one name, packed side by side in memory, and each value is reached by a zero-based index. A list is far more relaxed, and that freedom is exactly what makes it slower for number crunching.

Feature List NumPy array
Element types Can mix int, float, string All elements share one type
Memory layout Not stored side by side; each element carries its own type information Stored in one contiguous block
Speed and memory Slower and heavier Faster and lighter, since the type is stored once
Element wise operations Not supported (list / 3 is an error) Supported (arr / 3 divides every element)
Belongs to Core Python The NumPy library

Why contiguous memory makes it fast. Because all elements have the same size and sit next to each other, the computer can jump straight to any element and sweep through the block without chasing scattered locations. Think of a train of identical coaches coupled together versus coaches parked at random places across a city.

Beyond the textbook. For the exam: an array holds one data type and is stored contiguously, so it is faster than a list. In reality: the speed also comes from NumPy running its loops in compiled C code instead of Python. And an array can be sliced without copying the data (a slice is a window onto the same memory, called a view), so changing a slice changes the original array. A list slice, by contrast, is a copy. Why it matters: if a program mysteriously changes data you did not touch, a shared view is the usual culprit.

Creating arrays from lists

The array() function converts a list into an array. A flat list gives a 1-D array (one row of elements). A nested list, meaning a list whose elements are lists, gives a 2-D array.

Code
import numpy as np
array1 = np.array([10, 20, 30])
array3 = np.array([[2.4, 3], [4.91, 7], [0, -1]])
print(array1)
print(array3)
Output
[10 20 30]
[[ 2.4   3.  ]
 [ 4.91  7.  ]
 [ 0.   -1.  ]]

Notice that the integers in array3 were turned into floats (3 became 3.0). An array has only one type, so NumPy promotes every element to the "widest" type present. With a string in the list, everything becomes a string.

Input list Resulting dtype Why
[1, 2, 3] integer All ints
[1, 2.5, 3] float64 One float promotes all
[5, -7.4, 'a', 7.2] Unicode string (U32), shown as '5', '-7.4', 'a', '7.2' One string promotes all

A classic slip is np.array(1, 2, 3, 4). That passes four arguments. The function needs one argument that is a list, so the correct form is np.array([1, 2, 3, 4]).

Attributes: what is inside this array?

Five attributes describe an array. They are properties, so they are written without brackets: a.shape, not a.shape().

Attribute What it gives Example for a 3 by 2 float array
ndim Number of dimensions (axes, also called rank) 2
shape Tuple with the length along each axis (3, 2)
size Total number of elements (product of the shape) 6
dtype Data type of every element float64
itemsize Bytes occupied by one element 8

A 2-D array has two axes. Axis 0 runs down the rows and axis 1 runs across the columns. A 1-D array's shape has a trailing comma, (3,), because a Python tuple with one item needs it.

Worked example: reading the attributes. For a = np.array([[1.5, 2, 3], [4, 5, 6]]), find ndim, shape, size, dtype and itemsize.

Step What we do Result
1 The list has two inner lists, so there are 2 rows. Each has 3 values, so 3 columns shape is (2, 3)
2 Two axes ndim is 2
3 Multiply the shape size is $2 \times 3 = 6$
4 One float (1.5) is present, so all are promoted to float dtype is float64
5 float64 uses 64 bits, and 8 bits make a byte itemsize is $64 / 8 = 8$ bytes

The common wrong step is to say itemsize is 64. That is the number of bits; itemsize is always in bytes. Likewise int32 and float32 give 4 bytes and int64 and float64 give 8 bytes.

Beyond the textbook. For the exam: an integer array shows int32 and itemsize 4. In reality: the default integer type depends on your machine and NumPy version, so the same code can print int64 and itemsize 8 on another computer. Write dtype=int32 or dtype=int64 explicitly if the exact size matters. Why it matters: never hard code an itemsize in your head. Work it out from the dtype named in the question.

Other ways to create arrays

Call What it makes Output
np.array([[1, 2], [3, 4]], dtype=float) Converts the data to the type you name 2 by 2 floats: 1. 2. / 3. 4.
np.zeros((3, 4)) 3 rows, 4 columns, all 0. Default type is float
np.ones((3, 2)) 3 rows, 2 columns, all 1. Default type is float
np.arange(6) Like range(6) but returns an array [0 1 2 3 4 5]
np.arange(-2, 24, 4) Start, stop (excluded), step [-2 2 6 10 14 18 22]

For zeros() and ones() the shape goes in as one tuple, so there are double brackets: np.zeros((3, 4)). Pass dtype=int to get integer zeros or ones. In arange() the stop value is never included, exactly like range(), so np.arange(2, 10, 3) gives [2 5 8]: the next value, 11, is past the stop.

Pause and predict: what does np.arange(1, 10, 3) give, and what is its size?

Check your answer: Start at 1, step 3, stop before 10: [1 4 7]. The next value would be 10, which is the excluded stop. So the size is 3.

Indexing and slicing

Indexing picks single elements. For a 2-D array you give two indices, array[row, column], and both start at 0. Going past the end raises an IndexError.

Slicing picks a block using start:end, where the end index is excluded and a missing start or end means "from the beginning" or "to the end". ::-1 reverses. For a 2-D array you slice each axis, separated by a comma: array[row_slice, column_slice]. A bare : means "everything along this axis".

Worked example: slicing a marks table. The marks of four students in Maths, English and Science are stored as shown.

Code
marks = np.array([[78, 67, 56],
                  [76, 75, 47],
                  [84, 59, 60],
                  [67, 72, 54]])
print(marks[3, 1])
print(marks[:, 1])
print(marks[1:3, 0:2])
print(marks[::-1])
Output
72
[67 75 59 72]
[[76 75]
 [84 59]]
[[67 72 54]
 [84 59 60]
 [76 75 47]
 [78 67 56]]
Expression Thinking Result
marks[3, 1] Row index 3 is the 4th student, column index 1 is the 2nd subject 72
marks[:, 1] All rows, column index 1 only [67 75 59 72]
marks[1:3, 0:2] Rows 1 and 2 (3 is excluded), columns 0 and 1 (2 is excluded) [[76 75] [84 59]]
marks[::-1] Reverse along the first axis, so the last row comes first rows in reverse order

The wrong step to avoid: reading marks[1:3, 0:2] as "rows 1 to 3, columns 0 to 2", which would give three rows and three columns. The end is excluded on both axes, so the result has $3 - 1 = 2$ rows and $2 - 0 = 2$ columns. A quick check: the shape of a slice is (end minus start) along each axis.

Pause and predict: for the same marks array, what is marks[0, 4]?

Check your answer: An IndexError. There are only 3 columns, so the valid column indices are 0, 1 and 2. Index 4 is out of bounds for axis 1.

Arithmetic on arrays

When you use +, -, *, /, % or ** on two arrays of the same shape, NumPy works on each corresponding pair of elements. This is called an element wise operation. With a single number on one side (marks + 5, marks / 3) the number is applied to every element.

Worked example: element wise versus matrix product. Let array1 = np.array([[3, 6], [4, 2]]) and array2 = np.array([[10, 20], [15, 12]]).

Expression Thinking Result
array1 + array2 Add matching positions: 3+10, 6+20, 4+15, 2+12 [[13 26] [19 14]]
array1 * array2 Multiply matching positions: 30, 120, 60, 24 [[30 120] [60 24]]
array1 @ array2 Row by column: first cell is 3 times 10 plus 6 times 15 [[120 132] [70 104]]
array2 / array1 Divide matching positions [[3.33 3.33] [3.75 6.]] (floats)
array2 % array1 Remainders: 10 mod 3, 20 mod 6, 15 mod 4, 12 mod 2 [[1 2] [3 0]]
array1 ** 3 Cube each element [[27 216] [64 8]]

The common wrong step is to treat * as matrix multiplication. In NumPy, * is always element wise. The matrix product uses the @ operator. Check the first cell both ways: element wise gives $3 \times 10 = 30$, while the matrix product gives $3 \times 10 + 6 \times 15 = 120$.

If the shapes differ, for example a 2 by 2 array with a 3 by 3 array, the operation fails with an error. The textbook rule to remember is that the shapes must be equal.

Transpose, sort, concatenate, reshape and split

Transpose swaps rows and columns, so a shape (3, 4) becomes (4, 3). Write a.transpose() or a.T. The original array is not changed.

Sort arranges values in ascending order. For a 2-D array the default is row wise (axis=1, each row sorted on its own), and axis=0 sorts each column on its own. Here sort() changes the array itself and returns nothing.

Code
x = np.array([[10, -7, 0, 20], [-5, 1, 200, 40], [30, 1, -1, 4]])
x.sort()
print(x)
Output
[[ -7   0  10  20]
 [ -5   1  40 200]
 [ -1   1   4  30]]

Re-creating x and calling x.sort(axis=0) instead gives [[-5 -7 -1 4] [10 1 0 20] [30 1 200 40]], where every column is now ascending from top to bottom.

Concatenate joins arrays with np.concatenate((a, b), axis). The default is axis=0 (stack below). All dimensions must match except along the axis you join. Reshape changes the shape with a.reshape(r, c) but cannot change how many elements there are. Split cuts an array into pieces with np.split(a, indices_or_N, axis): give a list of cut positions, or an integer N for N equal parts (the default axis is 0).

Worked example: when does concatenation fail? a has shape (2, 2) and b = np.zeros((2, 3), dtype=a.dtype) has shape (2, 3).

Call Thinking Result
np.concatenate((a, b), axis=1) Joining side by side needs equal rows: both have 2 works, shape (2, 5)
np.concatenate((a, b), axis=0) Stacking needs equal columns: 2 versus 3 ValueError, dimensions must match

The memory trick: the axis you join along is the one that is allowed to differ; every other axis must be equal.

Worked example: reshape and split. Take arr = np.arange(10, 22), which has 12 elements.

Call Thinking Result
arr.reshape(3, 4) $3 \times 4 = 12$, so it fits 3 rows: 10..13, 14..17, 18..21
arr.reshape(2, 6) $2 \times 6 = 12$, so it fits 2 rows of 6
arr.reshape(4, 4) $16 \ne 12$ error: cannot reshape size 12 into (4, 4)
np.split(arr, 3) 12 divides equally into 3 parts of 4 three arrays of 4 elements
np.split(arr, 5) 12 does not divide equally by 5 error: not an equal division
np.split(arr, [1, 3]) Cut before positions 1 and 3 three pieces, sizes 1, 2 and 9

For a 2-D array, np.split(a, [1, 3]) cuts rows at indices 1 and 3, giving three pieces. Adding axis=1 cuts at column indices instead. The pieces come back as a list, so you can unpack them: first, second, third = np.split(a, [1, 3]).

Beyond the textbook. For the exam: arrays cannot be expanded, so concatenate is how you "add" elements. In reality: concatenate builds a brand new array and copies the data into it, which is why growing an array one element at a time inside a loop is slow. Collect values in a list first, then convert once. Also, a.sort() changes a in place and returns None, while the function np.sort(a) returns a sorted copy and leaves a as it was. Why it matters: b = a.sort() leaves b empty, a mistake that shows up as a famous MCQ trap.

Statistics and the axis rule

NumPy gives max(), min(), sum(), mean() and std(). With no argument they work on the whole array and return one number. With axis they work along that axis.

The rule that never fails: the axis you name is the one that disappears.

Call What collapses What you get
axis=0 The rows are combined, going down each column One result per column
axis=1 The columns are combined, going across each row One result per row

Worked example: statistics on the marks array. Using the 4 by 3 marks array from earlier:

Call Thinking Result
marks.max() Whole array 84
marks.max(axis=0) Biggest in each of the 3 columns (subjects) [84 75 60]
marks.sum(axis=1) Total for each of the 4 rows (students) [201 198 203 193]
marks.mean(axis=0) Average per subject: for column 0, $(78+76+84+67)/4$ [76.25 68.25 54.25]

The wrong step is to say "axis=0 means row wise". It does not; axis=0 gives column results because it collapses the row axis. A shape check helps: for a (4, 3) array, axis=0 returns 3 numbers and axis=1 returns 4.

Beyond the textbook. For the exam: std() is the standard deviation of the values. In reality: NumPy's std() divides by $N$ (the number of values), the population formula. Many calculators and the Pandas std() method divide by $N - 1$ instead, so you can get a slightly different number for the same data. Why it matters: when two tools disagree on a standard deviation, this is usually the reason.

Pause and predict: for B = np.array([[3, 6], [4, 2]]), what do B.sum(axis=0) and B.mean(axis=1) give?

Check your answer: axis=0 adds down the columns: $3+4 = 7$ and $6+2 = 8$, so [7 8]. axis=1 averages across the rows: $(3+6)/2 = 4.5$ and $(4+2)/2 = 3$, so [4.5 3. ].

Loading and saving data files

Real data lives in files, most often CSV (comma separated values) text files. Every row must have the same number of values.

Function Use Key parameters
np.loadtxt(file, ...) Load clean numeric data skiprows=1 skips the header, delimiter=',', dtype=int, unpack=True
np.genfromtxt(file, ...) Load data that may have missing or non numeric entries skip_header=1, delimiter=',', dtype, filling_values
np.savetxt(file, array, ...) Write an array to a text file delimiter=',', fmt='%i' for integers (default is float)

Two defaults catch students out. The default delimiter is whitespace (so for a comma file you must say delimiter=','), and the default dtype is float, so you get 36. unless you ask for dtype=int.

unpack=True transposes the loaded data so each column becomes its own array. For a file with columns RollNo, Marks1, Marks2 you can write rollno, mks1, mks2 = np.loadtxt('data.txt', skiprows=1, delimiter=',', unpack=True, dtype=int). Without it you get rows instead.

Missing values. loadtxt() stops with an error when a field cannot be converted. genfromtxt() carries on and fills the gap.

Situation Result in genfromtxt()
Float data (default), value missing or not a number nan
dtype=int, value missing or not a number -1
filling_values=-999 given -999

So a row 2,ab,23,45 loaded as floats becomes 2. nan 23. 45., and a row 3,43,51, ends with nan.

Pause and predict: a file has the header line RollNo,Marks, and you call np.loadtxt('f.txt', delimiter=',') with no skiprows. What happens?

Check your answer: It fails, because the header contains text that cannot be converted to a number. Adding skiprows=1 tells NumPy to ignore that first line.

Function sheet

Task Code Remember
Create from list np.array([..]) One list argument, in square brackets
Zeros or ones np.zeros((r, c)), np.ones((r, c)) Shape as a tuple, float by default
Number sequence np.arange(a, b, s) b is excluded
Inspect .ndim .shape .size .dtype .itemsize No brackets; itemsize in bytes
Matrix product a @ b * is element wise
Transpose a.T or a.transpose() Original unchanged
Sort a.sort(), a.sort(axis=0) In place; default axis 1 for 2-D
Join np.concatenate((a, b), axis) Other axes must match
Reshape a.reshape(r, c) $r \times c$ must equal size
Split np.split(a, [i, j], axis) Integer N needs an equal division
Statistics max min sum mean std with axis Named axis disappears
File in or out loadtxt, genfromtxt, savetxt genfromtxt tolerates missing data

Exam traps

Trap Why it is tempting The truth
"axis=0 is row wise" Axis 0 is called the row axis axis=0 collapses rows and gives one result per column
"* multiplies matrices" It does in maths class * is element wise; use @ for the matrix product
"arange(2, 10, 3) includes 10" The stop looks like part of the range The stop is excluded: [2 5 8]
"zeros() gives integers" Zero looks like an integer The default dtype is float; add dtype=int for integers
"np.array(1, 2, 3) makes an array" Looks like a list of items It needs one list argument: np.array([1, 2, 3])
"reshape can make room for more elements" The shape is just a label The element count must stay the same, otherwise error
"b = a.sort() stores the sorted array" Most functions return a result sort() works in place and returns None
"loadtxt handles missing values" Both functions load text files loadtxt fails; genfromtxt fills nan, -1 or your filling_values

60-second revision

Topic Remember
Array Same type, contiguous memory, zero based index, fixed size
Attributes ndim dims, shape tuple, size product, dtype type, itemsize bytes
itemsize int32 and float32 give 4; int64 and float64 give 8
Promotion Mixed list: int becomes float, anything becomes string if a string is present
Slicing [start:end], end excluded; 2-D is [rows, cols]; ::-1 reverses
Arithmetic Same shape, element wise; @ is the matrix product
Sort Default axis 1 (each row); axis=0 sorts each column
Concatenate Default axis 0; every other axis must match
Axis rule The named axis disappears
Files skiprows, delimiter, dtype (default float), unpack for columns

Where this chapter connects

NumPy arrays are the engine behind the Series and DataFrame objects in Data Handling Using Pandas - I, where the same ideas of shape, axis and element wise arithmetic return with row and column labels added. The descriptive statistics here (mean, standard deviation) come from Understanding Data. For working with Python's own list structure, see Lists, and for turning arrays into charts, see Plotting Data using Matplotlib.

🎯 Practice MCQs

First 3 questions free · create a free account to unlock the rest — answers & explanations included, no payment needed

Q1. What is the default data type of the array created by np.zeros((3, 4))?

▸ Show answer & explanation

Answer: C

Both zeros() and ones() create arrays of floating point numbers unless you pass dtype. Option A is wrong because integer zeros need dtype=int. Options B and D are unrelated to the default.

Q2. Consider: (1) a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) (2) print(a[1:3, 0:2]). What is printed?

▸ Show answer & explanation

Answer: B

The row slice 1:3 selects rows at index 1 and 2, and the column slice 0:2 selects columns at index 0 and 1, because the end index is excluded. That gives [4 5] and [7 8]. Option A starts from row 0. Option C takes columns 1 and 2. Option D keeps all three columns.

Q3. Assertion (A): np.array([1, 2, 3]) + np.array([4, 5, 6]) gives array([5, 7, 9]). Reason (R): Arithmetic operators on two arrays of the same shape act on corresponding pairs of elements.

▸ Show answer & explanation

Answer: A

1+4, 2+5 and 3+6 give 5, 7 and 9, so the assertion is true. The reason states the pairing rule, which is exactly why the result is element by element, so R explains A. The result is not a joined list like [1, 2, 3, 4, 5, 6], which is what happens with lists.

🔒 12 more practice MCQs

Create a free account to unlock every MCQ in this chapter — answers and explanations included. No payment needed.

Already registered? Just log in and they'll all appear here.

📊 Previous-Year Questions

Practise with real CUET Computer Science previous-year papers — every question solved, with the correct answer and a step-by-step explanation.

View solved CUET PYQ papers →

Ready to drill Computer Science?

Unlock all MCQs, chapter tests, mocks & PYQs for ₹199/year.

Get UniDrill Pro