In [2]:
    
import numpy as np
    
In [3]:
    
arr1 = np.random.randint(10,30, size=8)
arr1
    
    Out[3]:
In [4]:
    
arr2 = np.random.randint(20,200,size=50).reshape(5,10)  #method chaining - numbers from 0 to 50
arr2
    
    Out[4]:
In [8]:
    
arr1[0]
    
    Out[8]:
In [9]:
    
arr1[3]
    
    Out[9]:
In [11]:
    
arr1[:3] #get the first 3 elements. Gets lower bounds inclusive, upper bound exclusive
    
    Out[11]:
In [14]:
    
arr1[2:] #lower bound inclusive
    
    Out[14]:
In [15]:
    
arr1[2:5] #get elements at index 2,3,4
    
    Out[15]:
In [16]:
    
arr2
    
    Out[16]:
In [18]:
    
arr2[0,0] #style 1 - you pass in a list of indices
    
    Out[18]:
In [19]:
    
arr2[0][0] #style 2 - parse it as list of lists - not so popular
    
    Out[19]:
In [20]:
    
arr2[1] # get a full row
    
    Out[20]:
In [22]:
    
#get the second column
arr2[:,1]
    
    Out[22]:
Thus, you specify : for all columns, followed by 1 for column. And you get a 1D array of the result
In [24]:
    
#get the 3rd row
arr2[2,:] #which is same as arr2[2]
    
    Out[24]:
In [27]:
    
#get the center 3,3 elements - columns 4,5,6 and rows 1,2,3
arr2[1:4, 4:7]
    
    Out[27]:
In [28]:
    
arr2
    
    Out[28]:
In [31]:
    
arr2_subset = arr2[1:4, 4:7]
arr2_subset
    
    Out[31]:
In [34]:
    
arr2_subset[:,:] = 999 #assign this entire numpy the same values
arr2_subset
    
    Out[34]:
In [35]:
    
arr2 #notice the 999 in the middle
    
    Out[35]:
In [38]:
    
arr2_subset_a = arr2_subset
arr2_subset_a is arr2_subset
    
    Out[38]:
Notice they are same obj in memory
In [36]:
    
arr3_subset = arr2_subset.copy()
arr3_subset
    
    Out[36]:
In [37]:
    
arr3_subset is arr2_subset
    
    Out[37]:
Notice they are different objects in memory. Thus changing arr3_subset will not affect its source
In [39]:
    
arr3_subset[:,:] = 0.1
arr2_subset
    
    Out[39]:
In [41]:
    
arr1
    
    Out[41]:
Get all numbers greater than 15
In [42]:
    
arr1[arr1 > 15]
    
    Out[42]:
In [43]:
    
arr1[arr1 > 12]
    
    Out[43]:
just the condition returns a boolean matrix of same dimension as the one being queried
In [44]:
    
arr1 > 12
    
    Out[44]:
In [46]:
    
arr2[arr2 > 50] #looses the original shape as its impossible to keep the 2D shape
    
    Out[46]:
In [47]:
    
arr2[arr2 < 30]
    
    Out[47]:
In [5]:
    
arr_sum = arr1 + arr1
arr_sum
    
    Out[5]:
In [6]:
    
arr_cubed = arr1 ** 2
arr_cubed
    
    Out[6]:
Similarly, you can add a scalar to an array and NumPy will broadcast that operation on all the elements.
In [7]:
    
arr_cubed - 100
    
    Out[7]:
In [9]:
    
arr_cubed[0] = 0
arr_cubed
    
    Out[9]:
In [10]:
    
arr_cubed / 0
    
    
    Out[10]:
Thus 0/0 = nan and num/0 = inf
Numpy has a bunch of universal functions that work on the array elements one at a time and allow arrays to be used or treated as scalars.
Before writing a loop, look up the function list here