import numpy as np
import pandas as pd

"""
Explanation: Pandas
Pandas is a software library written for the Python programming language for data manipulation and analysis. In particular, it offers data structures and operations for manipulating numerical tables and time series. The data manipulation capabilities of pandas are built on top of the numpy library. In a way, numpy is a dependency of the pandas library.
In this notebook we'll try various pandas methods and in the process learn more about Pandas.
Installation
Please follow this link. All the necessary steps are mentioned here.
Importing Pandas
Once Pandas is installed, we can use it our file
End of explanation
"""


seriesLabel = ['label1', 'label2', 'label3']
exampleList = [5, 10, 20]

pd.Series(exampleList)

pd.Series(exampleList, seriesLabel)

"""
Explanation: Series
Series are similar to numpy arrays. The only difference between them is that series can have axis labels which means that it can be indexed by a label and also by number location.
Creating Series
There are various ways to create Series. Some of them are listed below.

Using Python List
End of explanation
"""


exampleNumpyArray = np.array([6, 12, 18])

pd.Series(exampleNumpyArray)

pd.Series(exampleNumpyArray, seriesLabel)

"""
Explanation: Using Numpy Arrays
End of explanation
"""


exampleDictionary = { 'label4': 7, 'label5': 14, 'label6': 21 }

# No need to mention labels parameter
pd.Series(exampleDictionary)

# If you mention different labels for a dictionary
pd.Series(exampleDictionary, seriesLabel)

"""
Explanation: Using Dictionary
End of explanation
"""


def sampleFunc1():
    pass

def sampleFunc2():
    pass

def sampleFunc3():
    pass

pd.Series(data=[sampleFunc1, sampleFunc2, sampleFunc3])

pd.Series(['a', 2, 'hey'])

"""
Explanation: Data and Index Parameter in Series

Data

Series can hold a variety of data.
End of explanation
"""


pd.Series(data=[sampleFunc1, sampleFunc2, sampleFunc3], index=['a', 'b', 'c'])

pd.Series(['a', 2, 'hey'], ['label', 2, 'key'])

"""
Explanation: Index

It is the second parameter which acts as the label for the series.
End of explanation
"""


pd.DataFrame(data = np.random.randint(1,51, (4,3)), index = ['row1', 'row2', 'row3', 'row4'], columns = ['col1', 'col2', 'col3'])

"""
Explanation: DataFrames
DataFrames are like spreadsheets or SQL tables. DataFrames are utilised a lot by pandas users.
Creating a DataFrame
pd.DataFrame( data, index, columns )
data -> content of the cells<br>
index -> labels for rows<br>
columns -> labels for columns
Returns wwo-dimensional size-mutable, potentially heterogeneous tabular data i.e. DataFrame
End of explanation
"""


dataFrame = pd.DataFrame(data = np.random.randint(1,51, (4,3)), index = ['row1', 'row2', 'row3', 'row4'], columns = ['col1', 'col2', 'col3'])
dataFrame

"""
Explanation: Selection and Indexing
End of explanation
"""


dataFrame['col1']

"""
Explanation: Selecting a single column
End of explanation
"""


dataFrame[['col1', 'col2']]

"""
Explanation: Selecting multiple columns
End of explanation
"""


dataFrame['newCol1'] = dataFrame['col3'] - dataFrame['col2']
dataFrame

dataFrame['newCol2'] = dataFrame['col1'] * dataFrame['col3']
dataFrame

"""
Explanation: Creation of new columns using arithmetic operators
End of explanation
"""


# axis -> 0 means that we are targeting the rows
# axis -> 1 means that we are targeting the columns
dataFrame.drop('newCol1', axis=1)

# we did not really drop the column
dataFrame

# Pandas saves us from accidentally dropping the columns
# Inorder to delete it
dataFrame.drop('newCol1', axis=1, inplace=True)
dataFrame

dataFrame.drop('newCol2', axis=1, inplace=True)
dataFrame

"""
Explanation: Removal of columns
End of explanation
"""


dataFrame.loc['row1']

"""
Explanation: Selecting a single Row
End of explanation
"""


dataFrame.loc[['row1', 'row2']]

"""
Explanation: Selecting multiple rows
End of explanation
"""


dataFrame.iloc[1]

"""
Explanation: Selecting rows based on their index number
End of explanation
"""


dataFrame.drop('row1', axis=0)

# again pandas didn't drop it completely
dataFrame

# we should use 'inplace' to drop the row
# dataFrame.drop('row1', axis=0, inplace=True)
# dataFrame

"""
Explanation: Removal of rows
End of explanation
"""


dataFrame.loc['row1', 'col2']

dataFrame.loc[['row1', 'row2', 'row3'],['col2', 'col3']]

dataFrame.iloc[0,1]

dataFrame.iloc[[0,1]]

"""
Explanation: Selecting both columns and rows
End of explanation
"""


dataFrame

dataFrame > 10

"""
Explanation: Conditional Selection
End of explanation
"""


dataFrame[dataFrame > 10]

"""
Explanation: Instead of getting true and false values, we can also get the actual value if the condition is satisfied
End of explanation
"""


dataFrame[dataFrame['col2'] > 10]

"""
Explanation: We can also target individual columns
End of explanation
"""


dataFrame[dataFrame['col2'] > 10]['col3']

dataFrame[dataFrame['col2'] > 10][['col1', 'col3']]

"""
Explanation: We can also output columns only that we want
End of explanation
"""


dataFrame[(dataFrame['col1'] > 10) & (dataFrame['col3'] > 10)]

"""
Explanation: If we want to apply conditional operators on multiple columns then we do so by
End of explanation
"""


