<jupyter_start><jupyter_text>Table of Content:=============================* [1. What is Python?](1)    * [1.1 History:](1.1)    * [1.2 Present:](1.2)    * [1.3 Future:](1.3)    * [1.4 Strengths of python:](1.4)    * [1.5 Weaknesses of python:](1.5)    * [1.6 Suitablity of python:](1.6)    * [1.7 Structure of python:](1.7)    * [1.8 Sample code in python:](1.8)* [2. DataTypes in python](2)    * [2.1 Strings](2.1)    * [2.2 Numeric Data Types](2.2)        * [2.2.1 Int Datatype](int)        * [2.2.2 Float Datatype](float)        * [2.2.3 Complex Datatype](complex)    * [2.3 Sequence Data Types](2.3)        * [2.3.1 List](2.3.1)        * [2.3.2 Tupple](2.3.2)    * [2.4 Dictionary](2.4)    * [2.5 Set](2.5)    * [2.6 Boolean](2.6)* [3. Comparison Operators](3)* [4. if-else Statement](4)    * [4.1 elif](4.1)* [5. Loops](5)    * [5.1 For Loop](5.1)    * [5.2 While Loop](5.2)* [6. Functions](6)    * [6.1 Built-in Functions](6.1)    * [6.2 User Define Functions](6.2)* [7. Lambda Functions](7)    * [7.1 Map()](7.1)    * [7.2 Filter()](7.2)    * [7.3 Reduce()](7.3)* [8. File I/O](8)    * [8.1 From Buffer](8.1)    * [8.2 From Text File](8.2)    * [8.3 Postioning](8.3)* [9. Introduction to Pandas](9)    * [9.1 History](9.1)    * [9.2 Features](9.1)    * [9.3 Purpose](9.3)* [10. Series in Pandas](10)    * [10.1  Defination](10.1)    * [10.2  Creating series from dict](10.2)    * [10.3  Creating series from ndarray](10.3)    * [10.4  Creating series from scaler values](10.4)    * [10.5  Accessing Series](10.5)    * [10.6  Performing operations on series](10.6)    * [10.7  Vectorized operations on series](10.7)    * [10.8  Naming of series](10.8)* [11. DataFrame in Pandas](11)    * [11.1 Defination ](11.1)    * [11.2 Creating DataFrame from List](11.2)    * [11.3 From dict of series](11.3)    * [11.4 From dict of ndarrays / lists](11.4)    * [11.5 From a list of dicts](11.5)    * [11.6 From a dict of tuples ](11.6)    * [11.7 Column selection, addition, deletion](11.7)    * [11.8 Indexing / Selection](11.8)    * [11.9 Data alignment and arithmetic](11.9)    * [11.10 Boolean Operators ](11.10)    * [11.11 Transposing](11.11)    * [12. Viewing Data in Pandas](12)    * [12.1 Head of Data](12.1)    * [12.2 tail of Data](12.2)    * [12.3 Display Index](12.3)    * [12.4 Display Column](12.4)    * [12.5 Printing Values](12.5)    * [12.6 Sorting by axis](12.6)    * [12.7 Sorting by values](12.7)    * [12.8 Describing DataFrame](12.8)    * [12.9 Selecting a Column](12.9)    * [12.10 Slicing](12.10)                         1. What is Python? ===============================**Python is a well-known programming language widely adopted by Programmers , Data Scientists , ML(Machine Learning)  and AI(Artificial Intelligence) Engineers ,these days. It is an interpreted high level language in which we can implement the concepts of object oriented programing as well. It is easy to learn and easy to implement language. Python supports modularity of the code which increase reusability of the code. Python has a large active community which is way there are thousands of  open source libraries  for python are avaible on the internet to facilitate the coders. As there is no compilation in python so debugging of code is comparitively easy here.** 1.1 History: ==============**The project python was found in 1980s and in around 1989  <NAME>  started implementing it. Later on in 1991 published the very first code with version (0.9.0). With the passage of time a lot of version was released but the revolutionary one was  python 3.0  (released on December 3, 2008). This was the moment when python start getting fame and adoption in the world of computer science** 1.2 Present: =================**At this time, the latest version of python is (3.9.2). Currently python is widely being used in:**-  Machine Learning -  Deep Learning -  Artificial Inteligence -  Natural Language Processing -  Web Development -  Data Analysis and Processing -  Big Data -  Cloud Computing -  Game Development **So we can say that among all programing languages python has the largest scope of implementation** 1.3 Future: ===============    **As we know the that future of Computer Science is strongly couppled with AI and ML and python has a great significance for both thsee domains so we can deduct that the   Python is the language of future  . If we observe the Statistics of based on google searches we'll find out that the python is at the top and its popularity is increasing at the rate of 4.1% every year.** 1.4 Strengths of python: ================================-  Simplicity:  **The syntax of python is simple and it is quite easy for developers to switch on it.**-  Supports Multiple Programming Paradigms:  **It has full support of object oriented and structured programing.**-  Liberaries:  **A large no of robust standard libararies are avaible for python.**-  Open source frameworks and tools :  **we have different tools avaiable for different needs. e.g. Django, Flask and etc.** 1.5 Weaknesses of python: ==================================-  Speed:  **As python is an interpreter so it is compartively slower than C and C++ which use compilers as translators.**-  Inter version incompatibility:  **Devlepers might face problem if some of the module are written in python 2.0 and others are in python 3.0**-  Under developed database access layer:  **Database access layer of python is not as good as JDBC(Java Database Connectivity) and ODBC(Open Database Connectivity). It is underdeveloped and not often used at enterprise level**-  Not native to mobile :  **When it comes to mobiles python is not a good choice for development as it is not native to mobile environment. Neither Android nor iOS supports python as an official programing language for mobile development.** 1.6 Suitablity of python: ==================================**Talking about the current era the scale of suitablity of python is huge. you can use it for**-  Web Development -  Game Development -  Machine Learning and Artificial Intelligence -  Data Science and Data Visualization -  Desktop GUI -  Web Scraping Applications -  Business Applications -  Audio and Video Applications -  CAD Applications -  Embedded Applications  1.7 Structure of python: ===================================    **Python is an interpreted language and the structure of python code is simpler than many other programming languages. It uses the concepts of indentation which makes the code written in python highly maitainable and readable** 1.8 Sample code in python: ====================================<jupyter_code>#here's the code check even and odd in python
a=1
print("Output:")
if(a%2):
    print("The number is odd")
else:
    print("The number is even")<jupyter_output>Output:
The number is odd<jupyter_text>2. DataTypes in Python: =====================================**Unlike other programming languages in python we do not mention the datatypes before declaring a variable it rather is defined when we assign value to it on the basis of the value which is being assigned. It could be a string, int, float, complex, boolean, tuple and etc.** 2.1 Strings: =============== Defination:============    **In python strings are the datatypes which contains  text   elclosed by either ' '(single qoutes) or " "(double quotes).** Purpose:=========**The purpose of string is simply to store text type data. A string could consist of single charecter like "a" and many charecters like "apple". Besides a string might be without spaces like "mohsin" and it might contains blank spaces as well like "mohsin is a student"**     Importance:=============**String is an important datatype because it allows us to store text in simplest possible manner. If we don't have strings dealing with textual data would be quite tricky.**     Application:=============    **we use strings when we need to:***  Send textual data to program  *  Recive textual data from program and display to user.      Strength:=============    **In Python strings are actually arrays of bytes you can access a particular byte using [index].**     Weakness:=============    **In python if we have a sigle charecter we have to make a string for that like charecter="a" as python do not have any datatype dealing with single charecters.**  Example 1 Defining strings: ==============================     Code:<jupyter_code>name="Mohsin"
print("Output:")
print(name)<jupyter_output>Output:
Mohsin<jupyter_text>Example 2 Concatinating strings:====================================     Code:<jupyter_code>word1="hello"
word2="world"
sentence= word1+" "+word2
print("Output:")
print(sentence)<jupyter_output>Output:
hello world<jupyter_text>Example 3 Spliting the strings:================================== Code:<jupyter_code>sentence= "Pakistan is my homeland"
print("Output:")
print(sentence.split(" ")) 
#splitting the whole sentence into substrings after every blank space<jupyter_output>Output:
['Pakistan', 'is', 'my', 'homeland']<jupyter_text>Example 4 Slicing strings:===============================     Code:<jupyter_code>sentence= "Pakistan Zindabad"
print("Output:")
print(sentence[0:3])<jupyter_output>Output:
Pak<jupyter_text>Example 5 Insert numbers into strings:==========================================    Code:<jupyter_code>amount=500
print("Output:")
print(f"the amount in your bank is {amount}")<jupyter_output>Output:
the amount in your bank is 500<jupyter_text>Example 6 Replacing the strings:====================================    Code:<jupyter_code>sentence= "hello world"
print("Output:")
print(sentence.replace("h","H"))<jupyter_output>Output:
Hello world<jupyter_text>2.2 Numeric Data Types: ================================ Defination:============    In Python we have 3 types of numeric datatypes -  int:  deals with integers (negative and positive)-  float:  deals with floating numbers (negative and positive) -  complex:  deals with imagnary/complex numbers Purpose:==========**Purpose of having 3 different numeric datatypes in python we need to deal with differnt natures of numeric data and it is not possible for us to deal with all those data using a single type**     Importance:============    **These datatypes are important as numbers plays a big role in life and while developing most of solutions for a problem related to real life numbers are involved in it as foundational blocks.**     Application:============    **We use numeric datatypes when we are dealing with calculations and it makes scope of these datatypes quite huge as calculations are involved everywhere.**     Strength:============     Here are few plus points about Numeric datatypes in python:* In int type we can have (positive and negative) numbers of ulimited length.  * We can deal with scientfic notation numbers using float type.* We have a separate data type "complex" for dealing with imaginary numbers.   * These numeric datatypes are inter convertable, means that you can change int to float and vice versa.             Weakness:============    **As python is dynamically-typed language the others could get confused when reading your code.**         Example 1: Class of int type ===================================<jupyter_code>num1= 5
num2=46456460404
num3=-155464564
print("Output:")
print("datatype of num1 =",type(num1))
print("datatype of num2 =",type(num2))
print("datatype of num3 =",type(num3))<jupyter_output>Output:
datatype of num1 = <class 'int'>
datatype of num2 = <class 'int'>
datatype of num3 = <class 'int'><jupyter_text>Example 2:   Prefixing Integers===================================     we can represent integers in python in the form of binary, octal and hexa decimal by applying prefexes.-  0b for binary -  0o for octal  -  0x for hexa decimal<jupyter_code>#decimal number 
decimal_number=5 
#binnary number 
binary_number=0b111001
#octal number
octal_number= 0o57
#hexadecimal number
hex_number=0x10
print("Output:")
print("Decimal number= ", decimal_number)
print("0b111001 in decimal = ", binary_number)
print("0o57 in decimal = ", octal_number)
print("0x10 in decimal = ", hex_number)<jupyter_output>Output:
Decimal number=  5
0b111001 in decimal =  57
0o57 in decimal =  47
0x10 in decimal =  16<jupyter_text>Example 3: Class of float type ===================================<jupyter_code>float1=5.04
float2=-455.464
float3= 5/7
float4= float('-infinity')#float can store -ve and positive infinity
float5= float('nan') #it also stores nan=not any number format
float6= float(3e-5)#it also stores exponential numbers.

print("Output:")
print("datatype of float1 =",type(float1))
print("datatype of float2 =",type(float2))
print("datatype of float3 =",type(float3))
print("datatype of float4 =",type(float4))
print("datatype of float5 =",type(float5))
print("datatype of float6 =",type(float6))<jupyter_output>Output:
datatype of float1 = <class 'float'>
datatype of float2 = <class 'float'>
datatype of float3 = <class 'float'>
datatype of float4 = <class 'float'>
datatype of float5 = <class 'float'>
datatype of float6 = <class 'float'><jupyter_text>Example 4: Complex Numbers =================================<jupyter_code>#in complex datatype we use j to represent imagnary part

complex_no1= 5+2j
complex_no2= 3j
complex_no3= 5-2j
print("Output:")
print("type of complex_no1=",type(complex_no1))
print("type of complex_no2=",type(complex_no2))
print("type of complex_no3=",type(complex_no3))<jupyter_output>Output:
type of complex_no1= <class 'complex'>
type of complex_no2= <class 'complex'>
type of complex_no3= <class 'complex'><jupyter_text>Example 5: Type conversions=================================<jupyter_code>int_num = 10   # int
float_num = 3.14  # float
complex_num = 5j   # complex

#convert from int to float:
converted_float = float(int_num)

#convert from float to int:
converted_int = int(float_num)

#convert from int to complex:
converted_complex = complex(complex_num)
print("Output:")
print(converted_float,type(converted_float))
print(converted_int,type(converted_int))
print(converted_complex,type(converted_complex))<jupyter_output>Output:
10.0 <class 'float'>
3 <class 'int'>
5j <class 'complex'><jupyter_text>2.3 Sequence Data Types: =================================== Defination:=============    Sequence datatypes allows us to efficently store multiple values in an organized manner. In python we have these sequence datatypes. -  List  -  Tupple  -  Range -  Strings (discuessed earlier) Go to Strings     2.2.1 List: =============      Defination:=============     In python List is a datatype in which we can store multiple items. In list we have multiple elements seprated by commas. List is one of the widely used datatype of in python when we are dealing with collections. Strength:=============    - The elements of lists are ordered- The elements of lists can be duplicate- The elements of lists are changeable- It use contigous memory block so the indexing is fast here      Suitability:=============    - You need an mutable collection you prefer to use a list     Example1: Decleration and Indexing=======================================    Here you can see how to decleare a list and how indexing works in a list.<jupyter_code>people= ["teacher", "student", "doctor", "engineer"]
print("Output:")
print(type(people))
print(people)
print("index[0]",people[0])
print("index[1]",people[1])
print("index[2]",people[2])
print("index[3]",people[3])<jupyter_output>Output:
<class 'list'>
['teacher', 'student', 'doctor', 'engineer']
index[0] teacher
index[1] student
index[2] doctor
index[3] engineer<jupyter_text>Example2: Mutablity=====================    **here you will see that how can we make changes in a list**<jupyter_code>people= ["teacher", "student", "doctor", "engineer"]
print("Output:")
print("List before changes:",people)
print("index[0]",people[0])
print("index[1]",people[1])
print("index[2]",people[2])
print("index[3]",people[3])
#here we are going to make chagnes in list
people[2]= "lawyer"
people[3]= "painter"
print("List after changes:",people)
print("index[0]",people[0])
print("index[1]",people[1])
print("index[2]",people[2])
print("index[3]",people[3])<jupyter_output>Output:
List before changes: ['teacher', 'student', 'doctor', 'engineer']
index[0] teacher
index[1] student
index[2] doctor
index[3] engineer
List after changes: ['teacher', 'student', 'lawyer', 'painter']
index[0] teacher
index[1] student
index[2] lawyer
index[3] painter<jupyter_text>Example3: Finding length of List==================================<jupyter_code>people= ["teacher", "student", "doctor", "engineer"]
print("Output:")
print("Lenght of list of people=", len(people))<jupyter_output>Output:
Lenght of list of people= 4<jupyter_text>Example4: Concatination in List==================================<jupyter_code>fruits=["apple", "grapes", "oranges"]
flowers=["rose", "lilly","sunflower"]
concatinated_list= fruits+flowers
print("Output:")
print("list of after concatination is ", concatinated_list)<jupyter_output>Output:
list of after concatination is  ['apple', 'grapes', 'oranges', 'rose', 'lilly', 'sunflower']<jupyter_text>Example5: Slicing in List=============================<jupyter_code>people= ["teacher", "student", "doctor", "engineer"]
#slicing from left side
print(people[1:])
print(people[2:])
print(people[3:])
print(people[4:])<jupyter_output>['student', 'doctor', 'engineer']
['doctor', 'engineer']
['engineer']
[]<jupyter_text>Example6: Reversing a list===============================<jupyter_code>people= ["teacher", "student", "doctor", "engineer"]
fruits=["apple", "grapes", "oranges"]
flowers=["rose", "lilly","sunflower"]
print("Output:")
print("Lists befor reversing are")
print(people)
print(fruits)
print(flowers)

people.reverse()
fruits.reverse()
flowers.reverse()

print("\nLists after reversing are:")
print(people)
print(fruits)
print(flowers)<jupyter_output>Output:
Lists befor reversing are
['teacher', 'student', 'doctor', 'engineer']
['apple', 'grapes', 'oranges']
['rose', 'lilly', 'sunflower']

Lists after reversing are:
['engineer', 'doctor', 'student', 'teacher']
['oranges', 'grapes', 'apple']
['sunflower', 'lilly', 'rose']<jupyter_text>2.3.2 Tuple: ============ Defination:===========     Tuple in python is a data type which is used to store collections of data. Unlike list, it is not enclosed by [ ] by square brackets but by () parenthesis. Strength:=========    - The elements of lists are ordered- The elements of lists can be duplicate- The stored elements are hetrogenous. Weakness:=========    - The stored elements cannot be changed as tuple is immutable.     Suitability:==========    - Tuples are by convention used when we need to store hetrogenous elements.     Example1: Decleration of tuple and Accessing ==================================================    Let's see how can we declear a tuple<jupyter_code>food= ("biryani", "qorma","pulao")
print("Output:")
print(food)
print(type(food))
print("index[0]=",food[0])
print("index[1]=",food[1])
print("index[2]=",food[2])

#immutable this is not allowed in tuple (you can uncomment and check)
#food[0]="nihari"<jupyter_output>Output:
('biryani', 'qorma', 'pulao')
<class 'tuple'>
index[0]= biryani
index[1]= qorma
index[2]= pulao<jupyter_text>Example2: Finding length of a tuple=======================================<jupyter_code>food= ("biryani", "qorma","pulao")
print("Length=",len(food))<jupyter_output>Length= 3<jupyter_text>Example3: Converting a list to tuple<jupyter_code>list1= ["a","b","c"]
print("Output:")
print("type of list1",type(list1))
tuple1= tuple(list1)
print("type of tuple 1:", type(tuple1))<jupyter_output>Output:
type of list1 <class 'list'>
type of tuple 1: <class 'tuple'><jupyter_text>Example4: Concatinating tuples=======================================<jupyter_code>food= ("biryani", "qorma","pulao")
fast_food=("burger","shawarma","pizza")
concatinating_tuple=food+fast_food
print("Output:")
print(concatinating_tuple)<jupyter_output>Output:
('biryani', 'qorma', 'pulao', 'burger', 'shawarma', 'pizza')<jupyter_text>Example5: Slicing in the tuple=======================================<jupyter_code>food= ("biryani", "qorma","pulao")
print("Output:")
print(food[1:])
print(food[2:])
print(food[3:])<jupyter_output>Output:
('qorma', 'pulao')
('pulao',)
()<jupyter_text>2.4 Dictionary: =================== Defination:=============    In python dictionary is an ordered set of key value pair   (As of python 3.7 dictionary is ordered)  . It is similer to a hash table where each key hold a specific value:-  Key:  It could be any primitive datatype in python.-  Value:  It is an aribitrary python object.     Strength: =============    - Using python dictionaries we can store data in more descriptive from.- We can use it as a hash table in python. Weaknesses:=============    - Dictionaries used to be unordered (but as of python 3.7 this weakness is removed)- Compared to other data structures dictionary consumes more storage.     Suitability:=============    - Are used when we have a data in which we need to map keys against some data- Is used in graphs for making adjustcency lists.     Example1: Initializing a dictionary====================================    Here you can see how to decleare a dictionary and extracting values using indexs.<jupyter_code>students={1:"mohsin",
          2: "gama",
          3:"ghazi",
          4:"nomi"}
print("Type:",type(students))
print(students)
#here indexing begins with 1
print(students[1])
print(students[2])
print(students[3])
print(students[4])<jupyter_output>Type: <class 'dict'>
{1: 'mohsin', 2: 'gama', 3: 'ghazi', 4: 'nomi'}
mohsin
gama
ghazi
nomi<jupyter_text>Example2: Deleting a key-value pair in dictionary=====================================================<jupyter_code>students={1:"mohsin",
          2: "gama",
          3:"ghazi",
          4:"nomi"}
print("Before Deletion:\n",students)
del(students[3])
print("After Deletion:\n",students)<jupyter_output>Before Deletion:
 {1: 'mohsin', 2: 'gama', 3: 'ghazi', 4: 'nomi'}
After Deletion:
 {1: 'mohsin', 2: 'gama', 4: 'nomi'}<jupyter_text>Example3: Poping item from a Dictionary ============================================<jupyter_code>students={1:"mohsin",
          2: "gama",
          3:"ghazi",
          4:"nomi"}
print("Output:")
print("Before Deletion:\n",students)
result= students.pop(4)
#here value 4 is poped out and stored in the variable result
print("After Deletion:\n",students)
print("\nresult=",result)<jupyter_output>Output:
Before Deletion:
 {1: 'mohsin', 2: 'gama', 3: 'ghazi', 4: 'nomi'}
After Deletion:
 {1: 'mohsin', 2: 'gama', 3: 'ghazi'}

result= nomi<jupyter_text>Example4: Clearing all items in the dictionary using clear()================================================================<jupyter_code>students={1:"mohsin",
          2: "gama",
          3:"ghazi",
          4:"nomi"}
print("Output:")
print("Before Clearing:\n",students)
students.clear()
#we'll have a empty dictionary now
print("After Clearing:\n",students)<jupyter_output>Output:
Before Clearing:
 {1: 'mohsin', 2: 'gama', 3: 'ghazi', 4: 'nomi'}
After Clearing:
 {}<jupyter_text>2.5 Set: ============= Defination:=============    In python we use sets for representing   collection of unordered and unindexed data  . Every element in the set is unique which means it cannot contain duplicate items. Set itself is mutable but the stored elements are immutable. Values in set are seprated by comma.      Strength: =============    - Because sets cannot have multiple occurrences of the same element, it makes sets highly useful to efficiently remove duplicate values from a list or tuple and to perform common math operations like unions and intersections  Weakness: =============    - Its not being ordred and indexed make accessibility less efficient here.      Suitability:=============- Sets are suitable to use when we are dealing with membership testing and eliminating duplicate entries.      Example1: Initializing a set==============================<jupyter_code>stationary_set= {"pen","pencil","scale"}
print("Output:")
print(stationary_set)
print(type(stationary_set))<jupyter_output>Output:
{'scale', 'pencil', 'pen'}
<class 'set'><jupyter_text>Example2: Adding value in the set=====================================<jupyter_code>stationary_set= {"pen","pencil","scale"}
print("Output:")
print("Before adding value")
print(stationary_set)
stationary_set.add("eraser")
print("After adding value")
print(stationary_set)<jupyter_output>Output:
Before adding value
{'scale', 'pencil', 'pen'}
After adding value
{'scale', 'pencil', 'eraser', 'pen'}<jupyter_text>Example3: Removing values from the set=============================================<jupyter_code>stationary_set= {"pen","pencil","scale"}
print("Output:")
print("Before removing value")
print(stationary_set)
stationary_set.discard("pen")
print("After after value")
print(stationary_set)<jupyter_output>Output:
Before removing value
{'scale', 'pencil', 'pen'}
After after value
{'scale', 'pencil'}<jupyter_text>Example4: Union of sets==========================<jupyter_code>stationary_set_mohsin = {"pen","pencil","scale"}
stationary_set_tiyab = {"eraser","pencil","marker"}
union_set= stationary_set_mohsin|stationary_set_tiyab
print("Output:")
print(union_set)<jupyter_output>Output:
{'scale', 'pencil', 'marker', 'eraser', 'pen'}<jupyter_text>Example5: Intersection of sets================================<jupyter_code>stationary_set_mohsin = {"pen","pencil","scale"}
stationary_set_tiyab = {"eraser","pencil","marker"}
intersection_set= stationary_set_mohsin & stationary_set_tiyab
print("Output:")
print(intersection_set)<jupyter_output>Output:
{'pencil'}<jupyter_text>2.6 Boolean: ================ Defination:=============    In python boolean is a datatype which contains 2 possible values:-  True  it will not accept "true" as bool.-  False  it will not accept "false" as bool.    this datatype belongs to bool class of python. bool is not the keyword in the python but it is advised not to use it for naming variables. Strength: ===========    - It is quite useful for storing results of logical expressions.     Suitability:==============    - It is useful when we are dealing with logical expressions and decesion making.     Example1: Initializing boolean variable=========================================<jupyter_code>boolean_variable= True
print(type(boolean_variable))<jupyter_output><class 'bool'><jupyter_text>Example2: Storing result of logical expressions:=====================================================<jupyter_code>number1= 10
number2= 20
result= number1>number2
print("Output:")
# 10>20 so it should return False
print("Result=", result)
result= number2>number1
# 20>10 so it should return True
print("Result=", result)<jupyter_output>Output:
Result= False
Result= True<jupyter_text>Example3: Boolean Operators ================================<jupyter_code>#and operator
boolean_variable1= True
boolean_variable2= False
result= boolean_variable1 and boolean_variable2
print("Output:")
print("Result= ", result)

#or operator
boolean_variable1= True
boolean_variable2= False
result= boolean_variable1 or boolean_variable2
print("Result= ", result)

#and operator
boolean_variable1= True
result= not boolean_variable1
print("Result= ", result)<jupyter_output>Output:
Result=  False
Result=  True
Result=  False<jupyter_text>3 Comparison Operators: ================================== Defination:=============     As the name suggest comparison operators are used to compare to operands. In python we have followin comparison operators-  Equal   (==)-  Not Equal    (!=)-  Greater than   (<)-  Less than    (>)-  Greater than or equal   (<=)-  Less than or equal    (>=)when we perform these operators we get a boolean type varible. Example1: Perfroming Comparison Operators:==================================================<jupyter_code>fruit1="Apples"
fruit2="Grapes"
## equal to operator
result=fruit1==fruit2
print("Output:")
print("Result=",result)

## not equal to operator
result=fruit1!=fruit2
print("Result=",result)

# greater than
number1= 10
number2= 20
result= number1>number2
print("Result=",result)

# Less than
number1= 10
number2= 20
result= number1<number2
print("Result=",result)

#Less than or equal 
number1= 10
number2= 10
result= number1>=number2
print("Result=",result)

# Greater than or equal
number1= 10
number2= 10
result= number1<=number2
print("Result=",result)<jupyter_output>Output:
Result= False
Result= True
Result= False
Result= True
Result= True
Result= True<jupyter_text>4 if-else : ============    Like in other programming lanuages if-else statemens are used for  conditional transfer of control in the program    . if statement evaluates a logical expression and decide wether to transfered control to if-block or rest of the program wheares, in case of else the control is either transfered to if block or else block.     Strength: ===========    - Helps in redirecting control during the execution of program.     Weakness:==============    - Intrupt the normal flow of execution of the program.     Suitability:==============    - if-else is suitable to use when we are dealing with multi-outcome problems.        Example1: Classifying Odd and Even number:==================================================<jupyter_code>num=10
print("Output:")
if(num%2):
    print("number is odd");
else:
    print("number is even")<jupyter_output>Output:
number is even<jupyter_text>Example2: Finding Largest Number:======================================<jupyter_code>num1= 10
num2= 20
print("Output:")
if(num1>num2):
    print(f"{num1} is greater than {num2}")
else:
    print(f"{num2} is greater than {num1}")<jupyter_output>Output:
20 is greater than 10<jupyter_text>4.1 elif : =======    elif is used when we want control to be transfered to more than 2 blocks. We can use multiple elif blocks and if condtion of certain blocks is fulfiled it is executed. If no condtion is fulfiled the control is transfered to else block.  Example3: Finding Grade of Student :================================================<jupyter_code>marks=50
print("Output:")
if(marks>=90):
    print("your grade is A")
elif(marks>=70 and marks<90):
    print("your grade is B")
elif(marks>=50 and marks<70):
    print("your grade is C")
else:
    print("your grade is F")<jupyter_output>Output:
your grade is C<jupyter_text>Example4: Ternary Operators (Short hand if-else) :=====================================================<jupyter_code>num1 = 20
num2 = 30
print("Output:")
print(f"{num1} is greater") if num1 > num2 else print(f"{num2} is greater")<jupyter_output>Output:
30 is greater<jupyter_text>5  Loops: ============    Loops play an important role in every programming language. We use loop when we need to perform   repitative tasks  . In python we have two types of loops.    -  For Loop:  -  While Loop:       Strength: ===========    - Our program do not have repitative line of codes when we use loops.     Weakness:==============    - Although loops makes our work easy but they might increase the complexity of the program.      Suitability:==============    - When we are dealing with a problem in which we need to perform repitative tasks.     5.1  For Loop: =================    Another name of this loop is counter loop. We use for loop to iterating through a sequence. It also is used when we need to iterate a code block for the   fixed number of times  . Example1: Looping through a list:======================================<jupyter_code>people= ["teacher", "student", "doctor", "engineer"]
print("Output:")
for i in people:
    print(i)
#on every iteration i pick a value from the list of people.<jupyter_output>Output:
teacher
student
doctor
engineer<jupyter_text>Example2: Looping through a String:==============================<jupyter_code>name="Mohsin"
print("Output:")
for i in name:
    print(i)<jupyter_output>Output:
M
o
h
s
i
n<jupyter_text>Example3: range() function:========================    The range() function returns a sequence of numbers, starting from 0 by default, and increments by 1 (by default), and ends at a specified number. we can replace the starting, ending and increament values which are set by default.<jupyter_code>print("Output:")
for num in range (6):
    print("Pakistan")<jupyter_output>Output:
Pakistan
Pakistan
Pakistan
Pakistan
Pakistan
Pakistan<jupyter_text>Example4: Nested for loop:==============================    we can use another for in loop inside  a for loop for performing multiple iteration within an iteration. here is an example of nested for loop.<jupyter_code>#printing triangle of astericks
print("Output:")
for i in range(0, 5):
    for j in range(0, i+1):
        print("* ",end="")
    print("\r")<jupyter_output>Output:
* 
* * 
* * * 
* * * * 
* * * * *<jupyter_text>5.2  While Loop: =====================    Another name of this loop is conditional loop. This loop keep on iterating as long as   a certain condtion is true  .  Example5 : Condtional Loop:=================================<jupyter_code>#video game goes on until you have life greater than 0 it works on while loop:
life=10
while(life>0):
    print(f"You have {life} lives remaining")
    life=life-1
else:
    print("Game over")<jupyter_output>You have 10 lives remaining
You have 9 lives remaining
You have 8 lives remaining
You have 7 lives remaining
You have 6 lives remaining
You have 5 lives remaining
You have 4 lives remaining
You have 3 lives remaining
You have 2 lives remaining
You have 1 lives remaining
Game over<jupyter_text>6  Function : ============    A function is defined as a block of code which only runs when we call it. We can pass data, known as parameters, into a function. A function can also return data as a result. In python we have two types of functions.    -  Built-in Functions:  -  User define Funcitons:       Strength: ===========    - Increase the reusability of code.- Gives modularity to our program.- Our code becomes organized if we work in modules.     Weakness:==============    - Call to a function and return statement takes couple of extra machine instructions which might increase the time complexity a littl bit     Suitability:==============- we use functions when we need to use same block of code at multiple places.     6.1  Built-in Functions : As the name suggest these are by defualt functions in the python we do not need to import them or manually write them program.     Example1 : Built-in Functions (Numeric):============================================<jupyter_code>number=10
print("Output:")
#bin() is a built in function returns the binary value of the number
print("bin(10)= ",bin(10))
#abs() is a built in function return absolute value of a number
print("abs(10)= ",abs(10))<jupyter_output>Output:
bin(10)=  0b1010
abs(10)=  10<jupyter_text>Example2 : Built-in Functions (Objects):============================================<jupyter_code>name= "Mohsin"
#type function tells the type of object
print("type(name)= ",type(name))
#len is a built in function returns the length of object
print("len(name)= ",len(name))<jupyter_output>type(name)=  <class 'str'>
len(name)=  6<jupyter_text>6.2  User-defined Functions : These functions are defined by user and and later called at different places in the program. we can define following type of user-defined functions.    -  Parametered Functions:  -  Non Parametered Functions:  -  Void Functions:  -  Value-returning Functions:   Example3 : Parametered Functions=======================================    In these functions we pass some values which can be used in the the block of the function.<jupyter_code>#defining a function which is taking a string a parameter
def print_string (string):
  print(string)

string= "<NAME>"
#calling a function and passing it a parameter "<NAME>"
print("Output:")
print_string(string)<jupyter_output>Output:
<NAME><jupyter_text>Example4 : Non Parametered Functions===========================================     In these functions we do not pass any argument or parameter.<jupyter_code>#defining a function which is taking no parameter
def print_string ():
  print("<NAME>")

#calling a function with no parameter/argument
print("Output:")
print_string()<jupyter_output>Output:
<NAME><jupyter_text>Example5 : Void Function===========================    These functions do not retrun anything.<jupyter_code># A function retrun no value
def print_string ():
    for i in range(10):
        print(f"{i+1} <NAME>")
print("Output:")
print_string()<jupyter_output>Output:
1 <NAME>
2 <NAME>
3 Mohsin Nazakat
4 Mohsin Nazakat
5 Mohsin Nazakat
6 Mohsin Nazakat
7 Mohsin Nazakat
8 <NAME>
9 <NAME>
10 <NAME><jupyter_text>Example6 : Value-Returning Function===========================    These functions return some value.<jupyter_code>#this function is returning some of 2 numbers
def add(a,b):
    return a+b
print("Output:")
print("Sum=",add(5,2))<jupyter_output>Output:
Sum= 7<jupyter_text>7  Lambda Function : ============================    These are the function that can be    created at runtime   using the construct called "lambda". It consist of expressions which is executed at the runtime. These functions do not have a name and return nothing. There are fews functions which takes lambda(annoymous) function as an arguement, Some of them are:    -   map()  -   filter()  -   reduce()       Strength: ===========    - Lambad function enables us to pass function as an arguement.- These functions enable us to bring abstraction to the block of code. Weakness:==============    - These functions just executes expressions and do not return anything.     Suitability:==============- When we need to pass function as an aurgument to some other function, we use lambda functions.         Example1 : Defining Lambda Function:============================================<jupyter_code>#defining a lambda function:
half_number= lambda num: num/2
print("Output:")
half_number(10)<jupyter_output>Output:<jupyter_text>Example2 : Using lambda funtion for getting Multipliers:=============================================================<jupyter_code>#fuction is multiplying argument with an unknown number
def multiplier(n):
  return lambda a : a * n

#here we set multiplier to 5 and now we have a lambda function which generates multiple of 5
my_multiplier = multiplier(5)
print("Output:")
for i in range (10):
    print(f"{5} * {i+1} = ",my_multiplier(i+1))<jupyter_output>Output:
5 * 1 =  5
5 * 2 =  10
5 * 3 =  15
5 * 4 =  20
5 * 5 =  25
5 * 6 =  30
5 * 7 =  35
5 * 8 =  40
5 * 9 =  45
5 * 10 =  50<jupyter_text>7.1  map () : ==============    Map() function is used with two arguments. Just like:   r = map(func, seq)  . It applies the function func to all the elements of the sequence seq. After applying func on seq it generates and return an updated list.-   func:   it is function which is to be performed.-   seq:   This is the sequence on which the function is to be performed.     Example3 : Multiplying list with a number n using map():===========================================================<jupyter_code>my_list= [10,20,30,40,50]
updated_list= list(map(lambda num: num*2,my_list))
print("Output:")
print("Orignal_list=" , my_list)
print("Updated_list=" , updated_list)<jupyter_output>Output:
Orignal_list= [10, 20, 30, 40, 50]
Updated_list= [20, 40, 60, 80, 100]<jupyter_text>Example4 : Concatinating a word using map():====================================================<jupyter_code>my_list= ["Mohsin", "<NAME>", "Noman", "AbuBakar"]
updated_list= list(map(lambda strr: strr+" Student",my_list))
print("Output:")
print("Orignal_list=" , my_list)
print("Updated_list=" , updated_list)<jupyter_output>Output:
Orignal_list= ['Mohsin', '<NAME>', 'Noman', 'AbuBakar']
Updated_list= ['Mohsin Student', 'Ghulam Rasool Student', 'Noman Student', 'AbuBakar Student']<jupyter_text>7.2 filter () : ==============    As the name suggest filter function helps us filtering elements from a list. it takes two arguements.    -   func:   It is the condition on which the elements are filtered out.-   seq:   This is a given sequence.     Example5 : Filtering odd numbers using filter():====================================================<jupyter_code>my_list= [1,2,3,4,5,6,7,8,9,10]
odd_list= list(filter(lambda num: num%2 ,my_list))
print("Output:")
print("Orignal_list=" , my_list)
print("List of odd numbers=" , odd_list)<jupyter_output>Output:
Orignal_list= [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
List of odd numbers= [1, 3, 5, 7, 9]<jupyter_text>Example5 : Fitering marks less than 50:==================================================<jupyter_code>my_list= [90,80,30,15,50,45,100,70,35]
updated_list= list(filter(lambda num: num<50 ,my_list))
print("Output:")
print("Orignal_list=" , my_list)
print("Updated_list=" , updated_list)<jupyter_output>Output:
Orignal_list= [90, 80, 30, 15, 50, 45, 100, 70, 35]
Updated_list= [30, 15, 45, 35]<jupyter_text>7.3 reduce () : ==============The reduce() function in Python takes in a function and a list as an argument. The function is called with a lambda function and an iterable and a new reduced result is returned. This performs a  repetitive operation over the pairs of the iterable   . The reduce() function belongs to the  functools module. It takes two arguements.    -   func:   An interable function which is to be performed.-   seq:   This is a given sequence.     Example6 : Finding sum of a series:========================================<jupyter_code>from functools import reduce
#importing reduce function from the the funtools libarary
my_list= [90,80,30,15,50,45,100,70,35]
sum_series = reduce((lambda x, y: x + y), my_list)
print ("Sum of series= ", sum_series)<jupyter_output>Sum of series=  515<jupyter_text>Example7 : Largest number in the series:============================================<jupyter_code>import functools 
lis = [90,80,30,15,50,45,100,70,35]
  
# using reduce to compute maximum element from list 
print ("The maximum element of the list is : ",end="") 
print (functools.reduce(lambda a,b : a if a > b else b,lis))<jupyter_output>The maximum element of the list is : 100<jupyter_text>8  File I/O : =================We usually take input from either:    -   Buffer/Keyboard:  -   File       8.1 From Buffer/ Keyboard: ==============================we simple use input() function which stores a single line stored in buffer in the form of a string. By default it returns string but we can typecaste it in other datatype as well     Example1 : Taking Input using keyboard / buffer:=====================================================<jupyter_code>name = input("Enter your name:")
print("Type: ",type(name))<jupyter_output>Enter your name:<NAME>
Type:  <class 'str'><jupyter_text>Example2 : Taking integer input:===================================<jupyter_code>#taking input and casting it to integer
num1= int (input("Enter first number "))
num2= int (input("Enter second number "))
result =num1+num2
print("Output:")
print(result)
print("type:",type(result))<jupyter_output>Enter first number 10
Enter second number 20
Output:
30
type: <class 'int'><jupyter_text>8.2 From File: ==============    In python we can read/write a file in following modes:    -   r   opens a file in read only mode.-   r+   opens a file read and write mode.-   w   opens a file in write mode only.-   a   opens a file in append mode-   a+   opens a file in append and read mode.     Example3 : Reading input from file:=====================================<jupyter_code>#createing a new file
f = open("mohsin.txt", "w")

#Inserting initial data in file
f = open("mohsin.txt", "r+")
f.write("Name: <NAME>\nReg no: FA18-BCS-052")
f.close()

#opening file in r+ mode
my_file = open("mohsin.txt", "r+")
text=my_file.read()
#reading text  from the file
print("Reading data from file----")
print("Output:")
print(text)
#closing the file
my_file.close()<jupyter_output>Reading data from file----
Output:
Name: <NAME>
Reg no: FA18-BCS-052<jupyter_text>Example4 : Writing data to file:================================<jupyter_code>#opening file in a+ mode
my_file = open("mohsin.txt", "a+")
text=my_file.write("\nDept: Computer Science ")
#closing the file
my_file.close()

my_file = open("mohsin.txt", "r+")
text=my_file.read()
#reading text  from the file
print("Reading data from file----")
print("Output:")
print(text)
#closing the file
my_file.close()<jupyter_output>Reading data from file----
Output:
Name: <NAME>
Reg no: FA18-BCS-052
Dept: Computer Science<jupyter_text>8.3 Positioning : ===================    While reading or writing a file we deal with   File handle which is like a cursor  . It defines from where the data has to be read or written in the file. We have following functions in python which deal with position of file handle:    -   tell()  : tells the current position of file handle.-   seek()  : change the current position of file handle.     Example5 : Checking Current Position:=========================================<jupyter_code>my_file= open("mohsin.txt","r+")
data= my_file.read(10)
print(data)
#checking postion of file handle
position= my_file.tell()
print("Position: ",position)<jupyter_output>Name: Mohs
Position:  10<jupyter_text>Example6 : Changing position of file handle:===============================================<jupyter_code>#Now setting file handle to begining again
position= my_file.seek(0,0)
print(my_file.read(10))
my_file.close()<jupyter_output>Name: Mohs<jupyter_text>9  Introduction to Pandas : ==================================In computer programming,  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. It is free software released under the three-clause BSD license         9.1 History : ==============    In 2008, pandas development began at AQR Capital Management. By the end of 2009 it had been open sourced. Since 2015, pandas is a NumFOCUS sponsored project.  Timeline:-   2008 : Development of pandas started-   2009  : pandas becomes open source-  2012  : First edition of Python for Data Analysis is published-   2015  : pandas becomes a NumFOCUS sponsored project-   2018  : First in-person core developer sprint     9.2 Features : ==============    - A fast and efficient   DataFrame   object for data manipulation with integrated indexing;- Tools for  reading and writing data   between in-memory data structures and different formats: CSV and text files, Microsoft Excel, SQL databases, and the fast HDF5 format;- Intelligent   data alignment   and integrated handling of missing data: gain automatic label-based alignment in computations and easily manipulate messy data into an orderly form;- Flexible   data alignment   and pivoting of data sets;- Intelligent   label-based slicing, fancy indexing  , and subsetting of large data sets;- Columns can be inserted and deleted from data structures for   size mutability   ;- Aggregating or transforming data with a powerful   group by    engine allowing split-apply-combine operations on data sets;- High performance    merging and joining   of data sets;-    Hierarchical axis indexing   provides an intuitive way of working with high-dimensional data in a lower-dimensional data structure;-   Time series -functionality: date range generation and frequency conversion, moving window statistics, date shifting and lagging. Even create domain-specific time offsets and join time series without losing data;-    Highly optimized for performance  , with critical code paths written in Cython or C.- Python with pandas is in use in a wide variety of   academic and commercial   domains, including Finance, Neuroscience, Economics, Statistics, Advertising, Web Analytics, and more.     9.3 Purpose : ==============    The purpose of Pandas is make data analytics and manipulation:- Accessible to everyone- Free for users to use and modify- Flexible- Powerful- Easy to use- Fast         10  Series in Pandas : ===============================    In python Series is a one-dimensional labeled array which can hold data of any type (integer, string, float, python objects, etc.). The axis labels are collectively called index.     10.1  Defination:==================    class pandas.Series (data=None, index=None, dtype=None, name=None, copy=False, fastpath=False) wheares,            -   data : Contains data stored in Series. If data is a dict, argument order is maintained.-   index : Values must be hashable and have the same length as data. Non-unique index values are allowed. Will default to RangeIndex (0, 1, 2, …, n) if not provided-   dtype : Data type for the output Series. If not specified, this will be inferred from data. -   name : The name to give to the Series.-   copy : Copy input data.    We can create series from     -   Array -   dict -   Scaler Value   10.2  Creating series from dict: ==================================<jupyter_code>import pandas as pd
my_dict = {'a': 1, 'b': 2, 'c': 3}
my_series = pd.Series(data=my_dict, index=['a', 'b', 'c'])
print(my_series)<jupyter_output>a    1
b    2
c    3
dtype: int64<jupyter_text>10.3  Creating series from ndarray: ====================================    - In case of   ndaray   index must be the same length as data.- if   no index   is passed by default it will be  set from [0,1....size -1]         When index is specified:=====================<jupyter_code>import numpy as np
#creating series with indexes.
my_series = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])
print (my_series)

#here are the index we assigned to series
my_series.index<jupyter_output><empty_output><jupyter_text>When index is not specified:========================<jupyter_code>pd.Series(np.random.randn(5))
#here you can see that, the index is starting from 0,1... size-1<jupyter_output><empty_output><jupyter_text>10.4  Creating series from scaler values: ==========================================<jupyter_code>pd.Series(1,  index=['a','b','c','d','e'])<jupyter_output><empty_output><jupyter_text>10.5  Accessing Series : ==========================================     By Index : ==========<jupyter_code>print("Output:")
print("Values in series:")
for i in my_series:
    print(i)
#or we can print it index-by-index
print("\nIndex-by-Index Values")
for i in range (my_series.size):
    print(f"index[{i}]", my_series[i])<jupyter_output>Output:
Values in series:
0.8526565292218028
-0.4459370591736289
-1.992237280562101
-1.883839682458662
-0.31708353340386874

Index-by-Index Values
index[0] 0.8526565292218028
index[1] -0.4459370591736289
index[2] -1.992237280562101
index[3] -1.883839682458662
index[4] -0.31708353340386874<jupyter_text>By Multiple Index : ===============<jupyter_code>#we can print selected indexes as well
print(my_series[[1,4,0]])<jupyter_output>b   -0.445937
e   -0.317084
a    0.852657
dtype: float64<jupyter_text>By Labeled Index : ================<jupyter_code>print(my_series['a'])
print(my_series['b'])
print(my_series['c'])
print(my_series['d'])
print(my_series['e'])

print(my_series[['a','b','c']])<jupyter_output>0.8526565292218028
-0.4459370591736289
-1.992237280562101
-1.883839682458662
-0.31708353340386874
a    0.852657
b   -0.445937
c   -1.992237
dtype: float64<jupyter_text>By get method : ================<jupyter_code>print(my_series.get('a'))
print(my_series.get('b'))
print(my_series.get('c'))
print(my_series.get('d'))
print(my_series.get('e'))<jupyter_output>0.8526565292218028
-0.4459370591736289
-1.992237280562101
-1.883839682458662
-0.31708353340386874<jupyter_text>10.6  Performing operations on series : ==========================================     Finding Median : ================<jupyter_code>print(my_series.median)<jupyter_output><bound method Series.median of a    0.852657
b   -0.445937
c   -1.992237
d   -1.883840
e   -0.317084
dtype: float64><jupyter_text>Finding exponent : ================<jupyter_code>np.exp(my_series)<jupyter_output><empty_output><jupyter_text>Poping elements from series : =========================<jupyter_code>my_series1 =pd.Series(1,  index=['a','b','c','d','e'])
print("Output:")
print("series before poping")
print(my_series1)
poped_value= my_series1.pop("b")

print("series after poping")
print(my_series1)
print("poped_value=", poped_value)<jupyter_output>Output:
series before poping
a    1
b    1
c    1
d    1
e    1
dtype: int64
series after poping
a    1
c    1
d    1
e    1
dtype: int64
poped_value= 1<jupyter_text>Shape and size of series series : ===========================<jupyter_code>print("Shape of series: ",my_series.shape)
print("Size of series: ", my_series.size)<jupyter_output>Shape of series:  (5,)
Size of series:  5<jupyter_text>10.7  Vectorized operations on series : ========================================     Addition of series: ================<jupyter_code>my_series3 =pd.Series(1,  index=['a','b','c','d','e'])
my_series4 =pd.Series(1,  index=['a','b','c','d','e'])
print("series3\n",my_series3)
print("series4\n",my_series4)

print("sum of series \n",my_series3+my_series4)

# following will add the data of respective values of indexes. For example, in given output, it is calculate
# d as:
# a = s['a'] + s['a']
# b = s['b'] + s['b']
# c = s['c'] + s['c']
# d = s['d'] + s['d']
# e = s['e'] + s['e']<jupyter_output>series3
 a    1
b    1
c    1
d    1
e    1
dtype: int64
series4
 a    1
b    1
c    1
d    1
e    1
dtype: int64
sum of series 
 a    2
b    2
c    2
d    2
e    2
dtype: int64<jupyter_text>Scaler multiplication of series: =========================<jupyter_code>print ("Output:")
print (my_series3*2)
#following will multiply the data of each values of indexes, with 2. For example, in given output, it is ca
#lculated as:
#a = s['a'] *2
#b = s['b'] *2
#c = s['c'] *2
#d = s['d'] *2
#e = s['e'] *2<jupyter_output>Output:
a    2
b    2
c    2
d    2
e    2
dtype: int64<jupyter_text>10.8  Naming of series : ==========================<jupyter_code>my_series3 =pd.Series(1,  index=['a','b','c','d','e'], name="random array")
print("Output:")
print("Name of series: ", my_series3.name)<jupyter_output>Output:
Name of series:  random array<jupyter_text>Renaming series: ===================<jupyter_code>my_series4 =my_series3.rename("super random array")
print("Output:")
print("Name of series:", my_series4.name)<jupyter_output>Output:
Name of series: super random array<jupyter_text>11  DataFrame in Pandas : ===============================    In Pandas DataFrame is In case of   two-dimensional size-mutable, potentially heterogeneous tabular data structure   with labeled axes (rows and columns).Three major components of DataFrames are:    -   Rows  -   Columns  -   Data           11.1  Defination: ==================    class pandas.DataFrame (data=None, index=None, columns=None, dtype=None, copy=False) wheares,            -   data : Dict can contain Series, arrays, constants, dataclass or list-like objects. If data is a dict, column order follows insertion-order.   Changed in version 0.25.0:    If data is a list of dicts, column order follows insertion-order        -   index : Index to use for resulting frame. Will default to RangeIndex if no indexing information part of input data and no index provided.-   column  : Column labels to use for resulting frame. Will default to RangeIndex (0, 1, 2, …, n) if no column labels are provided.-   dtype  : Data type to force. Only a single dtype is allowed. If None, infer.-   copy : Copy data from inputs. Only affects DataFrame / 2d ndarray input.    DataFrame accepts following kinds of inputs:-   Dict of 1D ndarrys,lists,dicts or Series  -   2D numpy.ndarray  -   Structured or record ndarray  -   A series  -   Another DataFrame   10.2  Creating DataFrame from List: ====================================== With no index and columns: ===============================<jupyter_code>my_list=['biryani', 'pulao', 'karahi','saji']
my_dataframe= pd.DataFrame(my_list)
print("Output:")
my_dataframe
#default  index and column are set<jupyter_output>Output:<jupyter_text>With provided index and columns: ===============================<jupyter_code>my_list=['biryani', 'pulao', 'karahi','saji']
my_dataframe= pd.DataFrame(my_list, index=['a','b','c','d'], columns=['Dish'])
print("Output:")
my_dataframe
#Given index and column are set<jupyter_output>Output:<jupyter_text>11.3  From dict of series: ============================- The result index will be the union of the indexes of the various Series.- If there are any nested dicts, these will be first converted to Series.- If no columns are passed, the columns will be the sorted list of dict keys. Index given Column not given: ==========================<jupyter_code>my_dict = {
 'one' : pd.Series([1., 2., 3.], index=['a', 'b', 'c']),
 'two' : pd.Series([1., 2., 3., 4.], index=['a', 'b', 'c', 'd'])
 }

my_dataFrame = pd.DataFrame(my_dict)
my_dataFrame

pd.DataFrame(my_dict, index=['d', 'b', 'a'])<jupyter_output><empty_output><jupyter_text>Index and Column Both given: ==========================<jupyter_code>pd.DataFrame(my_dict, index=['d', 'b', 'a'], columns=['two', 'three'])<jupyter_output><empty_output><jupyter_text>Information about columns: ==========================<jupyter_code>df.columns<jupyter_output><empty_output><jupyter_text>11.4  From dict of ndarrays / lists :  - The ndarrays must all be the same length.- If an index is passed, it must clearly also be the same length as the arrays.- If no index is passed, the result will be range(n), where n is the array length. Column and Index not given: ==========================<jupyter_code>my_dict = {
 'one' : [1., 2., 3., 4.],
 'two' : [4., 3., 2., 1.]
 }
pd.DataFrame(my_dict)
#As column is not given, so the key in sorted form will be used as column
#As index is not given so it'll range from 0 to size-1<jupyter_output><empty_output><jupyter_text>Applying indexes: ===============<jupyter_code>pd.DataFrame(my_dict, index=['a', 'b', 'c', 'd'])<jupyter_output><empty_output><jupyter_text>Applying Column Lables: =====================<jupyter_code>pd.DataFrame(my_dict, index=['a', 'b', 'c', 'd'], columns=['two', 'three'])
#as there is no recrod agains there it's giving NaN<jupyter_output><empty_output><jupyter_text>11.5  From a list of dicts  :<jupyter_code>my_list = [{'a': 1, 'b': 2}, {'a': 5, 'b': 10, 'c': 20}]
pd.DataFrame(my_list)<jupyter_output><empty_output><jupyter_text>Adding Indexes: ===============<jupyter_code>pd.DataFrame(my_list, index=['first', 'second'])<jupyter_output><empty_output><jupyter_text>Passing Column Labels: ======================<jupyter_code>pd.DataFrame(my_list, columns=['a', 'b'])<jupyter_output><empty_output><jupyter_text>11.6  From a dict of tuples  :  ===========================        You can automatically create a multi-indexed frame by passing a tuples dictionary<jupyter_code>pd.DataFrame({('a', 'b'): {('A', 'B'): 1, ('A', 'C'): 2},
 ('a', 'a'): {('A', 'C'): 3, ('A', 'B'): 4},
 ('a', 'c'): {('A', 'B'): 5, ('A', 'C'): 6},
 ('b', 'a'): {('A', 'C'): 7, ('A', 'B'): 8},
 ('b', 'b'): {('A', 'D'): 9, ('A', 'B'): 10}})<jupyter_output><empty_output><jupyter_text>11.7   Column selection, addition, deletion  :  ============================================        -   DataFrame can be treated semantically like a dict of like-indexed Series objects. Getting, setting, and deleting columns works with the same syntax as the analogous dict operations<jupyter_code>my_dict = {
 'one' : pd.Series([1., 2., 3.], index=['a', 'b', 'c']),
 'two' : pd.Series([1., 2., 3., 4.], index=['a', 'b', 'c', 'd'])
 }

_dataFrame = pd.DataFrame(my_dict)
_dataFrame['one'] #it will display data of colum1<jupyter_output><empty_output><jupyter_text>Assigning values to new columns: ==============================<jupyter_code>_dataFrame['three'] = _dataFrame['one'] * _dataFrame['two'] # assigning values to a colomn named 'three' after calculation
my_dataFrame['flag'] = my_dataFrame['one'] > 2 #check if value at column 'one' is > 2 then assign True otherwise false
my_dataFrame #print a complete data frame<jupyter_output><empty_output><jupyter_text>Deleting a Column: =================<jupyter_code>del _dataFrame['two'] #delete a coloumn 'two' from data frame<jupyter_output><empty_output><jupyter_text>Poping a column: ==================<jupyter_code>three = _dataFrame.pop('three') #pop a complete coloumn 'three' from dataframe
_dataFrame<jupyter_output><empty_output><jupyter_text>Inserting a scaler value: =======================<jupyter_code>_dataFrame['yes'] = 'no' #new coloum yes will be progate wit no
_dataFrame<jupyter_output><empty_output><jupyter_text>Taking value from a colum and placing in other: =========================================<jupyter_code>_dataFrame['super'] = _dataFrame['yes'][:2]
_dataFrame
#taking first 2 values from column yes and placing in super column<jupyter_output><empty_output><jupyter_text>Inserting new column using Insert Function: =====================================<jupyter_code>_dataFrame.insert(1, 'hello', _dataFrame['one'])
# it will add a new column of name hello and copy data of column two in it
_dataFrame<jupyter_output><empty_output><jupyter_text>11.8  Indexing / Selection :  ===========================<jupyter_code>_dataFrame.loc['b'] #it will return the coloumn labels and values on row label 'b'
_dataFrame.iloc[1] #it will return the values of those coloumns that is > than 1<jupyter_output><empty_output><jupyter_text>11.9  Data Alignment and Arithmatic :  ======================================<jupyter_code>df = pd.DataFrame(np.random.randn(10, 4), columns=['A', 'B', 'C', 'D'])
df2 = pd.DataFrame(np.random.randn(7, 3), columns=['A', 'B', 'C'])<jupyter_output><empty_output><jupyter_text>Adding Columns: ==============<jupyter_code>df + df2 # add values of respective coloumn labels<jupyter_output><empty_output><jupyter_text>Adding Rows: ============<jupyter_code>df - df.iloc[1] #subtracting row1 from row1<jupyter_output><empty_output><jupyter_text>Multiplication and Addition: =======================<jupyter_code>df * 5 + 2<jupyter_output><empty_output><jupyter_text>Division: =======<jupyter_code>1 / df<jupyter_output><empty_output><jupyter_text>Exponential: =======<jupyter_code>df ** 4<jupyter_output><empty_output><jupyter_text>11.10  Boolean Operators :  ==========================<jupyter_code>df1 = pd.DataFrame({'a' : [1, 0, 1], 'b' : [0, 1, 1] }, dtype=bool)

df2 = pd.DataFrame({'a' : [0, 1, 1], 'b' : [1, 1, 0] }, dtype=bool)
pd.DataFrame({'a' : [0, 1, 1], 'b' : [1, 1, 0] }, dtype=bool)<jupyter_output><empty_output><jupyter_text>And Operator: ============<jupyter_code>df1 & df2<jupyter_output><empty_output><jupyter_text>OR Operator: ============<jupyter_code>df1 | df2<jupyter_output><empty_output><jupyter_text>NOT Operator: ============<jupyter_code>-df1
-df2<jupyter_output><empty_output><jupyter_text>11.11  Transpose :  ===================<jupyter_code># we use T attribute for trnaspose<jupyter_output><empty_output><jupyter_text>Transpose of Whole Table: =====================<jupyter_code>df.T<jupyter_output><empty_output><jupyter_text>Transpose of Selected rows: ========================<jupyter_code>df[:4].T<jupyter_output><empty_output><jupyter_text>12. Viewing Data in Pandas :  =====================================   We can view data / display data in different ways: - See the top & bottom rows of the frame- Selecting a single column- Selecting via [], which slices the rows- For getting a cross section using a label- Selecting on a multi-axis by labe- Showing label slicing, both endpoints are included- Reduction in the dimensions of the returned object- For getting a scalar value- For getting fast access to a scalar- Select via the position of the passed integers- By integer slices, acting similar to numpy/python- By lists of integer position locations, similar to the numpy/python style- For slicing rows explicitly- For slicing columns explicitly- For getting a value explicitly- For getting fast access to a scalar- Using a single column’s values to select data.- Selecting values from a DataFrame where a boolean condition is met.- Using the isin() method for filtering  12.1 Head of Data:  =======================<jupyter_code>df.head(2)
#first 2 records<jupyter_output><empty_output><jupyter_text>12.2 tail of Data:  =====================<jupyter_code>df.tail(2)
#last 2 records<jupyter_output><empty_output><jupyter_text>12.3 Display Index:  =====================<jupyter_code>df.index<jupyter_output><empty_output><jupyter_text>12.4 Display Column:  ========================<jupyter_code>df.columns<jupyter_output><empty_output><jupyter_text>12.5 Printing Values:   ======================<jupyter_code>df.values<jupyter_output><empty_output><jupyter_text>12.6 Sorting by axis:   ======================<jupyter_code>df.sort_index(axis=0, ascending=False)<jupyter_output><empty_output><jupyter_text>12.7 Sorting by values:  =========================<jupyter_code>df.sort_values(by='B')<jupyter_output><empty_output><jupyter_text>12.8 Describing DataFrame:  ============================<jupyter_code>df.describe()<jupyter_output><empty_output><jupyter_text>12.9 Selecting a Column:  ============================<jupyter_code>df['C']
#here's the data of column C<jupyter_output><empty_output><jupyter_text>12.10 Slicing:  ==================<jupyter_code>df[0:7]
# By lists of integer position locations, similar to the numpy/python style
df.iloc[[1,2,4],[0,2]]

# For slicing rows explicitly
df.iloc[:,1:2]
# For getting a value explicitly
df.iloc[1,2]

# Using a single column’s values to select data.
df[df.A > 0]

# Selecting values from a DataFrame where a boolean condition is met.
df[df > 0]

# Using the isin() method for filtering:
df2 = df.copy()
df2<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1897008651151456, 'fraction_numerical': 0.03005056658949677, 'mean_word_length': 5.535636074059327, 'pattern_counts': {'":': 0, '<': 469, '<?xml version=': 0, '>': 482, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '124', 'dir': 'jupyter-structured-clean-dedup', 'id': '316964', 'license': 'MIT', 'n_tokens_mistral': 21381, 'n_tokens_neox': 19872, 'n_words': 6904, 'path': 'Python-fundamentals-By-Mohsin Nazakat.ipynb', 'repo_name': 'mohsinnazakat1/python-fundamentals', 'size': '65800'}