# -*- coding: utf-8 -*-
"""C2S1 - Matrix (Images) in Python.ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/165vXorZLFCgEdntLefWhA7VWu_sw4sNh

# Chapter 2 - Session 1
# Matrix (Images) in Python

## Objectives
#### learn how to use python library call NumPy which is dedicated to matrix handling and related function (all images are form of matrixes)

## Content
#### 1) Installation and Importing NumPy Library
#### 2) Creating Matrixes using NumPy
#### 3) Indexing Matrixes
#### 4) Math Operations with Matrixes
#### 5) Get Statistics of Matrixes

<hr>

# 1) Installation and Importing NumPy Library

First we have to install NumPy to the Python using pip3 command as below
    
    pip3 install numpy
    
if you use Anaconda, NumPy library is already installed by default
    
then we can import and access various functions from NumPy library to our python work space
"""

'''import numpy library to python workspace as np'''

import numpy as np

"""# 2) Creating Matrixes using NumPy

Actually, we can represent vector or matrix as lists in python. as below

    [1,2,3,4] - vecor
    [[1,2,3,4],[5,6,7,8]] - matrix
    
but in order to do operation on matrixes, we have to use "for" loops which is not efficient and hectic. to address this, NumPy provide alternative way, which is easy to manage and efficient

"""

'''lists and numpy matrixes'''

my_list = [[1,2,3,4],[5,6,7,8]]
my_matrix = np.array([[1,2,3,4],[5,6,7,8]])

print(type(my_list))
print(type(my_matrix))

print('\n---------------------\n')

'''getting shape of matrixes'''
my_matrix = np.array([[1,2,3,4],[5,6,7,8]])
print(my_matrix.shape)

print('\n---------------------\n')

'''creating numpy arrays and matrixes'''

row_vector = np.array([1,2,3,4])
col_vector = np.array([1,2,3,4]).T #transpose
my_matrix = np.array([[1,2,3,4],[5,6,7,8]])

print(row_vector)
print(col_vector)
print(my_matrix)

print('\n---------------------\n')

'''we can use built in function in python to creat special matrixes, easily'''

zeros_mat = np.zeros((4,4))
ones_mat = np.zeros((4,4))
random_mat = np.random.rand(4,4)

print(zeros_mat)
print(ones_mat)
print(random_mat)

"""### Python Data Types

NumPy library has their own data types which are broader than python default data types

    int8 - Byte (-128 to 127)
    nt16 - Integer (-32768 to 32767)
    int32 - Integer (-2147483648 to 2147483647)
    uint8 - Unsigned integer (0 to 255)
    uint16 - Unsigned integer (0 to 65535)
    uint32 - Unsigned integer (0 to 4294967295)
    float16 - Half precision float: sign bit, 5 bits exponent, 10 bits mantissa
    float32 - Single precision float: sign bit, 8 bits exponent, 23 bits mantissa
    float64 - Double precision float: sign bit, 11 bits exponent, 52 bits mantissa
    
So we can define our own data type for matrix when we are creating it or we can converty between data types 
"""

'''get data type of matrix'''

my_matrix = np.array([[1,2,3,4],[5,6,7,8]])
print(my_matrix.dtype)

print('\n---------------------\n')

'''convert data type from float to int'''

my_matrix = np.array([1.5, 2.5, 3.5], dtype=np.float32)
print(my_matrix.dtype)
print(my_matrix)
my_matrix_int = np.array(my_matrix, dtype=np.int32)
print(my_matrix_int.dtype)
print(my_matrix_int)

"""### Exercise 1

Create a 4x4 matrix with values 1,2,3,4 in the diagonal

### Exercise 2

RGB images are 3-D matrixes. We will deal with 3-D matrixes in future sessions. So first, create random 3 by 3 random matrix and create 3-D matrix with 3x3x3 matrix with values from 1 to 27 values and print created matrixes

# 3) Indexing Matrixes

Once we have a matrix we should be able to access particular element of part of matrix (similar to lists, we extracted value at particular index from a list)

In case of matrixes also, we use indexes to extract values from a matrix. It's similar to Sub-setting a Image in Remote Sensing context
"""

my_matrix = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]])

print(my_matrix)

print('\n---------------------\n')

'''access only one value'''

print(my_matrix[0,0])
print(my_matrix[3,2])

print('\n---------------------\n')

'''access part of matrix'''

print(my_matrix[0:3,0:3])
print(my_matrix[1,:])
print(my_matrix[1:3,:])
print(my_matrix[2:,:])
print(my_matrix[:2,:])

"""### Exercise 3

Create a 4x4 matrix with values 1,2,3,4 in the diagonal and remove first , last columns and remove first , last rows and print final 2x2 matrix

# 4) Math Operations with Matrixes

Like numbers, we can perform mathematics operations with matrixes like Adding, Subtraction, Multiplication.. etc

Usually in matrixes operations perform in element-wise manner
"""

x = np.array([[1, 2, 3], [4, 5, 6]])
y = np.array([[1, 1, 1], [2, 2, 2]])

print(x)
print(y)

print('\n---------------------\n')

'''operatins with scalers'''

print(x * 0.1)
print(x + 10)
print(x / 10)


print('\n---------------------\n')

'''operatins between matrixes'''

print(x + y)
print(x * y)

print('\n---------------------\n')

'''we can use numpy fuctions to do advance element-wise operations on matrix'''

print(np.sqrt(x))
print(np.exp(x))

"""### Exercise 4

Create any 4by4 Matrix with decimal values and create and print matrix with rounded values of original matrix

### Exercise 5

Create any 4by4 Matrix and Calculate x^2+2x+1 for each element of the matrix

### Exercise 6

Create any 2 4by4 Matrix and Calculate (matrix 2 - matrix 1) / (matrix 2 + matrix 1) for each element of the matrix

# 5) Get Statistics of Matrixes

Now we can create, index, perform operations with matrixes. And there are another easy set of functions that allow us to calculate statistics parameters such as sum, mean, max, min, etc. of a matrix
"""

my_matrix = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]])

print(my_matrix)

print('\n---------------------\n')

print(np.sum(my_matrix))
print(np.mean(my_matrix))
print(np.max(my_matrix))
print(np.min(my_matrix))

"""In above examples, we calculate statistics for whole matrix. furthermore we can specify particular direction that we can calculate statistics.

In this example, we calculate statistics vertically and horizontally
"""

my_matrix = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]])

print(my_matrix)

print('\n---------------------\n')

print(np.sum(my_matrix, axis=0))
print(np.sum(my_matrix, axis=1))

"""Another important parameter of matrix is unique values in matrix. 

Let's imagine, if someone has given us classified satellite image. so we want to know, what are the class value and home many of them are there. this this case, we have to first find unique values in given image (matrix)

"""

my_matrix = np.array([3, 3, 3, 2, 2, 1, 1, 4, 4])
np.unique(my_matrix)

"""### Exercise 7

Create a 10x10 array with random values and find the minimum and maximum values

### Exercise 8

Create any 4by4 Matrix containing duplicates and print all unique values of that matrix and print number of unique values of the matrix

### Exercise 9

create matrix with 100 rows and 2 columns with random value, can calculate and print mean, standard deviation in each column

### Exercise 10

create 3-D matrix with 3x3x3 matrix with values from 1 to 27 values and print created matrixes. and calculate sum, mean, max, min in all 3 dimensions
"""

