Array Defination
Array is a fundamental data structure used in programming to store a collection of elements of the same type.
Basic Array Defination by Numpy:
1 2 3 4 5 6 7 | from numpy import ndarray, array array_: ndarray = array([1, 2, 3, 4, 5]) print(array_) >>> array([1, 2, 3, 4, 5]) |
Fixed Types and Memory
Size of Any Object: Typically refers to the amount(bytes) of memory that the object occupies in the computer’s memory(RAM).
Byte: Byte is a memory measurement unit in electronics and computer science, generally consisting of 1 or 0 values across an 8-bit sequence, independent of the type of information being stored and recorded.
Dtypes
Dtype: In NumPy, dtype
refers to the data type object, which represents the data type of elements stored in a NumPy array.
Signed Integers:
- int8,
- int16
- int32
- int64
1 2 3 4 5 6 7 8 9 | from numpy import array, ndarray array([-1, -2, 6, 4, 5, 8, 9, 4, 5, 6, 47, 77], dtype="int8") >>> array([-1, -2, 6, 4, 5, 8, 9, 4, 5, 6, 47, 77], dtype="int8") array([-1, -2, 6, 4, 5, 8, 9, 4, 5, 6, 47, 77], dtype="int16") >>> array([-1, -2, 6, 4, 5, 8, 9, 4, 5, 6, 47, 77], dtype="int16") |
Int dtypes provides holding all signed and unsigned number among maximum supported number of bit.
Unsigned Integers:
- uint8
- uint16
- uint32
- uint64
1 2 3 4 5 | from numpy import ndarray, array array([1, 2, 6, 4, 5, 8, 9, 4, 5, 6, 47, 77], dtype="uint8") >>> array([1, 2, 6, 4, 5, 8, 9, 4, 5, 6, 47, 77], dtype="uint8") |
Unlike Int dtypes, UInt dtypes doesn’t support signed values such as -5, -2 …
Floating Point Numbers:
- float16
- float32
- float64
- float128
1 2 3 4 5 | from numpy import ndarray, array array([1.3, 5.7, 11.8, 99.0, 5, 7, -21], dtype="float16") >>> array([1.3, 5.7, 11.8, 99.0, 5, 7, -21], dtype="float16") |
Float dtype provides storing all floating numbers among maximum supported range of defined dtype.
Complex Numbers:
- complex64
- complex128
- complex256
1 2 3 4 5 6 7 | from numpy import array, ndarray complex_array: ndarray = array([1 + 2j, 3 - 1j, 5 + 6j], dtype=np.complex128) complex_array >>> array([1.+2.j, 3.-1.j, 5.+6.j]) |
We can define same array such as [1, 2, 3, 4, 5] different datatypes such as float64, int8, int32 but it will occupy different sizes in memory.
Boolean:
- bool
1 2 3 4 5 6 7 | from numpy import ndarray , array bool_array: ndarray = array([True, False, True, True], dtype=bool) bool_array >>> array([True, False, True, True], dtype=bool) |
Object:
- object
Object dtype allows you to store arbitrary Python objects in the array.
1 2 3 4 5 6 7 8 9 | from numpy import array, ndarray data = [1, 'hello', {'key': 'value'}, [1, 2, 3]] array_ = array(data, dtype=object) array_ >>> array([1, 'hello', {'key': 'value'}, list([1, 2, 3])], dtype=object) |
String:
- str
1 2 3 4 5 6 7 8 9 | from numpy import ndarray, array data = ['apple', 'banana', 'orange', 'grape'] arr: ndarray = array(data, dtype='str') arr >>> array(['apple', 'banana', 'orange', 'grape'], dtype='<U6') |
Memory
In programming languages all objects (such as list, dictionaries, arrays etc.) occupy memory in RAM. This occupation ratio directly related to data-type of array such as int8, int16 or complex64 etc.
Lets Define base array to measure number of bytes.
1 2 3 | array([1,2,3,4,5]).nbytes >>> 20 |
Default array dtype returns 20 bytes. Let’s change our dtype and measure change of number of bytes.
1 2 3 | array([1,2,3,4,5], dtype="int8").nbytes >>> 5 |
Let’s change our dtype to int16;
1 2 3 | array([1,2,3,4,5], dtype="int16").nbytes >>> 10 |
1 2 3 4 5 6 7 | array([1,2,3,4,5], dtype="int32").nbytes >>> 20 array([1,2,3,4,5], dtype="int64").nbytes >>> 40 |
1 2 3 4 5 6 7 | array([1,2,3,4,5], dtype="float32").nbytes >>> 20 array([1,2,3,4,5], dtype="float64").nbytes >>> 40 |
To sum up, numpy is mostly written in C so, we can define/change dtypes of any array. Change in dtype occurs change in memory. We need to choose the correct dtype for our ndarrays.
Accessing Arrays
Define base array to index and update:
1 2 3 4 5 6 7 8 9 10 11 12 13 | from numpy import ndarray, array array_1: ndarray = array([ ['a', 'b', 'c'], ['k', 'l', 'm'], ['x', 'y', 'z'] ]) array_1 >>> array([['a', 'b', 'c'], ['k', 'l', 'm'], ['x', 'y', 'z']], dtype='<U1') |
Shape of array:
1 2 3 | array_1.shape >>> (3,3) |
Basic Indexing
Indexing: Accessing individual elements within a data structure, such as an array, list, or string, using a numerical key.
Standart Index
In Python, counting starts from zero.
Unlike lists, numpy arrays are usually multi-dimensional. When indexing any n-dimensional array, we need to specify coordinates according to number of dimensions:
- 1D Array: [x]
- 2D Array: [x, y]
- 3D array: [x ,y ,z]
Let’s try Index array_1(defined above):
- Indexing First Row:
1 2 3 | array_1[0] >>> array(['a', 'b', 'c'], dtype='<U1') |
- Indexing Second Row
1 2 3 | array_1[1] >>> array(['k', 'l', 'm'], dtype='<U1') |
- Indexing first element
1 2 3 | array_1[0, 0] >>> 'a' |
- Indexing Any Element
1 2 3 4 5 6 7 8 9 10 11 | array_1[0, 0] >>> 'a' array_1[1, 1] >>> 'l' array[2, 2] >>> 'z' |
- Indexing First Row
1 2 3 | array_1[:,0] >>> array(['a', 'k', 'x'], dtype='<U1') |
- Indexing Any Row
1 2 3 4 5 6 7 | array_1[:, 1] >>> array(['b', 'l', 'y'], dtype='<U1') array_1[:, 2] >>> array(['c', 'm', 'z'], dtype='<U1') |
- Indexing Any Range
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | array_1[1:, 2] >>> array(['m', 'z'], dtype='<U1') array_1[0:2, 1] >>> array(['b', 'l'], dtype='<U1') array_1[0:2] >>> array([['a', 'b', 'c'], ['k', 'l', 'm']], dtype='<U1') array_1[:, 0:2] >>> array([['a', 'b'], ['k', 'l'], ['x', 'y']], dtype='<U1') |
Reverse Index
In python, reverse indexing starts from -1 and decreases.
- Indexing Last Row
1 2 3 | array_1[-1] >>> array(['x', 'y', 'z'], dtype='<U1') |
- Indexing Last Element
1 2 3 | array_1[-1,-1] >>> 'z' |
- Indexing Any Element
1 2 3 | array_1[-1, -2] >>> 'y' |
Alter/Update Arrays
Numpy arrays are changeable. We can concatenate, remove, alter elements/lines.
Update Elements
We can update element by assigning new value to spesific index.
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 | from numpy import ndarray, array test_array: ndarray = array([ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]) test_array >>> array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) test_array[1,2] >>> 6 test_array[1,2] = -99 test_array >>> array([[ 1, 2, 3], [ 4, 5, -99], [ 7, 8, 9]]) |
We can update row/column:
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 | from numpy import ndarray, array test_array: ndarray = array([ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]) test_array >>> array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) test_array[0] = array([7,77,77]) test_array >>> array([[ 7, 77, 77], [ 4, 5, -99], [ 7, 8, 9]]) test_array[:,1] = array([0,11,222]) test_array >>> array([[ 7, 0, 77], [ 4, 11, -99], [ 7, 222, 9]]) |
Insert Row/Column
We can add row or column.
Row Insertion:
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 | import numpy as np from numpy import ndarray, array test_array: ndarray = array([ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]) test_array >>> array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) new_line = np.array([[9, 99, 999]]) new_array = np.concatenate((test_array, new_line), axis=0) new_array >>> array([[ 1, 2, 3], [ 4, 5, 6], [ 7, 8, 9], [ 9, 99, 999]]) |
Column Insertion:
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 | import numpy as np from numpy import ndarray, array test_array: ndarray = array([ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]) test_array >>> array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) new_line = np.array([[9], [99], [999]]) new_array = np.concatenate((test_array, new_line), axis=1) new_array >>> array([[ 1, 2, 3, 9], [ 4, 5, 6, 99], [ 7, 8, 9, 999]]) |
Row/Column Deletion
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 | import numpy as np from numpy import ndarray, array test_array: ndarray = array([ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]) test_array >>> array([ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]) test_array = np.delete(test_array, 1, axis=0) test_array >>> array([ [1, 2, 3], [7, 8, 9]] ) test_array = np.delete(test_array, 0, axis=1) test_array >>> array([ [2, 3], [8, 9] ]) |
Other Array Defination Types
Zeros Array
Numpy provides an array creation that filled with zeros.
1 2 3 4 5 6 7 8 9 10 11 12 | import numpy as np np.zeros(5) >>> array([0., 0., 0., 0., 0.]) np.zeros((3,3)) >>> array([[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]]) |
Ones Array
Numpy provides an array creation that filled with ones.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import numpy as np np.ones(7) >>> array([1., 1., 1., 1., 1., 1., 1.]) np.ones((4,4)) >>> array([[1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.]]) np.ones((4,4), dtype="int8") >>> array([[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]], dtype=int8) |
Fixed Value Arrays
Numpy provides an array creation that filled with fixed value.
1 2 3 4 5 6 7 | import numpy as np np.full((3,3), -1.25, dtype="float16") >>> array([[-1.25, -1.25, -1.25], [-1.25, -1.25, -1.25], [-1.25, -1.25, -1.25]], dtype=float16) |
Indentity Matrix
Numpy provides an indentity array creation.
1 2 3 4 5 6 7 8 | import numpy as np np.eye(4, dtype="int8") >>> array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], dtype=int8) |
Range Based Arrays
Numpy provides array that made from range based
Step Based
In numpy, we can create arrays which has start point, end point and step size.
1 2 3 4 5 6 7 8 9 10 | import numpy as np range_array_1 = np.arange(-5, 5, 0.5) range_array_1 >>> array([-5., -4.5, -4., -3.5, -3., -2.5, -2., -1.5, -1., -0.5, 0., 0.5, 1., 1.5, 2., 2.5, 3., 3.5, 4., 4.5]) |
Piece Based
In numpy, we can generate an array of evenly spaced numbers over a specified interval.
1 2 3 4 5 6 7 8 9 10 11 | import numpy as np range_array_2 = np.linspace(-5, 5, 24) range_array_2 >>> array([-5. , -4.56521739, -4.13043478, -3.69565217, -3.26086957, -2.82608696, -2.39130435, -1.95652174, -1.52173913, -1.08695652, -0.65217391, -0.2173913 , 0.2173913 , 0.65217391, 1.08695652, 1.52173913, 1.95652174, 2.39130435, 2.82608696, 3.26086957, 3.69565217, 4.13043478, 4.56521739, 5. ]) |
Random Based Arrays
Numpy provides random based arrays.
randn
randn generates an array of random numbers drawn from a standard normal distribution (mean 0, standard deviation 1).
1 2 3 4 5 6 7 | import numpy as np np.random.rand(3,2) >>> array([[0.13632219, 0.71289593], [0.1076102 , 0.74908037], [0.14968902, 0.82732271]]) |
randint
randint generates random integers.
1 2 3 4 5 6 7 8 9 | import numpy as np np.random.randint(0, 7, size=(5, 5)) >>> array([[2, 5, 4, 0, 0], [6, 1, 0, 4, 1], [3, 0, 3, 3, 6], [1, 1, 3, 5, 6], [0, 1, 6, 5, 3]]) |
Copying Arrays
In NumPy, there’s a difference between assigning arrays and copying arrays. This difference could be crucial as it affects how changes made to one array affect another.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | from numpy import array, ndarray import numpy as np array_1 = array([1, 2, 3, 4, 5]) array_1 >>> array([1, 2, 3, 4, 5]) array_2 = array_1 array_2 >>> array([1, 2, 3, 4, 5]) |
In the Above, we defined “array_1”, than we assigned “array_1” to “array_2”
If we update array_1 or array_2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | from numpy import array, ndarray import numpy as np array_1 = array([1, 2, 3, 4, 5]) array_2 = array_1 array_2[0] = -99 print("array_1:", array_1) print("array_2:", array_2) >>> array_1: [-99 2 3 4 5] >>> array_2: [-99 2 3 4 5] |
In the above we realized , if we assiagned any array to array: we’re just referencing array from memory. We can overcome this situation by using numpy’s copy() method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | from numpy import array, ndarray import numpy as np array_1 = array([1, 2, 3, 4, 5]) array_2 = array_1.copy() array_2[0] = -99 print("array_1:", array_1) print("array_2:", array_2) >>> array_1: [1 2 3 4 5] >>> array_2: [-99 2 3 4 5] |