0% found this document useful (0 votes)
59 views154 pages

Python Notes

Uploaded by

Sindhu
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
Download as pdf or txt
0% found this document useful (0 votes)
59 views154 pages

Python Notes

Uploaded by

Sindhu
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
Download as pdf or txt
Download as pdf or txt
You are on page 1/ 154

PYTHON PROGRAMMING

UNIT1
Fundamental ideas of Computer Science
Algorithms: Stepbystep procedures to solve problems efficiently.

Data Structures: Organized ways to store and manage data (e.g., arrays, trees).

Programming Paradigms: Different styles of writing code (e.g., procedural,


objectoriented).

Computational Complexity: Analysis of algorithm efficiency in terms of time and space.

Recursion: A method where a function calls itself to solve problems.

Abstraction: Simplifying complexity by hiding details and focusing on higherlevel


concepts.

Concurrency: Executing multiple tasks simultaneously for better performance.

Automata Theory: Study of abstract machines and computational problems they can solve.

Computability: Understanding what problems can and cannot be solved by computers.

Software Engineering: Principles and practices for building reliable and maintainable
software.

Strings
A String is a data structure in Python Programming that represents a sequence of
characters. It is an immutable data type, meaning that once you have created a string, you
cannot change it. Python String are used widely in many different applications, such as
storing and manipulating text data, representing names, addresses, and other types of data
that can be represented as text.

Syntax of String Data Type in Python

string_variable = 'Hello, world!'

Create a String in Python

Strings in Python can be created using single quotes or double quotes or even triple quotes.

Assignment
There are some important properties of assignment in Python :

o Assignment creates object references instead of copying the objects.


o Python creates a variable name the first time when they are assigned a value.
1
o Names must be assigned before being referenced.
o There are some operations that perform assignments implicitly.

o Three types of assignment in Python—Single Assignment, Multiple Assignment,


and Chained Assignment

Single Assignment:

Definition: Single assignment refers to assigning a value to a single variable. This is the
most basic form of assignment in Python.

Example:

x = 10

Multiple Assignment:

Definition: Multiple assignment allows you to assign values to multiple variables


simultaneously in a single line of code.

Example:

a, b, c = 3, 4, 5

Chained Assignment

Definition: Chained assignment allows you to assign a single value to multiple variables
simultaneously, linking them together in a chain.

Example:

x=y=z=0

Comments
Comments in Python are nonexecutable statements that you can include in your code to
explain, document, or clarify specific sections of the code. They are ignored by the Python
interpreter, meaning they have no impact on the execution of the program. Comments are
primarily used to make the code more understandable for anyone reading it, including your
future self.

Types of Comments in Python

• Singleline Comments
• Multiline Comments

1. Singleline Comments

2
Definition: Singleline comments start with the hash symbol and extend to the end of the
line. Everything after the on that line is considered a comment and is ignored by the
interpreter.

Example:

x = 5 This comment explains that x is being assigned the value 5

Multiline Comments

Definition: Python does not have a distinct syntax for multiline comments like some other
programming languages. However, multiline comments can be achieved in two common
ways:

Using multiple singleline comments () consecutively.

Using triplequoted strings (''' or """) that are not assigned to a variable.

Example with Multiple Singleline Comments:

This is a comment

that spans multiple lines

explaining the following block of code

x=5

y = 10

result = x + y

Example with Triplequoted Strings:

"""

This is a multiline comment.

It spans multiple lines.

Useful for providing more detailed explanations.

"""

x=5

y = 10

result = x + y

3
Numeric Data Types
In Python, numeric data types are used to represent and work with numbers. Python
supports several numeric data types, each serving different purposes:

1. Integer (int)

Description: Represents whole numbers without a fractional part. Integers can be positive,
negative, or zero.

Example: 10, 0, 42

Characteristics:

Integers in Python are of arbitrary precision, meaning they can be as large or as small as the
memory allows.

Common operations include addition, subtraction, multiplication, division, and more.

x = 10

y=3

z=0

2. Floatingpoint (float)

Description: Represents real numbers that contain a fractional part. Floats are used for
calculations involving decimals.

Example: 3.14, 0.001, 2.0

Characteristics:

Floats have limited precision due to their representation in memory, typically accurate to
about 15 decimal places.

Common operations include addition, subtraction, multiplication, division, and more


complex mathematical functions.

pi = 3.14159

temperature = 273.15

3.Complex :

Description: Represents complex numbers, which consist of a real part and an imaginary
part. The imaginary part is represented by j in Python.

Example: 3 + 4j, 1 2j

Characteristics:

4
Complex numbers are used in advanced mathematical computations, particularly in fields
like engineering and physics.

Operations on complex numbers include addition, subtraction, multiplication, and division.

z = 2 + 3j

w = 1j

Key Points About Numeric Data Types:

Type Conversion: Python allows you to convert between these numeric types using
functions like int(), float(), and complex().

Operations: You can perform various arithmetic operations on these types, and Python will
automatically handle the type conversions when necessary.

Character sets
A character set in Python refers to the set of characters that Python recognizes and uses in
its code and strings. It includes letters, digits, punctuation marks, and various symbols that
can be used to write Python programs and represent text data.

ASCII: A basic character set with 128 characters, covering English letters, digits, and
common symbols.

Unicode: A comprehensive character set that covers virtually all characters from all writing
systems, symbols, and emojis.

Escape Sequences: Special sequences that represent nonprintable or special characters in


strings.

Key Points About Character Sets in Python:

ASCII Character Set

Description: The ASCII (American Standard Code for Information Interchange) character
set is one of the earliest character sets and includes 128 characters. These characters
include:

Control characters (e.g., newline \n, tab \t)

Example: 'A', 'a', '1', '

ascii_value = ord('A') Returns 65, the ASCII value of 'A''


5
Unicode Character Set:

Description: Python uses the Unicode character set by default, which is a superset of ASCII
and includes a much broader range of characters. Unicode supports characters from
virtually every written language, as well as a wide range of symbols and emojis.

Escape Sequences

Description: Escape sequences are special character combinations in strings that represent
certain special characters. They start with a backslash (\).

Common Escape Sequences:

• \n: Newline
• \t: Tab
• \\: Backslash
• \': Single quote
• \": Double quote

Expressions
In Python, an expression is a combination of values, variables, operators, and function calls
that the Python interpreter evaluates to produce another value. Expressions are fundamental
in programming because they perform computations, assign values, and control the flow of
a program.

Key Concepts of Expressions in Python:

Literals as Expressions

Definition: A literal is a fixed value written directly in the code. Literals themselves are
simple expressions.

Examples:

• 42 (integer literal)
• 'Hello, World!' (string literal)
• 3.14 (float literal)
• True (Boolean literal)

result = 42 The literal 42 is an expression

Variable References as Expressions

Definition: A variable name that refers to a value in memory is also an expression.

Example:

If x = 5, then x is an expression that evaluates to 5.

6
Arithmetic Expressions

Definition: Expressions that involve arithmetic operators (+, , *, /, //, %, ) to perform


mathematical calculations.

Examples:

3 + 4 (Addition)

2 * 5 (Multiplication)

10 / 2 (Division)

Eg:

result = (3 + 4) * 2 Evaluates to 14

Comparison Expressions

Definition: Expressions that compare two values using comparison operators (==, !=, >, <,
>=, <=). These expressions evaluate to Boolean values (True or False).

Examples:

5 > 3 evaluates to True

4 == 4 evaluates to True

3 != 3 evaluates to False

Eg:

is_equal = (10 == 10) Evaluates to True

Logical Expressions

Definition: Expressions that combine Boolean values or comparison expressions using


logical operators (and, or, not).

Examples:

True and False evaluates to False

not (5 > 3) evaluates to False

(4 > 2) or (3 > 5) evaluates to True

Function Call Expressions

Definition: When you call a function, it is an expression because it produces a value (the
function's return value).

Example:

len("Hello") evaluates to 5
7
max(3, 7, 2) evaluates to 7

loops and selection statements


Loops and selection statements are fundamental control flow structures in Python that allow
you to manage how your code executes.

Selection Statements (Conditional Statements)

Selection statements, also known as conditional statements, enable your program to make
decisions and execute certain parts of code based on specific conditions.

Types of Selection Statements in Python:

• if Statement
• ifelse Statement
• ifelifelse Statement
• Nested if Statements

if Statement

Description: The if statement evaluates a condition (an expression that returns True or
False). If the condition is True, the code block within the if statement is executed.

Syntax:

if condition:

Code block to execute if condition is True

Example:

x = 10

if x > 5:

print("x is greater than 5")

ifelse Statement

Description: The ifelse statement provides an alternative path of execution. If the condition
is True, the if block is executed; otherwise, the else block is executed.

Syntax:

if condition:

Code block to execute if condition is True

else:

8
Code block to execute if condition is False

Example:

x=3

if x > 5:

print("x is greater than 5")

else:

print("x is 5 or less")

ifelifelse Statement

Description: The ifelifelse statement is used to check multiple conditions in sequence. If the
first if condition is False, the program checks the elif (elseif) conditions in order. If none of
the elif conditions are True, the else block is executed (if provided).

Syntax:

if condition1:

elif condition2:

elif condition3:

else:

Example:

x = 15

if x > 20:

print("x is greater than 20")

elif x > 10:

print("x is greater than 10 but not greater than 20")

elif x > 0:

print("x is positive but not greater than 10")

else:

print("x is zero or negative")

Nested if Statements

Description: An if statement inside another if statement is called a nested if. This allows
you to check additional conditions within an already true condition.

9
Example:

age = 25

if age >= 18:

if age < 21:

print("You are an adult but not yet 21.")

else:

print("You are 21 or older.")

else:

print("You are a minor.")

Loop Statements
Loops allow you to execute a block of code repeatedly based on certain conditions.

Types of Loops in Python:

• for Loop
• while Loop

for Loop

Description: The for loop is used to iterate over a sequence (like a list, tuple, string, or
range) and execute a block of code for each item in the sequence.

Syntax:

for variable in sequence:

Code block to execute for each item in the sequence

Example:

fruits = ['apple', 'banana', 'cherry']

for fruit in fruits:

print(fruit)

while Loop

Description: The while loop repeatedly executes a block of code as long as a specified
condition is True.

Syntax:

while condition:

10
Code block to execute while condition is True

Example:

count = 0

while count < 5:

print(count)

count += 1

Break and Continue Statements

break Statement: Exits the current loop prematurely.

Example:

for num in range(10):

if num == 5:

break

print(num)

continue Statement:

Skips the rest of the code inside the loop for the current iteration and moves to the next
iteration.

Example:

for num in range(10):

if num % 2 == 0:

continue

print(num)

UNIT 2
Accessing characters
Accessing characters in Python refers to retrieving individual characters from a string using
indexing. Python strings are sequences of characters, and you can use indexing to access
any character at a specific position within the string.

Positive Indexing:

11
Positive indexing means starting from the beginning of the string. The first character is at
position 0, the second at position 1, and so on.

Eg:

text = "Python"

print(text[0]) Output: 'P' (first character)

print(text[1]) Output: 'y' (second character)

print(text[5]) Output: 'n' (sixth character)

Negative Indexing:

Negative indexing starts from the end of the string. The last character is at position 1, the
secondtolast at 2, and so on.

Eg:

text = "Python"

print(text[1]) Output: 'n' (last character)

print(text[2]) Output: 'o' (secondtolast character)

Substrings in Strings:

A substring is simply a smaller part of a string. In Python, you can extract substrings from a
string using slicing. Slicing allows you to select a portion of the string based on its position.

Substrings: Parts of a string.

Basic Slicing: string[start:end] gets a substring from start to end1.

Omitting Indices: string[:end] or string[start:] extracts from the start or to the end.

Step Value: string[start:end:step] allows skipping characters.

Data Encryption
Data encryption in Python involves converting data into a secure format that is unreadable
without a decryption key. This process helps protect sensitive information from
unauthorized access. Python provides libraries and tools to perform encryption and
decryption operations.

Encryption: The process of converting plaintext data into ciphertext using an algorithm
and a key. This makes the data unreadable to anyone who does not have the decryption key.

12
Decryption: The process of converting ciphertext back into plaintext using a decryption
key. This allows authorized users to access the original data.

Common Libraries for Encryption in Python:

cryptography and pycryptodome

Strings and Number Systems


Strings:

A string is a sequence of characters enclosed in quotes. Strings are used to handle text in
Python.

Single Quotes: 'Hello'

Double Quotes: "Hello"

Triple Quotes: '''Hello''' or """Hello""" (for multiline strings)

Number Systems:

Python supports several number systems: decimal, binary, octal, and hexadecimal.

1.Decimal (Base 10)

Description: The standard number system used in daily life, with digits 09.

Example:

decimal_num = 42

2.Binary (Base 2)

Description: Uses only 0s and 1s. Prefixed with 0b.

Example

binary_num = 0b101010 Equivalent to 42 in decimal

3.Octal (Base 8)

Description: Uses digits 07. Prefixed with 0o.

Example:

octal_num = 0o52 Equivalent to 42 in decimal

4. Hexadecimal (Base 16)

Description: Uses digits 09 and letters AF. Prefixed with 0x.

Example:
13
hex_num = 0x2A Equivalent to 42 in decimal

String Methods
Transforming: Change case and format (upper(), lower(), capitalize(), title())

Trimming: Remove extra spaces (strip())

Replacing and Finding: Modify or locate parts (replace(), find())

Checking: See if it starts or ends with specific text (startswith(), endswith())

Splitting and Joining: Break into parts or combine (split(), join())

Padding: Add zeros (zfill())

Common String Methods:

1.upper(): Converts all characters to uppercase.

"hello".upper() Output: 'HELLO'

2.lower(): Converts all characters to lowercase.

"HELLO".lower() Output: 'hello'

3.capitalize(): Capitalizes the first character and makes others lowercase.

"hello world".capitalize() Output: 'Hello world'

4.title(): Capitalizes the first letter of each word.

"hello world".title() Output: 'Hello World'

5.strip(): Removes leading and trailing whitespace.

" hello ".strip() Output: 'hello'

6.replace(old, new): Replaces occurrences of old with new.

"hello world".replace("world", "Python") Output: 'hello Python'

7.find(substring): Finds the first occurrence of substring. Returns 1 if not found.

"hello world".find("world") Output: 6

8.startswith(prefix): Checks if the string starts with prefix.

"hello world".startswith("hello") Output: True

9.endswith(suffix): Checks if the string ends with suffix.

"hello world".endswith("world") Output: True


14
10.split(separator): Splits the string into a list using separator.

"hello world".split() Output: ['hello', 'world']

11.join(iterable): Joins elements of an iterable into a single string with the string as the
separator.

" ".join(["hello", "world"]) Output: 'hello world'

12. zfill(width): Pads the string with zeros on the left to reach width.

"42".zfill(5) Output: '00042'

Text
In Python, "text" refers to strings, which are sequences of characters. You can create strings
with quotes, manipulate them with operations and methods, and format them using various
techniques. Strings are immutable, meaning their content cannot be changed after creation.

Lists and Dictionaries


Lists:

Lists are ordered collections of items. You can store multiple items in a single variable.

How to Create: Use square brackets [] with items separated by commas.

my_list = [1, 2, 3, 'apple', 'banana']

Common Operations:

Add items: my_list.append('grape')

Remove items: my_list.remove('apple')

Get length: len(my_list)

Dictionaries:

Dictionaries are collections of keyvalue pairs. Each key is unique, and you use it to retrieve
the corresponding value.

How to Create: Use curly braces {} with keys and values separated by colons.

my_dict = {'name': 'Alice', 'age': 25}

Common Operations:

Add items: my_dict['city'] = 'New York'

15
Remove items: del my_dict['name']

Get keys: my_dict.keys()

Get values: my_dict.values()

Design and Functions


Design:

Purpose: Organize code for clarity and reuse.

Modularization: Split code into functions and modules.

Readability: Use clear names, comments, and consistent formatting.

Functions:

Definition: Reusable code blocks that perform tasks.

Syntax:

def greet(name):

return f"Hello, {name}!"

Problem Solving with TopDown Design


Steps for TopDown Design

o Understand the Problem: Define the overall goal or problem you need to solve.
o Break Down the Problem: Divide the problem into smaller subproblems or tasks.
Each subproblem should be a manageable piece of the overall solution.
o Design HighLevel Structure: Outline the highlevel structure of your solution,
including major components and their interactions.
o Implement SubComponents: Write code for each subproblem or component.
Focus on implementing the functionality for each part.
o Integrate Components: Combine the subcomponents to form the complete
solution, ensuring that they work together as intended.
o Test and Refine: Test the integrated solution, fix any issues, and refine the code as
needed.

Program:

#HighLevel Function

def process_numbers(numbers):

avg = compute_average(numbers)

16
maximum = find_max(numbers)

minimum = find_min(numbers)

return avg, maximum, minimum

#SubFunctions

def compute_average(numbers):

return sum(numbers) / len(numbers)

def find_max(numbers):

return max(numbers)

def find_min(numbers):

return min(numbers)

#Example Usage

numbers = [10, 20, 30, 40, 50]

avg, maximum, minimum = process_numbers(numbers)

print(f"Average: {avg}, Max: {maximum}, Min: {minimum}")

Design with recursive Functions


A recursive function is a function that calls itself in order to solve a problem. Recursion is a
powerful technique that allows a problem to be divided into smaller subproblems of the same type.
The recursive function keeps calling itself with these smaller subproblems until it reaches a base
case, which is a simple condition that stops the recursion.

Key Components of a Recursive Function:

• Base Case: The condition under which the function stops calling itself. It prevents
infinite recursion and typically represents the simplest, smallest instance of the
problem.
• Recursive Case: The part of the function where it calls itself with a modified version
of the original problem, gradually moving towards the base case.

17
Designing a recursive function in Python involves identifying a problem that can be broken down
into smaller subproblems of the same type. Let's illustrate this with an example: calculating the
factorial of a number.

Example: Factorial Calculation using Recursion

The factorial of a number n (denoted as n!) is the product of all positive integers less than or equal
to n. It's defined as:

• 0! = 1 (Base Case)

• n! = n * (n1)! (Recursive Case)

Program:

def factorial(n):

Base case: when n is 0, factorial is 1

if n == 0:

return 1

else:

Recursive case: multiply n by the factorial of n1

return n * factorial(n 1)

Test the recursive function

number = 5

print(f"The factorial of {number} is {factorial(number)}")

Managing a Program’s namespace


Managing a program's namespace in Python involves understanding how Python organizes and
accesses variables, functions, classes, and other identifiers within a program. Namespaces help
avoid naming conflicts by grouping identifiers into different contexts.

Key Concepts:

1. Namespace:

o A namespace is a container that holds a set of identifiers (names) and the objects
they are bound to.

o Python uses different namespaces to distinguish between identifiers with the same
name but in different contexts.

2. Types of Namespaces:
18
o Builtin Namespace: Contains names for all builtin functions and exceptions, such as
print(), len(), int(), etc. This namespace is available everywhere in the program.

o Global Namespace: Contains names defined at the top level of a script or module.
These names are accessible within the module or script.

o Local Namespace: Contains names defined inside a function. These names are only
accessible within that function.

o Enclosing Namespace: When you have nested functions, the enclosing namespace
refers to the namespace of the outer function.

Example:

Global namespace

global_var = 10

def outer_function():

Enclosing namespace

enclosing_var = 20

def inner_function():

Local namespace

local_var = 30

print("Local variable:", local_var)

print("Enclosing variable:", enclosing_var)

print("Global variable:", global_var)

inner_function()

outer_function()

print("Global variable accessed outside function:", global_var)

The globals() and locals() Functions:

• globals(): Returns a dictionary of the current global namespace.


19
• locals(): Returns a dictionary of the current local namespace.

Example: Using globals() and locals()

x=5

def func():

y = 10

print("Local Namespace:", locals())

print("Global Namespace:", globals())

func()

Output

Local Namespace: {'y': 10}

Global Namespace: {'__name__': '__main__', '__doc__': None, ..., 'x': 5, 'func': <function func at
0x7f5e8c2d0>}

Scope Resolution (LEGB Rule):

Python resolves names by looking them up in the following order:

1. Local (L): The innermost scope, where the function is currently executing.

2. Enclosing (E): The scope of any enclosing functions, from the inside out.

3. Global (G): The scope of the current module.

4. Builtin (B): The scope of builtin names.

If Python doesn't find a name in one scope, it moves to the next, eventually raising a NameError if
the name isn't found.

Managing Namespaces:

• Avoid Global Variables: Limit the use of global variables to prevent naming conflicts and
make your code more modular.

• Use Functions: Encapsulate code within functions to create local namespaces.

• Modules: Group related functions and classes in modules to manage the global namespace
effectively.

20
HigherOrder Functions
Higherorder functions are a powerful concept in programming, especially in languages like Python
that support firstclass functions. A higherorder function is a function that either:

1. Takes one or more functions as arguments.

2. Returns a function as its result.

These functions enable more abstract and concise ways to manipulate data and behavior in your
code.

Key Characteristics of HigherOrder Functions:

1. Functions as Arguments:

o You can pass functions to other functions as arguments. This allows for operations
like applying a function to each element in a list, filtering elements based on a
condition, or reducing a list to a single value.

2. Functions as Return Values:

o You can return a function from another function. This is useful for creating
customized or partially applied functions on the fly.

Examples of HigherOrder Functions:

1. Using Functions as Arguments

Example: map() function

The map() function is a builtin Python higherorder function that applies a given function to all
items in an input list (or any iterable) and returns a map object.

python code

#A simple function that squares a number

def square(x):

return x * x

# Use map to apply the square function to each element in a list

numbers = [1, 2, 3, 4, 5]

squared_numbers = map(square, numbers)

print(list(squared_numbers))

Output:

21
[1, 4, 9, 16, 25]

2. Returning Functions from Functions

Example: Creating a Function Factory

You can create a function that returns another function, useful for generating customized functions.

python

def make_multiplier(factor):

def multiply(x):

return x * factor

return multiply

#Create a function that doubles a number

doubler = make_multiplier(2)

print(doubler(5)) Output: 10

# Create a function that triples a number

tripler = make_multiplier(3)

print(tripler(5)) Output: 15

In this example, make_multiplier() is a higherorder function because it returns a function (multiply)


that multiplies a number by a given factor.

3.Common HigherOrder Functions in Python:

1. map(function, iterable): Applies a function to every item in an iterable.

2. filter(function, iterable): Filters items out of an iterable based on a function.

3. reduce(function, iterable): Applies a function cumulatively to the items in an iterable,


reducing it to a single value (from the functools module).

4. sorted(iterable, key=function): Sorts an iterable based on a key function.

5. any(iterable) and all(iterable): Check if any or all elements in an iterable satisfy a


condition, often using a lambda or another function.

4.Using Lambdas with HigherOrder Functions

Lambda functions are often used with higherorder functions for short, inline operations.

22
Example:

python

Copy code

numbers = [1, 2, 3, 4, 5]

squared_numbers = map(lambda x: x * x, numbers)

print(list(squared_numbers)) Output: [1, 4, 9, 16, 25]

Benefits of HigherOrder Functions:

• Modularity: They allow you to break down complex operations into simpler functions.

• Reusability: Functions can be reused and composed in different ways.

• Abstraction: They allow you to write more abstract and generalpurpose code.

Higherorder functions are a key feature in functional programming and can make your Python code
more expressive and powerful

Unit III
Design with Classes:
Designing with classes in Python involves using objectoriented programming (OOP) principles to
create models that represent realworld entities or abstract concepts. Classes encapsulate data
(attributes) and behaviors (methods) into a single blueprint that can be used to create objects
(instances).

Key Concepts in OOP:

1. Class: A blueprint for creating objects. It defines attributes (data) and methods (functions)
that the created objects will have.

2. Object: An instance of a class. It has its own state and can use the methods defined by its
class.

3. Attributes: Variables that belong to a class or object. They represent the state or properties
of an object.

4. Methods: Functions defined inside a class that describe the behaviors of an object.

5. Encapsulation: The bundling of data and methods that operate on the data within one unit
(class). It also involves restricting direct access to some of an object’s components.

6. Inheritance: A mechanism by which one class (child class) can inherit attributes and
methods from another class (parent class).

23
7. Polymorphism: The ability to use a single interface to represent different underlying forms
(data types).

8. Abstraction: Hiding the complex implementation details and showing only the essential
features of the object.

Getting inside Objects and Classes


Classes and Objects in Python

Classes and objects are fundamental concepts in objectoriented programming (OOP). They
provide a way to model realworld entities and their interactions in a program.

1. Class

A class is a blueprint for creating objects. It defines a set of attributes (data) and methods
(functions) that the objects created from the class will have. A class encapsulates data and the
functions that operate on that data, providing a way to model the behavior and properties of
entities.

Key Points:

• A class defines a type of object, but it is not an object itself.

• It acts as a template from which objects are instantiated.

• A class can include data members (attributes) and methods (functions) that describe the
behavior of the objects.

Example:

python

Copy code
24
class Car:

Constructor method to initialize attributes

def __init__(self, brand, model, year):

self.brand = brand Instance attribute

self.model = model Instance attribute

self.year = year Instance attribute

Method to describe the car

def describe(self):

return f"{self.year} {self.brand} {self.model}"

Method to start the car

def start(self):

return f"{self.brand} {self.model} is starting."

Car is a class that defines the structure and behavior of a car object

2. Object

An object is an instance of a class. When a class is defined, no memory is allocated until an object
of that class is created. Each object has its own state (the values of its attributes) and can use the
methods defined by the class to interact with its data or perform actions.

Key Points:

• Objects are instances of a class, and each object can have different attribute values.

• Objects can access methods defined in their class, allowing them to perform actions.

Example:

Python code

Creating an object of the Car class

my_car = Car("Toyota", "Corolla", 2021)

Accessing attributes of the object

25
print(my_car.brand) Output: Toyota

print(my_car.model) Output: Corolla

print(my_car.year) Output: 2021

Calling methods of the object

print(my_car.describe()) Output: 2021 Toyota Corolla

print(my_car.start()) Output: Toyota Corolla is starting.

3. Relationship Between Classes and Objects

• Class: Defines the structure and behavior of objects. It's like a blueprint or a template.

• Object: An instance of a class with actual data. It's like a physical object created based on
the blueprint.

Data modeling
Data modeling in Python involves creating classes that represent entities and their relationships,
encapsulating data, and defining how this data can be manipulated. It provides a structured way to
represent realworld objects and their interactions within a program. Here are some simple examples
of data modeling in Python:

1. Basic Data Modeling

Example: Modeling a Book

Let's model a simple Book class to represent a book with attributes like title, author, and year of
publication.

python

Copy code

class Book:

def __init__(self, title, author, year):

self.title = title Title of the book

self.author = author Author of the book

self.year = year Year of publication

def get_details(self):

return f"{self.title} by {self.author}, published in {self.year}"


26
Creating an object of the Book class

book1 = Book("To Kill a Mockingbird", "Harper Lee", 1960)

Accessing the attributes and method

print(book1.get_details()) Output: To Kill a Mockingbird by Harper Lee, published in 1960

2. Data Modeling with Relationships

Example: Modeling a Library System

Let's extend the example to model a library that holds multiple books. We will use two classes:
Library and Book, where Library contains a collection of Book objects.

python

Copy code

class Book:

def __init__(self, title, author, year):

self.title = title

self.author = author

self.year = year

def get_details(self):

return f"{self.title} by {self.author}, published in {self.year}"

class Library:

def __init__(self):

self.books = [] A list to store books

def add_book(self, book):

self.books.append(book)

def list_books(self):
27
return [book.get_details() for book in self.books]

Creating objects of Book

book1 = Book("1984", "George Orwell", 1949)

book2 = Book("The Great Gatsby", "F. Scott Fitzgerald", 1925)

Creating an object of Library and adding books to it

library = Library()

library.add_book(book1)

library.add_book(book2)

Listing all books in the library

print(library.list_books())

Output:

['1984 by George Orwell, published in 1949', 'The Great Gatsby by F. Scott Fitzgerald, published
in 1925']

3. Data Modeling with Inheritance

Example: Modeling Different Types of Employees

Let's model different types of employees using inheritance. We will have a base class Employee
and derived classes FullTimeEmployee and PartTimeEmployee.

python

Copy code

class Employee:

def __init__(self, name, id_number):

self.name = name

self.id_number = id_number

def get_info(self):

return f"Name: {self.name}, ID: {self.id_number}"

28
class FullTimeEmployee(Employee):

def __init__(self, name, id_number, salary):

super().__init__(name, id_number)

self.salary = salary

def get_info(self):

return f"{super().get_info()}, Salary: ${self.salary}"

class PartTimeEmployee(Employee):

def __init__(self, name, id_number, hourly_rate):

super().__init__(name, id_number)

self.hourly_rate = hourly_rate

def get_info(self):

return f"{super().get_info()}, Hourly Rate: ${self.hourly_rate}"

Creating objects of FullTimeEmployee and PartTimeEmployee

full_time_emp = FullTimeEmployee("Alice", "E001", 60000)

part_time_emp = PartTimeEmployee("Bob", "E002", 20)

Accessing information

print(full_time_emp.get_info()) Output: Name: Alice, ID: E001, Salary: $60000

print(part_time_emp.get_info()) Output: Name: Bob, ID: E002, Hourly Rate: $20

4. Data Modeling with Encapsulation and Validation

Example: Modeling a Bank Account with Validation

Let's model a BankAccount class with validation to ensure that deposits and withdrawals are valid.

29
python

Copy code

class BankAccount:

def __init__(self, owner, balance=0):

self.owner = owner

self._balance = balance Encapsulated attribute

def deposit(self, amount):

if amount > 0:

self._balance += amount

print(f"Deposited ${amount}. New balance is ${self._balance}.")

else:

print("Deposit amount must be positive.")

def withdraw(self, amount):

if amount > 0 and amount <= self._balance:

self._balance = amount

print(f"Withdrew ${amount}. New balance is ${self._balance}.")

else:

print("Invalid withdrawal amount.")

def get_balance(self):

return self._balance

Creating a BankAccount object

account = BankAccount("Sindhu", 1000)

Depositing and withdrawing money

30
account.deposit(500) Output: Deposited $500. New balance is $1500.

account.withdraw(200) Output: Withdrew $200. New balance is $1300.

print(account.get_balance()) Output: 1300

Building a New Data Structure


Creating custom data structures in Python involves defining classes that manage data and provide
operations for manipulating that data. You can build a variety of data structures, such as stacks,
queues, linked lists, and more. Here are some simple examples of custom data structures:

1. Stack

A stack is a linear data structure that follows the Last In, First Out (LIFO) principle. Elements are
added and removed from the same end, called the "top" of the stack.

Implementation of Stack

python

Copy code

class Stack:

def __init__(self):

self.items = []

def is_empty(self):

return len(self.items) == 0

def push(self, item):

self.items.append(item)

def pop(self):

if not self.is_empty():

return self.items.pop()

else:

raise IndexError("Pop from an empty stack")

31
def peek(self):

if not self.is_empty():

return self.items[1]

else:

raise IndexError("Peek from an empty stack")

def size(self):

return len(self.items)

Using the Stack class

stack = Stack()

stack.push(1)

stack.push(2)

stack.push(3)

print(stack.peek()) Output: 3

print(stack.pop()) Output: 3

print(stack.size()) Output: 2

2. Queue

A queue is a linear data structure that follows the First In, First Out (FIFO) principle. Elements are
added to the rear and removed from the front.

Implementation of Queue

python

Copy code

class Queue:

def __init__(self):

self.items = []

def is_empty(self):
32
return len(self.items) == 0

def enqueue(self, item):

self.items.append(item)

def dequeue(self):

if not self.is_empty():

return self.items.pop(0)

else:

raise IndexError("Dequeue from an empty queue")

def size(self):

return len(self.items)

Using the Queue class

queue = Queue()

queue.enqueue(1)

queue.enqueue(2)

queue.enqueue(3)

print(queue.dequeue()) Output: 1

print(queue.size()) Output: 2

Dimensional grid
Creating a dimensional grid in Python involves creating a multidimensional structure where each
cell or entry can be accessed using indices. Here’s a basic example of how you might create a 2D
grid (a matrix) and access its elements. You can expand this idea to more dimensions as needed.

1. Creating a 2D Grid

33
To create a 2D grid, you can use nested lists:

```python

Create a 2D grid (e.g., 3x3 grid)

rows, cols = 3, 3

grid = [[0 for _ in range(cols)] for _ in range(rows)]

Print the grid

for row in grid:

print(row)

```

2. Accessing and Modifying Elements

You can access and modify elements in the grid using row and column indices:

```python

Accessing an element at row 1, column 2

print(grid[1][2]) Output: 0

Modifying an element at row 1, column 2

grid[1][2] = 5

Print the modified grid

for row in grid:

print(row)

```

3. Creating a 3D Grid

To create a 3D grid, you extend the concept to three dimensions:

```python

Create a 3D grid (e.g., 2x3x4 grid)

34
depth, rows, cols = 2, 3, 4

grid_3d = [[[0 for _ in range(cols)] for _ in range(rows)] for _ in range(depth)]

Print the 3D grid

for d in grid_3d:

for row in d:

print(row)

print() Separate layers

```

4. Using NumPy for More Complex Grids

For more complex grid operations, you might consider using the `numpy` library, which provides
powerful tools for handling multidimensional arrays:

```python

import numpy as np

Create a 2D grid

grid_np = np.zeros((3, 3), dtype=int)

Access and modify elements

print(grid_np[1, 2]) Output: 0

grid_np[1, 2] = 5

print(grid_np)

Create a 3D grid

grid_3d_np = np.zeros((2, 3, 4), dtype=int)

print(grid_3d_np)

```

Using `numpy`, you get a lot of builtin functionality for operations on arrays and matrices, which
can simplify complex grid manipulations.

35
Structuring Classes with Inheritance and Polymorphism
Structuring classes with inheritance and polymorphism in Python involves using objectoriented
programming principles to create a hierarchy of classes where child classes inherit properties and
methods from parent classes, and where different classes can implement the same interface in
different ways.

1. Inheritance

Inheritance allows one class (the child or subclass) to inherit attributes and methods from another
class (the parent or superclass). This promotes code reuse and establishes a hierarchical
relationship between classes.

Basic Example

python

Copy code

Parent class

class Animal:

def __init__(self, name):

self.name = name

def speak(self):

raise NotImplementedError("Subclasses must implement this method")

Child class inheriting from Animal

class Dog(Animal):

def speak(self):

return "Woof!"

Child class inheriting from Animal

class Cat(Animal):

def speak(self):

return "Meow!"

36
Instantiate and use the classes

dog = Dog("Buddy")

cat = Cat("Whiskers")

print(dog.name) Output: Buddy

print(dog.speak()) Output: Woof!

print(cat.name) Output: Whiskers

print(cat.speak()) Output: Meow!

In this example:

• Animal is the parent class with a method speak that is intended to be overridden by
subclasses.

• Dog and Cat are subclasses of Animal that provide their own implementations of the speak
method.

2. Polymorphism

Polymorphism allows different classes to be treated through the same interface or method name,
even if their implementations are different. It enables a unified way to interact with objects of
different classes.

Example

python

Copy code

def make_animal_speak(animal):

print(animal.speak())

Instantiate objects

dog = Dog("Buddy")

cat = Cat("Whiskers")

Using polymorphism

make_animal_speak(dog) Output: Woof!


37
make_animal_speak(cat) Output: Meow!

In this example:

• The function make_animal_speak takes an animal object and calls its speak method.

• Because both Dog and Cat have a speak method, make_animal_speak can be used with
either type of animal, demonstrating polymorphism.

Graphical User Interfaces


Creating Graphical User Interfaces (GUIs) in Python can be done using various libraries. Here are
some simple examples using popular libraries:

1. Tkinter

Tkinter is the standard Python interface to the Tk GUI toolkit. It's included with Python, so you
don't need to install anything extra.

Example 1: Basic Tkinter Window

python

Copy code

import tkinter as tk

Create the main window

root = tk.Tk()

root.title("Simple Tkinter Window")

Create a label widget

label = tk.Label(root, text="Hello, Tkinter!")

label.pack()

Start the GUI event loop

root.mainloop()

2. PyQt5

PyQt5 is a set of Python bindings for Qt libraries, used to create crossplatform applications.

Example 1: Basic PyQt5 Window

38
python

Copy code

from PyQt5.QtWidgets import QApplication, QLabel, QWidget, QVBoxLayout

app = QApplication([])

Create the main window

window = QWidget()

window.setWindowTitle('Simple PyQt5 Window')

Create a layout and add widgets

layout = QVBoxLayout()

label = QLabel('Hello, PyQt5!')

layout.addWidget(label)

window.setLayout(layout)

window.show()

Run the application

app.exec_()

3. Kivy

Kivy is an opensource Python library for developing multitouch applications. It is especially useful
for creating mobile apps and touch interfaces.

Example 1: Basic Kivy Application

python

Copy code

from kivy.app import App

from kivy.uix.label import Label

39
class MyApp(App):

def build(self):

return Label(text='Hello, Kivy!')

if __name__ == '__main__':

MyApp().run()

The Behavior of terminal


In Python, interacting with the terminal (or command line) involves a variety of operations such as
reading input, displaying output, and executing system commands. Here’s an overview of common
behaviors and operations you can perform in the terminal using Python:

1. Displaying Output

To display output in the terminal, you use the `print()` function:

```python

print("Hello, world!")

```

This prints the string `"Hello, world!"` to the terminal.

2. Reading Input

To read input from the user, use the `input()` function:

```python

user_input = input("Enter your name: ")

print(f"Hello, {user_input}!")

```

This code prompts the user to enter their name and then greets them using the input.

3. Executing System Commands

You can execute system commands using the `subprocess` module:

```python

import subprocess

#Run a system command and get the output

result = subprocess.run(['ls', 'l'], capture_output=True, text=True)


40
print(result.stdout)

```

In this example, the `subprocess.run()` function executes the `ls l` command (which lists files in
long format on Unixlike systems) and prints its output.

4. Handling Terminal Output

Sometimes you might want to format or control terminal output, such as clearing the screen or
positioning the cursor. This can be done using ANSI escape codes:

```python

#Clear the terminal screen

print("\033c", end="")

# Move cursor to (0, 0)

print("\033[H", end="")

#Change text color

print("\033[31mThis text is red\033[0m")

```

5. Working with Files

You can read from and write to files, which often involves terminal operations:

```python

#Writing to a file

with open('example.txt', 'w') as file:

file.write("Hello, file!")

# Reading from a file

with open('example.txt', 'r') as file:

content = file.read()

print(content)```

41
6. Command Line Arguments

You can handle command line arguments passed to a Python script using the `sys.argv` list from
the `sys` module:

```python

import sys

if len(sys.argv) > 1:

print(f"Arguments passed: {sys.argv[1:]}")

else:

print("No arguments passed.")

```

#Run this script from the terminal with arguments like this:

```sh

python script.py arg1 arg2

```

7. Error Handling

Handling errors and exceptions is crucial for robust terminal applications:

```python

try:

value = int(input("Enter a number: "))

print(f"You entered: {value}")

except ValueError:

print("That's not a valid number.")

```

8. Interactive Terminals

42
For more advanced terminal interactions, such as creating interactive commandline tools, you
might use libraries like `argparse` for argument parsing, or `curses` for creating textbased
interfaces:

Example with `argparse`

```python

import argparse

parser = argparse.ArgumentParser(description="A simple argument parser.")

parser.add_argument('name', type=str, help='Your name')

args = parser.parse_args()

print(f"Hello, {args.name}!")

```

9. Running a Python Script in Terminal

You typically run a Python script from the terminal using the `python` command:

```sh

python script.py

```

These are some of the fundamental ways you can interact with the terminal using Python.
Depending on your needs, you can explore additional libraries and techniques for more
sophisticated terminalbased applications.

Windows and Window Components


In the context of graphical user interfaces (GUIs), "windows" and "window components" refer to
the elements and structures used to create and manage visual interfaces in applications. These
concepts are central to understanding how GUIbased applications are structured and function.

1. Windows

In GUI terminology, a "window" is a rectangular area on the screen that displays information and
allows user interaction. Each window typically has its own set of controls and can contain various
components.

Characteristics of Windows:

• Title Bar: The top section of a window that usually displays the window's title and includes
controls like minimize, maximize, and close buttons.
• Borders: The edges of the window that define its size and can be resized by dragging.

43
• Menu Bar: An optional horizontal bar at the top of the window that provides access to
various application functions via menus.
• Content Area: The main part of the window where the application's content and controls are
displayed.
• Status Bar: An optional horizontal bar at the bottom of the window that can show status
information or additional controls.

2. Window Components

Window components, also known as "widgets" or "controls," are the interactive elements inside a
window that allow users to interact with the application. These components can be used to gather
input, display information, and execute commands.

Common Window Components:

• Buttons: Allow users to perform actions when clicked. Examples include `Button` in
Tkinter, `QPushButton` in PyQt5, and `Button` in Kivy.

```python

Example in Tkinter

import tkinter as tk

root = tk.Tk()

button = tk.Button(root, text="Click Me")

button.pack()

root.mainloop()

```

• Labels: Display text or images that provide information or instructions to the user.

```python

Example in Tkinter

import tkinter as tk

root = tk.Tk()

label = tk.Label(root, text="Hello, World!")

label.pack()

root.mainloop()

44
• Text Boxes (Entries): Allow users to input text. Examples include `Entry` in Tkinter and
`QLineEdit` in PyQt5.

```python

Example in Tkinter

import tkinter as tk

root = tk.Tk()

entry = tk.Entry(root)

entry.pack()

root.mainloop()

• Check Boxes: Allow users to select or deselect options. Examples include `Checkbutton` in
Tkinter and `QCheckBox` in PyQt5.

```python

Example in Tkinter

import tkinter as tk

root = tk.Tk()

check_var = tk.IntVar()

checkbox = tk.Checkbutton(root, text="Check me", variable=check_var)

checkbox.pack()

root.mainloop()

```

• Radio Buttons: Allow users to select one option from a set of choices. Examples include
`Radiobutton` in Tkinter and `QRadioButton` in PyQt5.

```python

Example in Tkinter

import tkinter as tk

45
root = tk.Tk()

var = tk.StringVar()

radio1 = tk.Radiobutton(root, text="Option 1", variable=var, value="1")

radio2 = tk.Radiobutton(root, text="Option 2", variable=var, value="2")

radio1.pack()

radio2.pack()

root.mainloop()

```

• Sliders: Allow users to select a value from a range. Examples include `Scale` in Tkinter and
`QSlider` in PyQt5.

```python

Example in Tkinter

import tkinter as tk

root = tk.Tk()

slider = tk.Scale(root, from_=0, to=100)

slider.pack()

root.mainloop()

``’

• Menus: Provide a list of options or commands. Examples include `Menu` in Tkinter and
`QMenuBar` in PyQt5.

• Dialogs: Popup windows used for alerting users or gathering additional input. Examples
include `MessageBox` in Tkinter and `QMessageBox` in PyQt5.

Command Buttons and responding to events


Command buttons and responding to events are fundamental concepts in graphical user
interfaces (GUIs). They involve user interaction with buttons and handling the actions that occur
when these interactions take place.
46
Command Buttons

A command button is a GUI element that users can click to trigger an action. It's commonly used
to perform a specific task, such as submitting a form, opening a dialog, or executing a command.

Characteristics of Command Buttons:

• Label: A text or icon displayed on the button that indicates its purpose (e.g., "Submit,"
"Cancel").

• Action: The task or operation that occurs when the button is clicked.

• Style: Can be customized in terms of size, color, and shape to fit the application's design.

Responding to Events

Responding to events involves defining how an application should react when a user performs an
action, such as clicking a button, entering text, or selecting an option. This is usually done by
attaching event handlers or callbacks to GUI components.

Common Events and Handlers:

• Button Click: Triggered when a button is clicked.

• Text Input: Triggered when the user types in a text field.

• Mouse Hover: Triggered when the mouse pointer hovers over a component.

• Key Press: Triggered when a key is pressed on the keyboard.

Unit – 4
Working with python libraries
Working with Python libraries involves using prewritten code modules that extend Python's functionality.
These libraries provide functions, classes, and methods that simplify tasks, enhance productivity, and reduce
the need to write code from scratch. Here's a breakdown of what it typically involves:

1. Installation : Before you can use a library, you usually need to install it. This is often done using package
managers like `pip`. For example, to install the popular library NumPy, you would use:

pip install numpy

2. Importing : Once installed, you need to import the library into your Python script or interactive session.
This makes the library's functionality available for use.

For example:

import numpy as np

47
3. Using Functions and Classes : Libraries come with a variety of functions and classes designed to handle
specific tasks. You use these by calling the appropriate methods or instantiating classes. For example, to
create an array with NumPy, you might use:

```python

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

4. Consulting Documentation : Libraries often come with documentation that explains how to use them,
including descriptions of available functions, classes, and examples. This is essential for understanding how
to implement specific features or solve problems.

5. Leveraging Community Support : Many libraries have active communities and forums where you can
seek help, share knowledge, and collaborate with others. This can be valuable for troubleshooting issues or
learning advanced features.

6. Version Management : Libraries can be updated, and different versions might have different features or
behaviors. Managing these versions can be important, especially in larger projects, to ensure compatibility
and stability.

By working with Python libraries, you can leverage existing solutions to complex problems, making your
code more efficient and allowing you to focus on the unique aspects of your projects.

Numpy
NumPy (Numerical Python) is a fundamental library in Python for numerical and scientific computing. It
provides support for arrays, matrices, and a wide range of mathematical functions to operate on these data
structures efficiently. Here’s an overview of its key features and components:

1. Ndimensional Arrays

• ndarray: The core object of NumPy is the ndarray, which stands for Ndimensional array. It is a
powerful data structure that allows you to store and manipulate large arrays of data.
• Shape and Dimension: Arrays can be onedimensional, twodimensional, or multidimensional, and
they support operations along any axis.

2. Mathematical Operations

• Elementwise Operations: NumPy supports a wide range of mathematical operations that can be
performed elementwise, including addition, subtraction, multiplication, division, and more.
• Universal Functions (ufuncs): These are functions that operate elementwise on arrays, such as
np.sin, np.exp, and np.log.

3. Array Creation and Manipulation

• Creation: Arrays can be created from lists or tuples using functions like np.array(), or initialized
using functions like np.zeros(), np.ones(), and np.arange().
• Reshaping: Arrays can be reshaped using reshape(), and their dimensions can be manipulated.
• Indexing and Slicing: NumPy provides advanced indexing and slicing capabilities, including
boolean indexing and fancy indexing.
48
4. Linear Algebra

• NumPy includes functions for linear algebra operations such as matrix multiplication (np.dot()),
matrix inversion (np.linalg.inv()), eigenvalue decomposition, and solving linear systems.

5. Random Number Generation

• Random Module: The numpy.random module includes functions for generating random numbers,
random sampling, and creating random distributions.

6. Statistical Functions

• NumPy offers functions to perform statistical operations, such as mean, median, standard deviation,
variance, and more.

Nd array
The `ndarray` (ndimensional array) is the core data structure of the NumPy library. It represents a
multidimensional, homogeneous array of fixedsize items. Here's an indepth look at the `ndarray` object:

1. Basic Characteristics

• Homogeneous: All elements in an `ndarray` are of the same type, such as integers, floats, or strings.
• Ndimensional: It can have any number of dimensions (axes). A onedimensional array is like a list, a
twodimensional array resembles a matrix, and higherdimensional arrays can represent more
complex data structures.
• Fixed Size: Once created, the size of an `ndarray` is fixed. You can't change its size without creating
a new array.

2. Creating an `ndarray`

From Lists or Tuples: You can create an `ndarray` from Python lists or tuples.

```python

import numpy as np

arr = np.array([1, 2, 3, 4, 5])

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

```

Using Functions:

NumPy provides several functions to create arrays:

`np.zeros()`: Array filled with zeros.

```python
49
zeros_arr = np.zeros((3, 3))

```

`np.ones()`: Array filled with ones.

```python

ones_arr = np.ones((2, 4))

```

`np.empty()`: Array with uninitialized entries.

```python

empty_arr = np.empty((2, 2))

```

`np.arange()`: Array with a range of values.

```python

range_arr = np.arange(0, 10, 2)

```

`np.linspace()`: Array with evenly spaced values.

```python

linspace_arr = np.linspace(0, 1, 5)

```

`np.eye()`: Identity matrix.

```python

identity = np.eye(3)

```

3. Attributes of `ndarray`

50
`ndarray.shape`: Tuple representing the dimensions of the array.

```python

arr_shape = arr.shape (5,)

matrix_shape = matrix.shape (2, 2)

```

`ndarray.ndim`: Number of dimensions (axes).

```python

num_dims = matrix.ndim 2

```

`ndarray.size`: Total number of elements in the array.

```python

total_elements = matrix.size 4

```

`ndarray.dtype`: Data type of the elements.

```python

data_type = arr.dtype int64 or similar

```

`ndarray.itemsize`: Size (in bytes) of each element.

```python

item_size = arr.itemsize 8 for int64

```

`ndarray.nbytes`: Total number of bytes used by the data portion of the array.

```python

51
total_bytes = arr.nbytes 40 for 5 elements of int64

```

4. Basic Operations

• Indexing and Slicing: Access elements or subsets of the array.

```python

element = matrix[0, 1] 2

sub_matrix = matrix[:1, :2] [[1, 2]]

```

• Elementwise Operations: Perform operations on each element.

```python

squared = arr 2

```

• Aggregation Functions: Compute summary statistics.

```python

sum_arr = np.sum(matrix) 10

mean_arr = np.mean(matrix) 2.5

```

5. Reshaping and Manipulating

`ndarray.reshape()`: Change the shape of the array.

```python

reshaped = matrix.reshape((1, 4)) [[1, 2, 3, 4]]

```

`ndarray.flatten()`: Flatten the array into a onedimensional array.

```python

52
flat_arr = matrix.flatten() [1, 2, 3, 4]

```

`ndarray.transpose()`: Transpose the array (reverse the axes).

```python

transposed = matrix.T [[1, 3], [2, 4]]

```

6. Advanced Features

Broadcasting: Allows operations between arrays of different shapes.

```python

a = np.array([1, 2, 3])

b = np.array([[10], [20], [30]])

result = a + b Broadcasting allows this operation

```

Fancy Indexing: Use arrays of indices to access specific elements.

```python

indices = np.array([0, 2, 4])

selected_elements = arr[indices] [1, 3, 5]

```

Boolean Indexing: Use boolean conditions to filter data.

```python

bool_mask = arr > 3

filtered_arr = arr[bool_mask] [4, 5]

The `ndarray` object is the foundation of NumPy and provides a powerful and efficient way to perform
numerical computations and handle large datasets in Python. Its flexibility, combined with a wide range of
functions, makes it an essential tool for scientific computing and data analysis.

53
Indexing, slicing, and iteration are fundamental techniques for accessing and manipulating data in NumPy
arrays. Here’s a detailed explanation of each:

Indexing, slicing, and iteration


Indexing allows you to access individual elements of an array. In NumPy, indexing is similar to standard
Python indexing but supports more advanced features, such as multidimensional arrays and boolean
indexing.

1. SingleDimensional Arrays

• Basic Indexing: Access elements by position.

```python

import numpy as np

arr = np.array([10, 20, 30, 40, 50])

Accessing the element at index 2

element = arr[2] 30

```

• Negative Indexing: Access elements from the end of the array.

```python

last_element = arr[1] 50

```

2. MultiDimensional Arrays

• Basic Indexing: Access elements in a multidimensional array.

```python

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

Accessing the element at row 1, column 2

54
element = matrix[1, 2] 6

```

• Slicing: Access subarrays or subsets of the array.

```python

Accessing the first two rows and first two columns

sub_matrix = matrix[:2, :2] [[1, 2], [4, 5]]

```

Slicing

Slicing allows you to extract portions of an array. It involves specifying a start, stop, and step value for each
axis.

1. SingleDimensional Arrays

Basic Slicing: Extract a portion of the array.

```python

arr = np.array([10, 20, 30, 40, 50])

Extract elements from index 1 to 3

slice_arr = arr[1:4] [20, 30, 40]

```

Slicing with Steps: Extract elements with a specified step.

```python

step_arr = arr[::2] [10, 30, 50]

```

2. MultiDimensional Arrays

Basic Slicing: Extract portions of a multidimensional array.

```python

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


55
Extract the first two rows and columns

slice_matrix = matrix[:2, :2] [[1, 2], [4, 5]]

```

Slicing with Steps: Apply steps to each dimension.

```python

step_matrix = matrix[::2, ::2] [[1, 3], [7, 9]]

```

Iteration

Iteration allows you to loop through the elements of an array. NumPy provides efficient ways to iterate over
arrays, including using loops and vectorized operations.

1. Iterating Over Elements

Flattened Iteration: Iterate over a flattened (onedimensional) view of the array.

```python

arr = np.array([1, 2, 3, 4, 5])

for element in arr:

print(element)

```

MultiDimensional Iteration: Iterate over each element in a multidimensional array.

```python

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

for row in matrix:

for element in row:

print(element)

```

2. Using `nditer`

56
`np.nditer`: A powerful iterator for multidimensional arrays.

```python

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

for x in np.nditer(matrix):

print(x)

```

3. Vectorized Operations

Vectorized Operations: Instead of using explicit loops, leverage NumPy’s ability to perform operations on
entire arrays at once.

```python

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

Adding 5 to each element using vectorized operations

result = arr + 5 [6, 7, 8, 9]

```

NumPy iterators. Vectorized operations often provide a more efficient alternative to iteration for many tasks.

Array manipulation
In Python, arrays are typically handled using lists , which are a versatile built-in data structure.
Lists allow for a wide variety of operations, including adding, removing, or changing elements, as
well as iterating over them. Lists are similar to arrays in other programming languages, though they
can store items of different data types, not just numbers.

Common Array/List Manipulation Operations in Python

1. Creating a List (Array) :

```python

my_list = [1, 2, 3, 4, 5]

```

2. Accessing Elements :

You can access elements using their index (0-based).

57
```python

print(my_list[0]) # Output: 1

```

3. Appending Elements :

You can add elements to the end of a list using the `append()` method.

```python

my_list.append(6)

print(my_list) # Output: [1, 2, 3, 4, 5, 6]

```

4. Inserting Elements :

Insert an element at a specific index using the `insert()` method.

```python

my_list.insert(2, 10) # Inserts 10 at index 2

print(my_list) # Output: [1, 2, 10, 3, 4, 5]

```

5. Removing Elements :

- Use `remove()` to remove the first occurrence of an element.

```python

my_list.remove(10)

print(my_list) # Output: [1, 2, 3, 4, 5]

```

- Use `pop()` to remove an element by index.

```python

my_list.pop(2)

print(my_list) # Output: [1, 2, 4, 5]

```

6. Slicing Arrays :

Retrieve parts of the list using slicing.

58
```python

sliced_list = my_list[1:3]

print(sliced_list) # Output: [2, 4]

```

7. Reversing a List :

Use the `reverse()` method to reverse the order of elements in the list.

```python

my_list.reverse()

print(my_list) # Output: [5, 4, 2, 1]

```

8. Sorting a List :

Use `sort()` to sort the list in ascending order.

```python

my_list.sort()

print(my_list) # Output: [1, 2, 4, 5]

```

9. List Comprehension :

A compact way to create a list or manipulate an array.

```python

squares = [x 2 for x in my_list]

print(squares) # Output: [1, 4, 16, 25]

```

10. Finding the Length of a List :

You can find the number of elements using `len()`.

```python

print(len(my_list)) # Output: 4

```

59
Pandas
Pandas is a powerful and popular data manipulation and analysis library in Python. It is
widely used for working with structured data, such as tables, datasets, or time series. Pandas is built
on top of NumPy , and it provides two main data structures: Series and DataFrame . These
structures are highly optimized for performance and usability.

Key Features of Pandas:

• DataFrame and Series for easy data manipulation.


• Supports data loading from multiple file formats like CSV, Excel, SQL databases, JSON,
etc.
• Provides tools for data cleaning, filtering, aggregation, and transformation.
• Built-in support for missing data handling.
• Integrated with plotting libraries for data visualization.

Main Data Structures in Pandas:

1. Series :

A Series is a one-dimensional labeled array capable of holding any data type (integers, strings,
floats, etc.). It is like a column in a spreadsheet or a table.

```python

import pandas as pd

# Creating a Series

data = pd.Series([10, 20, 30, 40])

print(data)

```

2. DataFrame :

A DataFrame is a two-dimensional labeled data structure with columns of potentially different


data types. It is similar to a table in a database or an Excel spreadsheet.

```python

# Creating a DataFrame

data = {'Name': ['John', 'Anna', 'Peter', 'Linda'],

'Age': [28, 24, 35, 32],

'City': ['New York', 'Paris', 'Berlin', 'London']}


60
df = pd.DataFrame(data)

print(df)

```

Common Pandas Operations:

1. Reading Data :

You can load data from various file formats like CSV, Excel, or JSON.

```python

df = pd.read_csv('data.csv') # Reading CSV file

df = pd.read_excel('data.xlsx') # Reading Excel file

```

2. Inspecting Data :

View the first few rows, data types, or summary statistics.

```python

print(df.head()) # View the first 5 rows

print(df.info()) # Get info about the DataFrame

print(df.describe()) # Summary statistics of numerical columns

```

3. Selecting Data :

Select specific columns or rows using labels or conditions.

```python

# Selecting a single column

age_column = df['Age']

# Selecting multiple columns

df_subset = df[['Name', 'Age']]

# Selecting rows by condition

adults = df[df['Age'] > 30]

61
```

4. Handling Missing Data :

Pandas provides easy ways to handle missing data, including filling or dropping missing values.

```python

df.fillna(0) # Replace NaN values with 0

df.dropna() # Drop rows with missing values

```

5. Data Cleaning :

Operations like renaming columns, changing data types, or modifying values are straightforward
in Pandas.

```python

# Renaming columns

df.rename(columns={'Age': 'Years'}, inplace=True)

# Changing data type of a column

df['Years'] = df['Years'].astype('int')

```

6. Data Aggregation :

You can perform aggregation functions like sum, mean, or count on data.

```python

# Grouping data by a column and finding the mean

df_grouped = df.groupby('City').mean()

```

7. Merging DataFrames :

Combine two or more DataFrames using merge, join, or concatenation.

```python

df1 = pd.DataFrame({'A': [1, 2], 'B': [3, 4]})

df2 = pd.DataFrame({'A': [1, 2], 'C': [5, 6]})


62
# Merging on column 'A'

merged_df = pd.merge(df1, df2, on='A')

```

8. Plotting :

Pandas integrates well with libraries like Matplotlib for data visualization.

```python

import matplotlib.pyplot as plt

df['Age'].plot(kind='hist') # Plotting a histogram of Age

plt.show()

```

Example:

Let’s put some of these features together in an example:

```python

import pandas as pd

# Creating a DataFrame

data = {'Name': ['Alice', 'Bob', 'Charlie', 'David'],

'Age': [24, 27, 22, 32],

'City': ['New York', 'Los Angeles', 'Chicago', 'Houston']}

df = pd.DataFrame(data)

# Displaying the first few rows

print(df.head())

# Adding a new column

df['Salary'] = [50000, 54000, 49000, 58000]

63
# Filtering rows where Age > 25

filtered_df = df[df['Age'] > 25]

# Grouping by City and calculating mean Salary

city_salary_mean = df.groupby('City')['Salary'].mean()

print(filtered_df)

print(city_salary_mean)

```

Benefits of Pandas:

• Ease of Use : Intuitive syntax for data manipulation.


• Handling Missing Data : Built-in handling of missing data simplifies working with
incomplete datasets.
• Integration : Works well with other Python libraries like NumPy, Matplotlib, and SciPy.
• Pandas is widely used in data science, financial analysis, and machine learning projects due
to its flexibility and power.

The Series
Pandas Series Overview

A Pandas Series is a one-dimensional labeled array capable of holding any type of data (integers,
floats, strings, or even Python objects). It is similar to a column in a table, like those in a
spreadsheet or database. Each element in a Series has a label (index), which helps in referencing
elements by name rather than only by position.

Key Features of a Series:

• Holds any data type: Series can store integers, floats, strings, or even mixed types.

• Labeled Index: Each element in the Series has an associated label (index), which can be
explicitly defined.

• Data Alignment: When performing operations, Series align automatically based on the
index.

Creating a Series

You can create a Series from:

• A Python list

• A NumPy array
64
• A Python dictionary

1. From a list:

Python code

import pandas as pd

data = [10, 20, 30, 40]

series = pd.Series(data)

print(series)

Output:

0 10

1 20

2 30

3 40

dtype: int64

2.From a dictionary:

python

Copy code

data = {'a': 10, 'b': 20, 'c': 30}

series = pd.Series(data)

print(series)

Output:

css

Copy code

a 10

b 20

c 30

dtype: int64

2. With a custom index: You can assign custom labels to the index.

65
python

Copy code

data = [100, 200, 300]

series = pd.Series(data, index=['x', 'y', 'z'])

print(series)

Output:

go

Copy code

x 100

y 200

z 300

dtype: int64

Accessing Elements in a Series

You can access the elements in a Series by:

• Index position (like accessing elements in a list)

• Index label (like accessing values in a dictionary)

1. By position:

python

Copy code

series = pd.Series([10, 20, 30, 40])

print(series[0]) # Output: 10

2. By label:

python

Copy code

series = pd.Series([10, 20, 30], index=['a', 'b', 'c'])

print(series['b']) # Output: 20

Basic Operations on a Series

1. Mathematical operations: Operations are performed element-wise, just like with NumPy
arrays.
66
python

Copy code

series = pd.Series([1, 2, 3, 4])

print(series + 10) # Adds 10 to each element

2. Filtering: You can filter values in a Series using conditions.

python

Copy code

series = pd.Series([1, 2, 3, 4])

filtered_series = series[series > 2]

print(filtered_series)

Output:

go

Copy code

2 3

3 4

dtype: int64

3. Check for missing data: You can easily check and fill missing values in a Series.

python

Copy code

series = pd.Series([1, 2, None, 4])

print(series.isnull()) # Checks for NaN (missing) values

print(series.fillna(0)) # Replaces NaN with 0

4. Label alignment: When performing operations with multiple Series, Pandas aligns them
based on index labels.

python

Copy code

s1 = pd.Series([1, 2, 3], index=['a', 'b', 'c'])

s2 = pd.Series([4, 5, 6], index=['b', 'c', 'd'])

print(s1 + s2)
67
Output:

Copy code

a NaN

b 6.0

c 8.0

d NaN

dtype: float64

The DataFrame
Pandas DataFrame Overview

A Pandas DataFrame is a two-dimensional, size-mutable, and potentially heterogeneous data


structure with labeled axes (rows and columns). It is similar to a table or spreadsheet where each
column can hold different data types (integers, floats, strings, etc.).

DataFrames are the most commonly used data structure in Pandas due to their flexibility and power
for data analysis and manipulation.

Key Features of a DataFrame:

Two-dimensional : Data is organized in rows and columns.

Labeled : Rows and columns can have labels, making it easier to manipulate and analyze data.

Flexible data types : Each column can hold different data types.

Missing data handling : Easily manage missing data with built-in methods.

Creating a DataFrame

You can create a DataFrame from:

- A Python dictionary

- A NumPy array

- Lists

68
- Existing Series

1. From a dictionary of lists:

```python

import pandas as pd

data = {'Name': ['John', 'Anna', 'Peter', 'Linda'],

'Age': [28, 24, 35, 32],

'City': ['New York', 'Paris', 'Berlin', 'London']}

df = pd.DataFrame(data)

print(df)

```

Output:

```

Name Age City

0 John 28 New York

1 Anna 24 Paris

2 Peter 35 Berlin

3 Linda 32 London

```

2. From a list of dictionaries:

```python

data = [{'Name': 'John', 'Age': 28},

{'Name': 'Anna', 'Age': 24},

{'Name': 'Peter', 'Age': 35}]

df = pd.DataFrame(data)

print(df)

69
```

3. From a NumPy array:

```python

import numpy as np

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

df = pd.DataFrame(data, columns=['A', 'B'])

print(df)

```

4. From existing Series:

```python

s1 = pd.Series([1, 2, 3], name='col1')

s2 = pd.Series([4, 5, 6], name='col2')

df = pd.concat([s1, s2], axis=1)

print(df)

```

Accessing Data in a DataFrame

1. Accessing Columns :

You can access columns in a DataFrame like accessing keys in a dictionary.

```python

print(df['Name']) # Accesses the 'Name' column

```

2. Accessing Rows by Index :

70
You can use `iloc` to access rows by their integer position, and `loc` to access rows by their
labels.

```python

# Access the first row

print(df.iloc[0])

# Access rows using a label

print(df.loc[0])

```

3. Accessing a Specific Value :

You can access a specific value using `at` or `iat`:

```python

print(df.at[0, 'Name']) # Get the value at row 0, column 'Name'

print(df.iat[0, 0]) # Get the value at row 0, first column

```

Basic Operations on a DataFrame

1. Adding a New Column :

You can create a new column and assign values.

```python

df['Salary'] = [50000, 54000, 58000, 62000]

print(df)

```

2. Modifying a Column :

Modify an existing column using basic operations or by assigning new values.

```python
71
df['Age'] = df['Age'] + 1 # Add 1 year to each person's age

```

3. Filtering Rows :

Filter rows based on conditions.

```python

filtered_df = df[df['Age'] > 30] # Only include rows where Age > 30

```

4. Dropping Rows or Columns :

You can drop rows or columns using `drop()`.

```python

df = df.drop('Salary', axis=1) # Drop the 'Salary' column

df = df.drop(1, axis=0) # Drop the second row (index 1)

```

5. Renaming Columns :

Rename columns using `rename()`.

```python

df.rename(columns={'Age': 'Years'}, inplace=True)

```

6. Handling Missing Data :

Fill missing values :

```python

df.fillna(0) # Fill missing values with 0

```

Drop missing values :

72
```python

df.dropna() # Drop rows with missing values

```

DataFrame Summary Statistics

Pandas offers many summary statistics methods to provide insight into the dataset:

```python

df = pd.DataFrame({'A': [1, 2, 3, 4], 'B': [4, 3, 2, 1]})

# Basic summary statistics

print(df.describe()) # Summary statistics for numerical columns

# Sum of a column

print(df['A'].sum()) # Output: 10

# Mean of a column

print(df['B'].mean()) # Output: 2.5

```

Grouping Data in a DataFrame

Pandas allows grouping data by one or more columns and applying aggregation functions (like
sum, mean, count, etc.).

```python

# Grouping by 'City' and calculating mean age

df_grouped = df.groupby('City').mean()

print(df_grouped)

```

Merging and Joining DataFrames

Pandas makes it easy to combine multiple DataFrames using:

`merge()` : Similar to SQL joins (inner, left, right, outer joins).


73
```python

df1 = pd.DataFrame({'key': ['A', 'B', 'C'], 'value': [1, 2, 3]})

df2 = pd.DataFrame({'key': ['B', 'C', 'D'], 'value2': [4, 5, 6]})

merged_df = pd.merge(df1, df2, on='key', how='inner')

print(merged_df)

```

`concat()` : Concatenates DataFrames along a particular axis (rows or columns).

```python

df1 = pd.DataFrame({'A': [1, 2], 'B': [3, 4]})

df2 = pd.DataFrame({'A': [5, 6], 'B': [7, 8]})

concatenated_df = pd.concat([df1, df2], axis=0)

print(concatenated_df)

```

Example of Using DataFrame:

```python

import pandas as pd

# Creating a DataFrame

data = {'Name': ['Alice', 'Bob', 'Charlie', 'David'],

'Age': [24, 27, 22, 32],

'City': ['New York', 'Los Angeles', 'Chicago', 'Houston']}

df = pd.DataFrame(data)

# Displaying the first few rows

print(df.head())

# Adding a new column

df['Salary'] = [50000, 54000, 49000, 58000]

74
# Filtering rows where Age > 25

filtered_df = df[df['Age'] > 25]

# Grouping by City and calculating the mean Salary

city_salary_mean = df.groupby('City')['Salary'].mean()

print(filtered_df)

print(city_salary_mean)

```

The Index Objects


Pandas Index Objects

An Index in Pandas is an immutable and ordered data structure that is used to label the axes of a
DataFrame or Series. It plays a crucial role in enabling fast lookups and aligning data when
performing operations across different Series or DataFrames. The Index serves as a "label" for
rows and columns.

There are several types of Index objects in Pandas, such as:

• `Index`: The generic index object.


• `RangeIndex`: A memory-efficient index for a range of integers.
• `MultiIndex`: A hierarchical index for multi-level indexing.
• `DatetimeIndex`: An index of timestamps.

Key Features of Index Objects:

• Immutable : Once created, the values in the Index cannot be modified, which allows Pandas
to optimize operations.
• Labeling : Index objects provide labels for rows and columns in DataFrames and Series.
• Supports operations : Index objects support many operations such as intersection, union,
and difference.
• Alignment : Pandas automatically aligns data along the index when performing operations
on DataFrames or Series.

75
Creating an Index

When you create a Pandas DataFrame or Series, the index is automatically created. You can also
create a custom Index explicitly.

1. Default Index :

If no index is specified, Pandas automatically creates a default integer index (starting from 0).

```python

import pandas as pd

data = {'Name': ['Alice', 'Bob', 'Charlie'], 'Age': [25, 30, 35]}

df = pd.DataFrame(data)

print(df.index)

```

Output:

```

RangeIndex(start=0, stop=3, step=1)

```

2. Custom Index :

You can create a DataFrame or Series with a custom index.

```python

df = pd.DataFrame(data, index=['a', 'b', 'c'])

print(df)

print(df.index)

```

Output:

```

Name Age

a Alice 25
76
b Bob 30

c Charlie 35

Index(['a', 'b', 'c'], dtype='object')

```

3. Explicitly Creating an Index :

You can create an Index object directly using the `pd.Index()` constructor.

```python

idx = pd.Index([10, 20, 30], name='my_index')

print(idx)

```

Output:

```

Int64Index([10, 20, 30], dtype='int64', name='my_index')

```

Common Operations on Index

1. Accessing Index Labels :

You can access the labels of the Index.

```python

df = pd.DataFrame(data, index=['a', 'b', 'c'])

print(df.index[0]) # Access the first index label ('a')

```

2. Reindexing :

Reindexing changes the index of the DataFrame or Series to match a new set of labels. If a label
is not found in the original data, it results in missing values.

```python

new_index = ['a', 'b', 'd']

reindexed_df = df.reindex(new_index)
77
print(reindexed_df)

```

Output:

```

Name Age

a Alice 25.0

b Bob 30.0

d NaN NaN

```

3. Indexing and Slicing :

Just like Series and DataFrames, Index objects can be sliced.

```python

print(df.index[:2]) # Access the first two index labels

```

4. Checking Existence :

You can check whether a label exists in the index.

```python

print('a' in df.index) # Output: True

print('d' in df.index) # Output: False

```

5. Set Operations :

Index objects support set operations like union, intersection, and difference.

```python

idx1 = pd.Index([1, 2, 3, 4])

idx2 = pd.Index([3, 4, 5, 6])

78
# Union of two indexes

print(idx1.union(idx2)) # Output: Int64Index([1, 2, 3, 4, 5, 6])

# Intersection of two indexes

print(idx1.intersection(idx2)) # Output: Int64Index([3, 4])

# Difference between two indexes

print(idx1.difference(idx2)) # Output: Int64Index([1, 2])

```

Types of Indexes

1. RangeIndex :

A RangeIndex is a memory-efficient index for representing a range of integers.

```python

df = pd.DataFrame({'A': [1, 2, 3]})

print(df.index) # Output: RangeIndex(start=0, stop=3, step=1)

```

2. MultiIndex :

MultiIndex allows hierarchical indexing, meaning you can have multiple levels of indexes.

```python

arrays = [['first', 'first', 'second', 'second'], ['one', 'two', 'one', 'two']]

idx = pd.MultiIndex.from_arrays(arrays, names=('Category', 'Number'))

df = pd.DataFrame({'Value': [10, 20, 30, 40]}, index=idx)

print(df)

```

Output:

```

Value

79
Category Number

first one 10

two 20

second one 30

two 40

```

3. DatetimeIndex :

A DatetimeIndex is used when the index contains timestamps or time-related data.

```python

dates = pd.date_range('2024-01-01', periods=4, freq='D')

df = pd.DataFrame({'Value': [1, 2, 3, 4]}, index=dates)

print(df.index)

```

Output:

```

DatetimeIndex(['2024-01-01', '2024-01-02', '2024-01-03', '2024-01-04'], dtype='datetime64[ns]',


freq='D')

```

Modifying Indexes

Even though Index objects are immutable, you can replace the entire Index in a DataFrame or
Series.

1. Resetting the Index :

Resetting the index removes the current index and replaces it with a default integer index.

```python

df_reset = df.reset_index()

print(df_reset)

```

80
2. Setting a New Index :

You can set a new index for a DataFrame.

```python

df.set_index('Name', inplace=True)

print(df)

```

Example:

```python

import pandas as pd

# Creating a DataFrame with a custom index

data = {'Name': ['John', 'Anna', 'Peter'], 'Age': [28, 24, 35]}

df = pd.DataFrame(data, index=['a', 'b', 'c'])

print("Original DataFrame:")

print(df)

# Accessing the index

print("\nIndex:")

print(df.index)

# Resetting the index

df_reset = df.reset_index()

print("\nDataFrame after resetting the index:")

print(df_reset)

# Setting a new index

df_new_index = df.set_index('Name')

81
print("\nDataFrame with 'Name' as index:")

print(df_new_index)

```

Key Takeaways:

Index objects are immutable and provide labels for rows and columns in Pandas data structures.

You can perform set operations (like union, intersection) on Index objects, as well as reindex
DataFrames or Series to match a new set of labels.

Different types of indexes (like RangeIndex , MultiIndex , and DatetimeIndex ) provide


flexibility depending on the nature of your data.

Indexes are crucial for aligning data during operations across multiple Series and DataFrames.

Matplotlib
Matplotlib is a widely-used Python library for creating static, animated, and interactive
visualizations. It provides a comprehensive range of plotting functionalities, making it a popular
choice for data visualization. The library is flexible, allowing users to create publication-quality
figures in a variety of formats, from simple line plots to complex multi-plot figures.

The core component of Matplotlib is `pyplot` , a module within the library that provides an
interface similar to MATLAB for creating plots.

Key Features:

• 2D plotting : Supports various plots like line, bar, scatter, histogram, etc.
• Customization : You can fully customize your plots (colors, labels, legends, etc.).
• Integration : Works well with other libraries like Pandas and NumPy.
• Interactive : Supports interactive plots in Jupyter notebooks.

Installation

If you don’t have Matplotlib installed, you can install it via `pip`:

```bash

pip install matplotlib

```

Basic Plotting with `pyplot`

The most common way to use Matplotlib is through its `pyplot` module, which provides functions
for creating and customizing different types of plots.
82
1. Simple Line Plot :

```python

import matplotlib.pyplot as plt

# Data

x = [1, 2, 3, 4]

y = [1, 4, 9, 16]

# Creating the plot

plt.plot(x, y)

# Adding labels and title

plt.xlabel('x-axis')

plt.ylabel('y-axis')

plt.title('Simple Line Plot')

# Displaying the plot

plt.show()

```

2. Customizing the Line :

You can customize the line style, color, and marker:

```python

plt.plot(x, y, color='red', linestyle='--', marker='o')

plt.show()

```

3. Multiple Lines in One Plot :

You can plot multiple lines in the same figure:

```python

83
x = [1, 2, 3, 4]

y1 = [1, 4, 9, 16]

y2 = [1, 2, 3, 4]

plt.plot(x, y1, label="y1 = x^2")

plt.plot(x, y2, label="y2 = x")

# Adding legend

plt.legend()

plt.show()

```

Types of Plots

1. Bar Plot :

Bar plots are useful for categorical data and comparisons.

```python

categories = ['A', 'B', 'C', 'D']

values = [3, 7, 8, 5]

plt.bar(categories, values)

plt.title('Bar Plot Example')

plt.show()

```

2. Scatter Plot :

Scatter plots show the relationship between two numerical variables.

```python

x = [1, 2, 3, 4, 5]

y = [5, 7, 8, 6, 9]

84
plt.scatter(x, y, color='green')

plt.title('Scatter Plot Example')

plt.show()

```

3. Histogram :

Histograms are useful for displaying the distribution of a dataset.

```python

data = [1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5]

plt.hist(data, bins=5, color='purple')

plt.title('Histogram Example')

plt.show()

```

4. Pie Chart :

Pie charts are used to represent parts of a whole.

```python

labels = ['Apples', 'Bananas', 'Cherries', 'Dates']

sizes = [15, 30, 45, 10]

plt.pie(sizes, labels=labels, autopct='%1.1f%%', startangle=140)

plt.title('Pie Chart Example')

plt.show()

```

Subplots

You can create multiple plots in a single figure using subplots.

```python

# Creating 2x2 subplots

85
fig, axs = plt.subplots(2, 2)

# Top-left subplot

axs[0, 0].plot(x, y1, 'r')

axs[0, 0].set_title('Plot 1')

# Top-right subplot

axs[0, 1].bar(categories, values)

axs[0, 1].set_title('Plot 2')

# Bottom-left subplot

axs[1, 0].scatter(x, y2)

axs[1, 0].set_title('Plot 3')

# Bottom-right subplot

axs[1, 1].hist(data)

axs[1, 1].set_title('Plot 4')

plt.tight_layout()

plt.show()

```

Customizing Plots

1. Titles, Labels, and Legends :

```python

plt.plot(x, y)

plt.title('Customized Plot')

plt.xlabel('X-axis')

plt.ylabel('Y-axis')

86
plt.legend(['y = x^2'])

plt.show()

```

2. Changing Figure Size :

You can change the size of your figure.

```python

plt.figure(figsize=(8, 6))

plt.plot(x, y)

plt.show()

```

3. Adding Grid :

You can add a grid to your plot.

```python

plt.plot(x, y)

plt.grid(True)

plt.show()

```

Working with Images

Matplotlib can also be used to display and manipulate images.

1. Displaying an Image :

```python

import matplotlib.image as mpimg

img = mpimg.imread('your_image.png')

plt.imshow(img)

87
plt.axis('off') # Hides the axes

plt.show()

```

2. Saving Plots :

You can save plots in various formats (e.g., PNG, JPG, PDF).

```python

plt.plot(x, y)

plt.savefig('my_plot.png')

```

Example: Plotting a Pandas DataFrame

Matplotlib integrates well with Pandas . You can plot data directly from DataFrames.

```python

import pandas as pd

# Creating a DataFrame

data = {'Month': ['Jan', 'Feb', 'Mar', 'Apr'],

'Sales': [200, 240, 300, 260]}

df = pd.DataFrame(data)

# Plotting

plt.plot(df['Month'], df['Sales'])

plt.xlabel('Month')

plt.ylabel('Sales')

plt.title('Sales per Month')

plt.show()

```

Key Takeaways:

Matplotlib is a versatile library that allows you to create a wide variety of visualizations. You can
customize virtually every aspect of a plot: size, colors, labels, and more.
88
`pyplot` provides a MATLAB-like interface for quickly creating basic plots.It integrates
seamlessly with NumPy and Pandas for working with numerical data and DataFrames.

Data visualization with Matplotlib


Data visualization is essential for understanding complex datasets, discovering patterns, trends,
and relationships, and communicating insights effectively. Matplotlib is one of the most powerful
and flexible Python libraries for creating a wide variety of data visualizations.

With Matplotlib, you can create visualizations ranging from simple line plots to complex, multi-
plot grids. Here's a deeper dive into data visualization with Matplotlib, including examples and tips
for making effective and clear visualizations.

1. Basic Plotting in Matplotlib

Line Plot :

One of the simplest plots in Matplotlib is the line plot, used to visualize relationships between
variables.

```python

import matplotlib.pyplot as plt

# Data

x = [1, 2, 3, 4]

y = [2, 4, 6, 8]

# Plotting the line graph

plt.plot(x, y, label="y = 2x", color="blue")

# Adding title, labels, and legend

plt.title("Simple Line Plot")

plt.xlabel("X-axis")

plt.ylabel("Y-axis")

plt.legend()

# Display the plot

plt.show()

```

Key Customizations :

`plt.xlabel()` and `plt.ylabel()` add labels to the axes.

89
`plt.title()` adds a title.

`plt.legend()` adds a legend to describe plotted lines.

2. Bar Plot

Bar plots are useful for comparing different categories of data. You can create vertical or horizontal
bar plots with `plt.bar()`.

```python

categories = ['Category A', 'Category B', 'Category C']

values = [10, 24, 36]

plt.bar(categories, values, color='green')

plt.title('Bar Plot Example')

plt.xlabel('Categories')

plt.ylabel('Values')

plt.show()

```

Horizontal Bar Plot :

```python

plt.barh(categories, values, color='orange')

plt.title('Horizontal Bar Plot Example')

plt.xlabel('Values')

plt.ylabel('Categories')

plt.show()

```

Grouped Bar Plot :

If you want to compare multiple sets of data, you can create grouped bar plots.

```python

import numpy as np

90
# Data

categories = ['A', 'B', 'C']

values1 = [10, 15, 20]

values2 = [12, 18, 22]

# Position of bars

x = np.arange(len(categories))

# Plotting

plt.bar(x - 0.2, values1, width=0.4, label='Set 1')

plt.bar(x + 0.2, values2, width=0.4, label='Set 2')

plt.xticks(x, categories)

plt.xlabel('Categories')

plt.ylabel('Values')

plt.title('Grouped Bar Plot')

plt.legend()

plt.show()

```

3. Scatter Plot

Scatter plots are ideal for visualizing relationships between two numerical variables.

```python

# Data

x = [1, 2, 3, 4, 5, 6]

y = [2, 4, 5, 7, 6, 9]

plt.scatter(x, y, color='purple', marker='o')

91
plt.title('Scatter Plot Example')

plt.xlabel('X-axis')

plt.ylabel('Y-axis')

plt.show()

```

Customization :

You can adjust marker size, shape, and color to reflect additional data dimensions:

```python

sizes = [50, 100, 150, 200, 250, 300]

colors = [10, 20, 30, 40, 50, 60]

plt.scatter(x, y, s=sizes, c=colors, cmap='viridis', alpha=0.8)

plt.colorbar() # Add a color scale

plt.title('Customized Scatter Plot')

plt.show()

```

4. Histogram

Histograms are great for visualizing the distribution of numerical data.

```python

data = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5]

plt.hist(data, bins=5, color='blue', edgecolor='black')

plt.title('Histogram Example')

plt.xlabel('Value')

plt.ylabel('Frequency')

plt.show()

```

92
You can adjust the number of bins and other aspects of the histogram to better visualize your data
distribution.

5. Pie Chart

Pie charts display proportions or percentages of categories. Use them when you want to visualize
parts of a whole.

```python

labels = ['Apples', 'Bananas', 'Cherries', 'Dates']

sizes = [15, 30, 45, 10]

plt.pie(sizes, labels=labels, autopct='%1.1f%%', startangle=90)

plt.title('Pie Chart Example')

plt.show()

```

Exploding Slices:

```python

explode = (0, 0.1, 0, 0) # Explode the second slice

plt.pie(sizes, labels=labels, explode=explode, autopct='%1.1f%%', startangle=140)

plt.title('Pie Chart with Exploding Slice')

plt.show()

```

6. Box Plot

Box plots (also known as whisker plots) are useful for understanding the distribution of data,
including median, quartiles, and outliers.

```python

data = [7, 8, 5, 4, 9, 7, 6, 8, 5, 6, 9, 6, 5, 8, 7]

plt.boxplot(data)

plt.title('Box Plot Example')

plt.show()

93
```

Box plots are especially useful for comparing distributions across multiple groups.

7. Heatmap

Heatmaps are ideal for visualizing correlations or relationships between two variables.

```python

import numpy as np

# Data

data = np.random.rand(5, 5)

plt.imshow(data, cmap='hot', interpolation='nearest')

plt.title('Heatmap Example')

plt.colorbar()

plt.show()

```

Heatmaps can be enhanced by adding tick labels and adjusting the colormap.

8. Subplots

Matplotlib allows for the creation of multiple plots (subplots) in a single figure using the
`plt.subplots()` function.

```python

fig, axs = plt.subplots(2, 2, figsize=(10, 8))

# First subplot

axs[0, 0].plot(x, y, color='red')

axs[0, 0].set_title('Line Plot')

# Second subplot

axs[0, 1].scatter(x, y, color='green')

axs[0, 1].set_title('Scatter Plot')

94
# Third subplot

axs[1, 0].bar(categories, values, color='blue')

axs[1, 0].set_title('Bar Plot')

# Fourth subplot

axs[1, 1].hist(data, bins=5, color='orange')

axs[1, 1].set_title('Histogram')

# Adjust layout

plt.tight_layout()

plt.show()

```

9. Customizing Plots

Matplotlib provides a wide range of options for customizing plots to make them more readable and
visually appealing.

1. Titles, Labels, and Legends :

`plt.title()` to set a title for the plot.

`plt.xlabel()` and `plt.ylabel()` to label the axes.

`plt.legend()` to add a legend to the plot.

2. Grid :

Add a grid to make reading values easier:

```python

plt.plot(x, y)

plt.grid(True)

plt.show()

```

3. Changing Figure Size :

Adjust the size of the figure:

```python

plt.figure(figsize=(8, 6))
95
plt.plot(x, y)

plt.show()

```

4. Saving Plots :

Save plots to files in different formats (PNG, PDF, etc.):

```python

plt.savefig('my_plot.png', dpi=300)

```

10. Visualizing Data from Pandas

You can use Matplotlib to visualize data directly from a Pandas DataFrame.

```python

import pandas as pd

# Sample DataFrame

data = {'A': [1, 2, 3, 4], 'B': [2, 4, 6, 8], 'C': [1, 3, 5, 7]}

df = pd.DataFrame(data)

# Plotting with Pandas

df.plot(kind='bar', figsize=(8, 6))

plt.title('Bar Plot from Pandas DataFrame')

plt.show()

```

Best Practices for Effective Data Visualization

• Clarity : Always ensure that your plots convey the message clearly. Labels, titles, and
legends are crucial.
• Simplicity : Avoid clutter. Minimal, clean plots are easier to understand.
• Consistency : Use consistent colors, labels, and scaling across multiple plots for
comparisons.

96
• Context : Provide context for your visualizations, such as appropriate labels, units, and
legends, so that viewers can interpret them correctly.

The Matplotlib Architecture


The architecture of Matplotlib consists of several layers that work together to generate high-
quality visualizations. Understanding this architecture is key to mastering the customization and
flexibility that Matplotlib offers.

Here’s a breakdown of the major components and how they interact:

1. Scripting Layer (`pyplot`)

The scripting layer is the most common interface for users of Matplotlib, provided by the
`matplotlib.pyplot` module. It offers a simple interface for creating and customizing plots, very
similar to MATLAB's plotting interface.

• Purpose : To simplify the process of creating plots and manage figure and axis creation, as
well as plot elements (like lines, bars, etc.).
• Functions : This layer contains most of the commonly used functions, like `plt.plot()`,
`plt.bar()`, `plt.show()`, etc.

Example:

```python

import matplotlib.pyplot as plt

plt.plot([1, 2, 3, 4], [1, 4, 9, 16])

plt.show()

```

Key features:

• Implicit : `pyplot` keeps track of the current figure and axes, making it easy to plot without
explicitly managing objects.
• State Machine : It works as a state machine, meaning that it keeps track of what the user is
doing with the plot. It automatically creates figures, adds elements like labels, and finally
displays them.

2. Artist Layer :The Artist Layer is a more detailed and explicit part of the architecture that
handles all of the elements that are seen on the figure (lines, text, axes, legends, etc.). In Matplotlib,
everything visible in a plot is an Artist .

97
Artists : These are the building blocks of any plot. They represent anything that can be drawn on a
figure (e.g., lines, text, tick marks, etc.).

Types of Artists :

1. Primitive Artists : Basic components like lines (`Line2D`), rectangles (`Rectangle`), and
circles (`Circle`).
2. Composite Artists : Collections of primitives that together form higher-level objects, such
as Axes , Ticks , or Legends .

There are two main types of artists:

Containers : These hold other artists (e.g., `Figure`, `Axes`, `Axis`).

Drawables : These are elements that actually get drawn (e.g., `Line2D`, `Text`, `Patch`).

Example of using Artists directly:

```python

import matplotlib.pyplot as plt

from matplotlib.lines import Line2D

# Creating a figure and axes

fig, ax = plt.subplots()

# Creating a Line2D artist and adding it to the Axes

line = Line2D([0, 1], [0, 1], linewidth=2, color='blue')

ax.add_line(line)

plt.show()

3. Backend Layer

The Backend Layer is responsible for rendering the plots and handling how the visual output is
displayed or saved. The backend layer can be divided into three types :

1. User Interface (UI) Backends : Used for displaying plots interactively within a graphical user
interface (GUI) framework, such as Tkinter, Qt, or GTK. These are interactive and allow zooming,
panning, and updating in real-time.

Common GUI backends:

• `TkAgg` for Tkinter,


98
• `Qt5Agg` for PyQt or PySide,
• `GTK3Agg` for GTK.

2. Hardcopy Backends : Used for saving figures to a file, such as PNG, PDF, SVG, etc.

Examples: `Agg` (PNG), `PDF` (PDF), `PS` (PostScript), `SVG` (SVG).

3. Headless Backends : Used when you need to create plots programmatically without displaying
them (e.g., in scripts or on servers). These are typically useful for generating plots in batch mode
without a display.

Example: `Agg`.

You can explicitly specify a backend using the `matplotlib.use()` function:

```python

import matplotlib

matplotlib.use('Agg') # For non-interactive (headless) backend

import matplotlib.pyplot as plt

```

The backends determine how Matplotlib interacts with different output devices (like screens or
files). They handle the process of taking your high-level plotting commands and transforming them
into visual graphics.

4. Object-Oriented API (OO API)

The Object-Oriented API in Matplotlib is a more explicit way to create and manage plots. Instead
of relying on the state-based `pyplot`, you directly manage `Figure`, `Axes`, and `Artist` objects.
This approach is more flexible and is recommended for complex plots and when embedding
Matplotlib in applications.

`Figure` Object : Represents the entire figure, including all the plots, text, labels, and more.

`Axes` Object : Represents an individual plot in the figure (e.g., an individual subplot).

`Axis` Object : Represents the x-axis or y-axis of a plot.

This method gives you finer control over every aspect of your plot and is highly customizable.

Example of the OO API:

```python

import matplotlib.pyplot as plt

99
# Create a Figure and Axes

fig, ax = plt.subplots()

# Plot using the Axes object

ax.plot([1, 2, 3, 4], [1, 4, 9, 16])

# Set properties using the Axes object

ax.set_title('Object-Oriented API Plot')

ax.set_xlabel('X Label')

ax.set_ylabel('Y Label')

plt.show()

```

Key advantage:

Explicit Control : You have direct access to `Figure` and `Axes` objects, which can be customized
individually. This is particularly useful for multi-plot figures or when embedding plots in other
environments (e.g., web applications, GUI apps).

5. Figures, Axes, and Axis Hierarchy

The hierarchy of Matplotlib objects is crucial for understanding how plots are structured:

1. Figure :

The top-level container for everything in the plot. It represents the entire canvas or window that
holds one or more `Axes`. You can have multiple `Axes` (subplots) within a single `Figure`.

Example:

```python

fig = plt.figure()

```

2. Axes :

- Each `Axes` represents an individual plot, with its own x-axis and y-axis. A `Figure` can
contain multiple `Axes`, making it possible to create subplots.

100
Example:

```python

ax = fig.add_subplot(111)

```

3. Axis :

- Represents the x-axis or y-axis in an `Axes` object. It includes ticks, labels, and limits.

Example:

```python

ax.xaxis.set_label_text('X-axis Label')

```

These objects form a hierarchical structure, with the `Figure` at the top, containing one or more
`Axes`, which in turn contain `Axis`, `Lines`, `Text`, and other `Artists`.

---

6. Rendering Pipeline

Matplotlib's architecture includes a rendering pipeline that determines how plots are transformed
into actual images (either displayed on screen or saved to a file). The key steps are:

1. Data is passed to `pyplot` or the Object-Oriented API.

2. Artists (lines, patches, text, etc.) are created and added to `Axes`.

3. The backend determines how to render the figure (either on-screen via a UI backend or saved
via a hardcopy backend).

4. The plot is rendered as an image or graphical output.

---

101
Summary of Matplotlib Architecture:

1. Scripting Layer (`pyplot`) : The user-friendly, state-based interface for creating plots.

2. Artist Layer : Handles all elements that are drawn in the figure (lines, texts, axes, etc.).

3. Backend Layer : Handles the rendering of figures to different output devices (interactive display
or file formats).

4. Object-Oriented API : Provides a more explicit and flexible way to create and customize plots
by managing `Figure`, `Axes`, and `Artist` objects directly.

5. Hierarchy : Matplotlib plots are structured in a hierarchy: `Figure` → `Axes` → `Axis` →


`Artists`.

Pyplot
Matplotlib pyplot Overview

The pyplot module in Matplotlib provides a simple interface for creating plots in a MATLAB-like
style. It is designed to simplify the process of generating plots by managing figures, axes, and other
plot elements for you in the background. It's a state-machine based interface, meaning it keeps
track of the current figure and axes and applies your plotting commands to them.

Key Features of pyplot

1. Implicit Figure and Axes Handling: When you call a function like plt.plot(), pyplot
automatically manages the creation of the figure and axes if they don't exist yet.

2. State-Based Interface: It allows you to call a series of functions like plt.title(), plt.xlabel(),
and plt.ylabel() to modify the current figure or plot without needing to explicitly reference
the figure or axes objects.

3. Simple Syntax: It provides an easy-to-use syntax for creating a wide variety of plots with
just a few lines of code.

Basic Usage

Here’s how you can create a simple plot using pyplot:

python

Copy code

import matplotlib.pyplot as plt

# Data
102
x = [1, 2, 3, 4]

y = [1, 4, 9, 16]

# Create a plot

plt.plot(x, y)

# Adding labels and title

plt.xlabel('X Label')

plt.ylabel('Y Label')

plt.title('Simple Line Plot')

# Show the plot

plt.show()

This example shows the basic usage of pyplot to generate a line plot.

Common pyplot Functions

1. plt.plot(): Used to plot lines or markers between points.

python

Copy code

plt.plot(x, y, label='Line')

2. plt.scatter(): Creates a scatter plot with individual points.

python

Copy code

plt.scatter(x, y)

3. plt.bar() and plt.barh(): Create vertical and horizontal bar plots, respectively.

python

Copy code

plt.bar(categories, values)

4. plt.hist(): Creates a histogram to show the distribution of a dataset.

103
python

Copy code

plt.hist(data, bins=10)

5. plt.pie(): Creates a pie chart to visualize proportions.

python

Copy code

plt.pie(sizes, labels=labels, autopct='%1.1f%%')

6. plt.title(), plt.xlabel(), and plt.ylabel(): Add a title and labels to the x-axis and y-axis.

python

Copy code

plt.title('Plot Title')

plt.xlabel('X Axis Label')

plt.ylabel('Y Axis Label')

7. plt.legend(): Adds a legend to the plot.

python

Copy code

plt.legend()

8. plt.savefig(): Saves the current figure to a file (e.g., PNG, PDF).

python

Copy code

plt.savefig('plot.png')

9. plt.show(): Displays the plot on the screen. This is often the final command to show the
current figure in a graphical window.

python

Copy code

plt.show()

Plot Customizations

Matplotlib’s pyplot interface provides extensive customization options to improve the clarity and
aesthetics of your plots.

104
1. Setting Line Style and Color: You can control the style and appearance of lines by using
arguments like color, linewidth, and linestyle.

python

Copy code

plt.plot(x, y, color='red', linewidth=2, linestyle='--')

2. Markers: Customize markers for data points with options like marker, markersize, and
markerfacecolor.

python

Copy code

plt.plot(x, y, marker='o', markersize=8, markerfacecolor='blue')

3. Grid: Add a grid to the plot for better readability.

python

Copy code

plt.grid(True)

4. Subplots: Create multiple plots in the same figure using plt.subplot().

python

Copy code

plt.subplot(2, 1, 1) # Two rows, one column, first plot

plt.plot(x, y)

plt.subplot(2, 1, 2) # Two rows, one column, second plot

plt.plot(y, x)

The plotting window


The Plotting Window in Matplotlib

The plotting window in Matplotlib refers to the graphical user interface (GUI) window or environment
where your plots are displayed. This window is managed by Matplotlib's backends, which handle the
rendering and interaction of plots.

105
Here’s an overview of how the plotting window works and how you can manage it:

1. Displaying Plots

By default, Matplotlib uses an interactive backend that displays plots in a new window. This is managed by
the `plt.show()` function, which is called at the end of your plotting script to render and display the current
figure.

```python

import matplotlib.pyplot as plt

# Create a plot

plt.plot([1, 2, 3, 4], [1, 4, 9, 16])

# Display the plot

plt.show()

```

2. Plotting Backends

Matplotlib supports various backends that determine how and where the plotting window is displayed. The
choice of backend can affect how you interact with the plotting window, such as whether you can zoom,
pan, or save the plot directly.

# Interactive Backends

TkAgg : Uses Tkinter as the GUI toolkit.

Qt5Agg : Uses Qt5 (PyQt5 or PySide2) as the GUI toolkit.

GTK3Agg : Uses GTK3 for GUI interactions.

WXAgg : Uses wxWidgets as the GUI toolkit.

106
# Non-Interactive Backends

Agg : A backend for creating image files (e.g., PNG). It does not support interactive plotting.

PDF : Creates PDF files of the plots.

SVG : Creates SVG files for vector graphics.

You can specify the backend to use with `matplotlib.use()` before importing `pyplot`:

```python

import matplotlib

matplotlib.use('Qt5Agg') # For example, to use Qt5 backend

import matplotlib.pyplot as plt

```

# Checking the Current Backend

To check the current backend, you can use:

```python

import matplotlib

print(matplotlib.get_backend())

```

3. Interacting with the Plotting Window

The interactive backends allow you to interact with your plot:

Zoom : Click and drag to zoom in on a region of the plot.

107
Pan : Click and drag to move the plot around.

Save : Use a save button or menu option to save the plot as an image or other file format.

4. Figure and Axes Management

In the plotting window, you manage the `Figure` and `Axes` objects:

Figure : The entire plotting window or canvas where all the plots are drawn.

Axes : The area within the figure where individual plots are created. You can have multiple axes in a figure,
allowing for subplots.

Example of creating multiple figures and managing their plotting windows:

```python

import matplotlib.pyplot as plt

# Create first figure

fig1 = plt.figure()

plt.plot([1, 2, 3], [4, 5, 6])

plt.title('First Figure')

plt.show()

# Create second figure

fig2 = plt.figure()

plt.plot([1, 2, 3], [6, 5, 4])

plt.title('Second Figure')

plt.show()

```

108
Each call to `plt.show()` will display the plots in separate windows (if you’re using an interactive backend).

5. Saving Figures

You can save the figure to a file using `plt.savefig()`. This saves the current figure to a file without
displaying it in a window.

```python

import matplotlib.pyplot as plt

# Create a plot

plt.plot([1, 2, 3, 4], [1, 4, 9, 16])

# Save the figure

plt.savefig('plot.png') # Save as PNG file

```

6. Adjusting Plot Appearance

You can adjust the appearance and layout of the plotting window using Matplotlib functions:

`plt.tight_layout()` : Automatically adjusts subplot parameters to give specified padding.

`plt.subplots_adjust()` : Allows manual adjustment of subplot parameters like spacing.

Example of adjusting layout:

```python

import matplotlib.pyplot as plt

109
# Create multiple subplots

fig, axs = plt.subplots(2, 2, figsize=(10, 8))

# Adjust layout

plt.tight_layout()

plt.show()

```

7. Embedding Plots in Applications

Matplotlib plots can be embedded in various applications:

Jupyter Notebooks : Use `%matplotlib inline` to display plots inline within the notebook.

GUI Applications : Use Matplotlib backends like `Qt5Agg` or `TkAgg` to integrate plots into applications
built with GUI frameworks.

Example for Jupyter Notebook:

```python

%matplotlib inline

import matplotlib.pyplot as plt

# Create a plot

plt.plot([1, 2, 3, 4], [1, 4, 9, 16])

plt.show()

```

110
Adding Elements to the Chart
Adding Elements to a Matplotlib ChartMatplotlib provides a variety of methods to add elements to your
charts, including text, annotations, legends, grids, and more. Here’s how you can enhance your charts with
different elements:

---

1. Adding Titles and Labels

Title : Adds a title to the entire plot.

X-axis Label : Labels the x-axis.

Y-axis Label : Labels the y-axis.

```python

import matplotlib.pyplot as plt

x = [1, 2, 3, 4]

y = [1, 4, 9, 16]

plt.plot(x, y)

plt.title('Plot Title')

plt.xlabel('X Axis Label')

plt.ylabel('Y Axis Label')

plt.show()

```

---
111
2. Adding Legends

Legends help identify different plots within the same figure. You can add a legend by using the `label`
parameter in plotting functions and then calling `plt.legend()`.

```python

import matplotlib.pyplot as plt

x = [1, 2, 3, 4]

y1 = [1, 4, 9, 16]

y2 = [1, 2, 3, 4]

plt.plot(x, y1, label='y = x^2')

plt.plot(x, y2, label='y = x')

plt.legend() # Add a legend

plt.show()

```

---

3. Adding Annotations

Annotations can highlight specific data points or areas in your plot. You can use `plt.annotate()` to add text
or arrows.

```python

import matplotlib.pyplot as plt

112
x = [1, 2, 3, 4]

y = [1, 4, 9, 16]

plt.plot(x, y)

plt.annotate('Highest point', xy=(4, 16), xytext=(3, 20),

arrowprops=dict(facecolor='black', shrink=0.05))

plt.show()

```

---

4. Adding Grid Lines

Grid lines can make it easier to read values from your plot. Use `plt.grid()` to add grid lines.

```python

import matplotlib.pyplot as plt

x = [1, 2, 3, 4]

y = [1, 4, 9, 16]

plt.plot(x, y)

plt.grid(True) # Add grid lines

plt.show()

113
```

---

5. Adding Text

You can add text anywhere on the plot using `plt.text()`. This is useful for displaying additional information
or notes.

```python

import matplotlib.pyplot as plt

x = [1, 2, 3, 4]

y = [1, 4, 9, 16]

plt.plot(x, y)

plt.text(2, 10, 'Quadratic Growth', fontsize=12, color='red')

plt.show()

```

---

6. Adding Markers

Markers can be added to data points to make them more distinct. Use the `marker` parameter in `plt.plot()`
or `plt.scatter()`.

```python

114
import matplotlib.pyplot as plt

x = [1, 2, 3, 4]

y = [1, 4, 9, 16]

plt.plot(x, y, marker='o', linestyle='-', color='b')

plt.show()

```

---

7. Adding Multiple Plots

You can create multiple plots within the same figure using `plt.subplot()` or `plt.subplots()`. This is useful
for comparing different datasets side by side.

```python

import matplotlib.pyplot as plt

# Create a 2x1 grid of subplots

fig, axs = plt.subplots(2, 1)

# First subplot

axs[0].plot([1, 2, 3], [1, 4, 9])

axs[0].set_title('First Subplot')

# Second subplot

axs[1].plot([1, 2, 3], [9, 4, 1])


115
axs[1].set_title('Second Subplot')

plt.tight_layout() # Adjust layout

plt.show()

```

---

8. Adding Bars

For bar plots, you can customize bars with labels and colors.

```python

import matplotlib.pyplot as plt

categories = ['A', 'B', 'C']

values = [3, 7, 2]

plt.bar(categories, values, color=['red', 'green', 'blue'])

plt.xlabel('Categories')

plt.ylabel('Values')

plt.title('Bar Chart Example')

plt.show()

```

---

116
9. Adding Pie Charts

You can add pie charts with labels and percentage displays.

```python

import matplotlib.pyplot as plt

sizes = [15, 30, 45, 10]

labels = ['A', 'B', 'C', 'D']

plt.pie(sizes, labels=labels, autopct='%1.1f%%')

plt.title('Pie Chart Example')

plt.show()

```

---

10. Adding Error Bars

Error bars represent the uncertainty in data points.

python

import matplotlib.pyplot as plt

x = [1, 2, 3, 4]

y = [1, 4, 9, 16]

117
errors = [0.2, 0.4, 0.6, 0.8]

plt.errorbar(x, y, yerr=errors, fmt='o', capsize=5)

plt.title('Error Bars Example')

plt.show()

Sure! Let’s go through each type of chart with examples and visualizations using Matplotlib: line charts, bar
charts, and pie charts.

1. Line Charts

Line charts are useful for displaying data points connected by lines, showing trends over time or other
continuous variables.

```python

import matplotlib.pyplot as plt

# Sample data

x = [1, 2, 3, 4, 5]

y = [2, 3, 5, 7, 11]

plt.figure(figsize=(8, 5)) # Set the figure size

plt.plot(x, y, marker='o', linestyle='-', color='b', label='Line Chart')

# Add titles and labels

plt.title('Line Chart Example')

plt.xlabel('X Axis Label')

plt.ylabel('Y Axis Label')

118
# Add grid and legend

plt.grid(True)

plt.legend()

# Show the plot

plt.show()

```

2. Bar Charts

Bar charts are great for comparing quantities across different categories. Bars can be vertical or horizontal.

```python

import matplotlib.pyplot as plt

# Sample data

categories = ['A', 'B', 'C', 'D']

values = [10, 20, 15, 25]

plt.figure(figsize=(8, 5)) # Set the figure size

119
plt.bar(categories, values, color='skyblue', edgecolor='black', label='Bar Chart')

# Add titles and labels

plt.title('Bar Chart Example')

plt.xlabel('Categories')

plt.ylabel('Values')

# Add grid and legend

plt.grid(axis='y', linestyle='--', alpha=0.7)

plt.legend()

# Show the plot

plt.show()

3. Pie Charts

Pie charts are used to show the proportion of each category as a slice of the whole pie.

```python

import matplotlib.pyplot as plt

120
# Sample data

sizes = [15, 30, 45, 10]

labels = ['A', 'B', 'C', 'D']

colors = ['gold', 'lightcoral', 'lightskyblue', 'lightgreen']

plt.figure(figsize=(8, 5)) # Set the figure size

plt.pie(sizes, labels=labels, colors=colors, autopct='%1.1f%%', startangle=140, wedgeprops={'edgecolor':


'black'})

# Add title

plt.title('Pie Chart Example')

# Show the plot

plt.show()

Unit – 5
Django
Overview of Django

121
Django is a high-level Python web framework that encourages rapid development and clean, pragmatic
design. It follows the "batteries-included" philosophy, providing many built-in features and tools to help you
build robust web applications efficiently.

Here’s a structured overview covering the essential aspects of Django:

1. Installing Django

To get started with Django, you need to install it using pip:

bash

Copy code

pip install django

2. Creating a Django Project

A Django project is a collection of settings and configurations for a particular website. To create a new
Django project, use:

bash

Copy code

django-admin startproject myproject

Replace myproject with your project name. This command creates a new directory with the following
structure:

markdown

Copy code

myproject/

manage.py

myproject/

__init__.py

settings.py

urls.py

wsgi.py

• manage.py: A command-line utility for administrative tasks.

• settings.py: Contains settings for your project.


122
• urls.py: The URL declarations for the project.

• wsgi.py: WSGI configuration for deployment.

3. Creating a Django Application

Applications are components of a Django project that handle specific functionalities. To create an
application:

bash

Copy code

python manage.py startapp myapp

Replace myapp with your application name. This command creates a new directory with the following
structure:

markdown

Copy code

myapp/

migrations/

__init__.py

__init__.py

admin.py

apps.py

models.py

tests.py

views.py

• admin.py: Register models for the Django admin site.

• models.py: Define your data models.

• views.py: Define your views that handle requests.

• tests.py: Write tests for your application.

• migrations/: Keep track of changes to your models.

4. Designing the Data Schema

123
Models define the structure of your data. Define your models in models.py:

python

Copy code

# myapp/models.py

from django.db import models

class Post(models.Model):

title = models.CharField(max_length=200)

content = models.TextField()

published_date = models.DateTimeField(auto_now_add=True)

def __str__(self):

return self.title

Run migrations to apply changes to the database:

bash

Copy code

python manage.py makemigrations

python manage.py migrate

5. Creating an Administration Site for Models

Django’s admin site allows you to manage your data models. Register your models in admin.py:

python

Copy code

# myapp/admin.py

from django.contrib import admin

from .models import Post

124
admin.site.register(Post)

Create an admin superuser to access the admin site:

bash

Copy code

python manage.py createsuperuser

Project Creation
Creating a Django project involves setting up the project structure and configuring essential settings. Here’s
a detailed guide on how to create a Django project:

1. Setting Up the Environment

Before you start creating a Django project, make sure you have Python and Django installed. It’s a good
practice to use a virtual environment to manage dependencies.

1. Install Django :

```bash

pip install django

```

2. (Optional) Set Up a Virtual Environment :

```bash

python -m venv myenv

source myenv/bin/activate # On Windows: myenv\Scripts\activate

```

2. Create a Django Project

125
To create a new Django project, use the `django-admin startproject` command. This command sets up a new
directory structure with the basic configuration files needed for your project.

```bash

django-admin startproject myproject

```

Replace `myproject` with the name you want for your project. This command creates a new directory called
`myproject` with the following structure:

```

myproject/

manage.py

myproject/

__init__.py

settings.py

urls.py

wsgi.py

```

Here’s a brief description of each file and directory:

`manage.py` : A command-line utility that lets you interact with this Django project. You can use it to run
the development server, create database migrations, and more.

`myproject/` : The directory with the same name as your project, containing the core settings and
configuration files.

`__init__.py` : An empty file that tells Python that this directory should be considered a Python package.

`settings.py` : Contains the settings/configurations for your Django project, such as database settings,
installed apps, middleware, etc.

`urls.py` : Defines the URL patterns for the project, mapping URLs to views.

126
`wsgi.py` : WSGI configuration for deploying the project.

3. Running the Development Server

Navigate to the project directory and start the development server to see if everything is set up correctly.

```bash

cd myproject

python manage.py runserver

```

This command will start the development server, and you should see output indicating that the server is
running. By default, it runs on `http://127.0.0.1:8000/`. Open this URL in your web browser to see the
default Django welcome page.

4. Creating and Configuring Applications

Django projects are composed of one or more applications. Each application handles a specific piece of
functionality. To create an application:

1. Create an Application :

```bash

python manage.py startapp myapp

```

Replace `myapp` with the name of your application. This command creates a new directory `myapp` with
the following structure:

```

myapp/
127
__init__.py

admin.py

apps.py

models.py

tests.py

views.py

```

`admin.py` : Register your models here to make them available in the Django admin interface.

`apps.py` : Contains application-specific settings.

`models.py` : Define your data models here.

`tests.py` : Write tests for your application.

`views.py` : Define views to handle HTTP requests.

2. Add the Application to Your Project :

Open `myproject/settings.py` and add your new application to the `INSTALLED_APPS` list:

```python

INSTALLED_APPS = [

...

'myapp',

```

5. Database Setup

Django uses SQLite by default, but you can configure other databases like PostgreSQL, MySQL, etc., in
`settings.py`. The default settings will look something like this:
128
```python

DATABASES = {

'default': {

'ENGINE': 'django.db.backends.sqlite3',

'NAME': BASE_DIR / 'db.sqlite3',

```

6. Creating Database Migrations

When you create or modify models, you need to create and apply database migrations. Migrations are a way
of propagating changes you make to your models into the database schema.

1. Create Migrations :

```bash

python manage.py makemigrations

```

2. Apply Migrations :

```bash

python manage.py migrate

```

7. Creating a Superuser

To access the Django admin site, you need to create a superuser:

129
```bash

python manage.py createsuperuser

```

Follow the prompts to create a superuser account.

8. Running the Server

After setting up your applications and making any necessary changes, you can run the development server
again to see your changes in action:

```bash

python manage.py runserver

```

Install Django : Use `pip install django`.

Create a Project : Use `django-admin startproject myproject`.

Run the Server : Use `python manage.py runserver`.

Create an Application : Use `python manage.py startapp myapp`.

Add App to Project : Include it in `INSTALLED_APPS` in `settings.py`.

Database Setup : Configure in `settings.py`.

Create and Apply Migrations : Use `makemigrations` and `migrate`.

Create a Superuser : Use `createsuperuser`.

Designing the Data Schema


Designing the data schema in Django involves defining your data models, which are Python classes that
represent the structure of your database tables. Here’s a step-by-step guide on designing a data schema in
Django:

1. Understanding Models

130
In Django, models are Python classes that inherit from `django.db.models.Model`. Each model class
corresponds to a table in your database, and each attribute of the model class represents a database field.

2. Creating a Model

To define a model, you’ll use Django’s ORM (Object-Relational Mapping) to describe the fields and
behaviors of your data. Here’s an example model for a blog application:

```python

# myapp/models.py

from django.db import models

class Post(models.Model):

title = models.CharField(max_length=200)

content = models.TextField()

published_date = models.DateTimeField(auto_now_add=True)

def __str__(self):

return self.title

```

In this example:

`title` : A `CharField` for storing the title of the post, with a maximum length of 200 characters.

`content` : A `TextField` for storing the main content of the post.

`published_date` : A `DateTimeField` that automatically sets the date and time when the post is created.

131
`__str__` : A method that returns a string representation of the model instance, used in the Django admin
interface and elsewhere.

3. Defining Relationships Between Models

Models can have relationships with other models, such as one-to-many, many-to-one, and many-to-many
relationships.

# One-to-Many Relationship

A `ForeignKey` is used to define a one-to-many relationship. For example, if each post belongs to a
category:

```python

# myapp/models.py

class Category(models.Model):

name = models.CharField(max_length=100)

def __str__(self):

return self.name

class Post(models.Model):

title = models.CharField(max_length=200)

content = models.TextField()

published_date = models.DateTimeField(auto_now_add=True)

category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='posts')

def __str__(self):

132
return self.title

```

In this example, each `Post` is associated with one `Category`, but each `Category` can have multiple `Post`
objects.

# Many-to-Many Relationship

A `ManyToManyField` is used for many-to-many relationships. For example, if posts can have multiple
tags:

```python

# myapp/models.py

class Tag(models.Model):

name = models.CharField(max_length=50)

def __str__(self):

return self.name

class Post(models.Model):

title = models.CharField(max_length=200)

content = models.TextField()

published_date = models.DateTimeField(auto_now_add=True)

tags = models.ManyToManyField(Tag, related_name='posts')

def __str__(self):

return self.title

```

133
In this example, each `Post` can have multiple `Tag` objects, and each `Tag` can be associated with multiple
`Post` objects.

4. Adding Metadata

You can add metadata to your models using the `Meta` class. This can include ordering, verbose names, and
database table names.

```python

# myapp/models.py

class Post(models.Model):

title = models.CharField(max_length=200)

content = models.TextField()

published_date = models.DateTimeField(auto_now_add=True)

class Meta:

ordering = ['-published_date']

verbose_name = 'Blog Post'

verbose_name_plural = 'Blog Posts'

def __str__(self):

return self.title

```

In this example:

`ordering` : Orders the posts by published date in descending order.

134
`verbose_name` : Provides a human-readable name for the model.

`verbose_name_plural` : Provides a plural name for the model.

5. Field Options

Django provides many options for fields to customize their behavior:

`null` : Whether the field can store `NULL` values in the database (default is `False`).

`blank` : Whether the field is allowed to be empty in forms (default is `False`).

`default` : The default value for the field.

`unique` : Whether the field must have a unique value across all rows in the database.

```python

# myapp/models.py

class Post(models.Model):

title = models.CharField(max_length=200, unique=True, blank=False, null=False)

content = models.TextField(blank=False)

published_date = models.DateTimeField(auto_now_add=True)

```

6. Running Migrations

After defining or modifying models, you need to create and apply migrations to update the database schema:

1. Create Migrations :

```bash

python manage.py makemigrations

135
```

2. Apply Migrations :

```bash

python manage.py migrate

```

Summary

Models : Define data structure in `models.py`.

Relationships : Use `ForeignKey` for one-to-many, `ManyToManyField` for many-to-many.

Metadata : Use `Meta` class for ordering and verbose names.

Field Options : Customize fields with options like `null`, `blank`, `default`, and `unique`.

Migrations : Use `makemigrations` and `migrate` to apply schema changes.

Creating an administration site for models


Creating an administration site for your models in Django allows you to manage your data through a user-
friendly interface. Django provides a built-in admin site that you can customize to fit your needs. Here’s
how to set it up and customize it for your models:

1. Registering Models with the Admin Site

To use the Django admin interface, you need to register your models with the admin site. This is done in the
`admin.py` file within your application.

# Example: Registering Models

1. Define Your Models : Ensure you have models defined in `models.py`.

```python

136
# myapp/models.py

from django.db import models

class Post(models.Model):

title = models.CharField(max_length=200)

content = models.TextField()

published_date = models.DateTimeField(auto_now_add=True)

def __str__(self):

return self.title

```

2. Register Models in `admin.py` :

```python

# myapp/admin.py

from django.contrib import admin

from .models import Post

@admin.register(Post)

class PostAdmin(admin.ModelAdmin):

list_display = ('title', 'published_date')

search_fields = ('title', 'content')

list_filter = ('published_date',)

```

137
`@admin.register(Post)` : This decorator registers the `Post` model with the admin site.

`list_display` : Specifies which fields should be displayed in the list view.

`search_fields` : Allows you to search by fields specified.

`list_filter` : Adds filters to the right sidebar in the list view.

2. Customizing the Admin Interface

Django’s admin interface is highly customizable. You can control the layout and behavior of the admin
forms and list views by using various options in the `ModelAdmin` class.

# Example: Customizing List Display and Filters

```python

# myapp/admin.py

from django.contrib import admin

from .models import Post

class PostAdmin(admin.ModelAdmin):

list_display = ('title', 'published_date', 'content_snippet')

search_fields = ('title', 'content')

list_filter = ('published_date',)

ordering = ('-published_date',)

fields = ('title', 'content', 'published_date')

readonly_fields = ('published_date',)

def content_snippet(self, obj):

return obj.content[:50] + '...'

content_snippet.short_description = 'Content Snippet'


138
admin.site.register(Post, PostAdmin)

```

`ordering` : Specifies the default ordering of objects in the list view.

`fields` : Specifies the order and inclusion of fields in the admin form.

`readonly_fields` : Makes specified fields read-only.

`content_snippet` : A custom method to display a snippet of the content in the list view.

# Example: Adding Inline Models

If you have related models and want to edit them inline within the parent model’s admin page, use
`InlineModelAdmin`.

```python

# myapp/models.py

class Comment(models.Model):

post = models.ForeignKey(Post, on_delete=models.CASCADE)

text = models.TextField()

created_at = models.DateTimeField(auto_now_add=True)

def __str__(self):

return self.text[:50]

```

```python

# myapp/admin.py

139
class CommentInline(admin.TabularInline):

model = Comment

extra = 1

class PostAdmin(admin.ModelAdmin):

list_display = ('title', 'published_date', 'content_snippet')

search_fields = ('title', 'content')

list_filter = ('published_date',)

ordering = ('-published_date',)

fields = ('title', 'content', 'published_date')

readonly_fields = ('published_date',)

inlines = [CommentInline]

admin.site.register(Post, PostAdmin)

```

3. Accessing the Admin Site

To access the Django admin site:

1. Run the Development Server :

```bash

python manage.py runserver

```

2. Navigate to the Admin Interface : Open your web browser and go to `http://127.0.0.1:8000/admin/`.

3. Log In : Use the superuser account created with `python manage.py createsuperuser` to log in.

140
4. Customizing the Admin Template

If you need more extensive customization, you can override Django’s admin templates. Create a `templates`
directory in your application or project and customize the admin templates as needed.

# Example: Overriding the Admin Base Template

1. Create a Directory for Templates :

```

myapp/

templates/

admin/

base_site.html

```

2. Customize `base_site.html` :

```html

<!-- myapp/templates/admin/base_site.html -->

{% extends "admin/base.html" %}

{% block title %}My Custom Admin{% endblock %}

{% block branding %}

<h1 id="site-name">My Custom Admin</h1>

{% endblock %}

```

This example changes the title and branding of the admin site.

141
Summary

Register Models : Use `admin.site.register()` or `@admin.register()` to add models to the admin interface.

Customize Admin Interface : Use options in `ModelAdmin` to customize the admin views.

Inline Models : Use `InlineModelAdmin` to edit related models inline.

Access Admin Site : Navigate to `/admin/` and log in with a superuser account.

Customize Templates : Override default admin templates for more control.

Working with QuerySets and Managers


In Django, working with QuerySets and Managers is crucial for interacting with your database. QuerySets
are used to retrieve and manipulate data, while Managers are used to define custom query methods and
manage query operations. Here’s an overview of how to work with QuerySets and Managers:

1. QuerySets

A QuerySet is a collection of database queries that Django ORM (Object-Relational Mapping) translates
into SQL queries. It represents a collection of objects from your database.

# Basic QuerySet Operations

Creating a QuerySet :

```python

from myapp.models import Post

# Retrieve all posts

posts = Post.objects.all()

```

Filtering Results :

142
```python

# Retrieve posts with a title containing 'Django'

posts = Post.objects.filter(title__icontains='Django')

```

`filter()` : Returns a new QuerySet containing objects that match the given lookup parameters.

`exclude()` : Returns a new QuerySet excluding objects that match the given lookup parameters.

Retrieving Single Objects :

```python

# Retrieve a single post by ID

post = Post.objects.get(id=1)

```

`get()` : Returns a single object matching the given lookup parameters. Raises `DoesNotExist` if no match
is found or `MultipleObjectsReturned` if more than one match is found.

Ordering Results :

```python

# Retrieve posts ordered by published date

posts = Post.objects.order_by('-published_date')

```

`order_by()` : Orders the QuerySet by the given fields. Prefix with `-` for descending order.

Limiting Results :

```python

# Retrieve the first 5 posts

posts = Post.objects.all()[:5]
143
```

Slicing : Use slicing syntax to limit the number of results returned.

Aggregating Data :

```python

from django.db.models import Count, Avg

# Count the number of posts

post_count = Post.objects.count()

# Get the average length of post content

avg_length = Post.objects.aggregate(Avg('content_length'))

```

`aggregate()` : Performs aggregate calculations on a QuerySet.

# QuerySet Chaining

You can chain QuerySet methods to refine your queries:

```python

# Retrieve posts with 'Django' in the title, published in the last 30 days, ordered by published date

from datetime import timedelta

from django.utils import timezone

recent_posts = Post.objects.filter(

title__icontains='Django',

144
published_date__gte=timezone.now() - timedelta(days=30)

).order_by('-published_date')

```

2. Custom Managers

Managers in Django are used to add extra manager methods and query operations to your models. By
default, each model has a manager called `objects`, but you can define custom managers to encapsulate
complex queries.

# Defining a Custom Manager

1. Create a Custom Manager :

```python

# myapp/models.py

from django.db import models

from datetime import datetime

class PublishedManager(models.Manager):

def get_queryset(self):

return super().get_queryset().filter(published_date__lte=datetime.now())

def recent(self):

return self.filter(published_date__gte=datetime.now() - timedelta(days=30))

```

`get_queryset()` : Returns the base QuerySet for the manager.

145
`recent()` : A custom method for retrieving recent posts.

2. Attach the Custom Manager to Your Model :

```python

# myapp/models.py

class Post(models.Model):

title = models.CharField(max_length=200)

content = models.TextField()

published_date = models.DateTimeField(auto_now_add=True)

objects = models.Manager() # The default manager

published = PublishedManager() # Custom manager for published posts

def __str__(self):

return self.title

```

# Using the Custom Manager

```python

# Retrieve all published posts

published_posts = Post.published.all()

# Retrieve recent published posts

recent_posts = Post.published.recent()

```

146
3. QuerySet Methods and Options

`select_related()` : Used for single-valued relationships (ForeignKey). Performs a SQL join and includes the
fields of the related object in the SELECT statement.

```python

posts = Post.objects.select_related('category').all()

```

`prefetch_related()` : Used for multi-valued relationships (ManyToManyField or reverse ForeignKey).


Performs a separate lookup for each relationship and does the joining in Python.

```python

posts = Post.objects.prefetch_related('tags').all()

```

`only()` and `defer()` : Optimize the fields that are retrieved from the database.

```python

posts = Post.objects.only('title', 'published_date').all() # Retrieve only specified fields

```

```python

posts = Post.objects.defer('content').all() # Retrieve all fields except the specified ones

```

Summary

• QuerySets : Represent collections of database queries.


147
• Creating : `all()`, `filter()`, `exclude()`, `get()`, `order_by()`
• Chaining : Combine methods to refine queries.
• Aggregating : Use `aggregate()` for summary statistics.
• Custom Managers : Extend model managers to include custom query methods.
• Define : Create custom managers and methods.
• Use : Access custom methods via `ModelName.manager`.
• QuerySet Methods : Optimize queries with methods like `select_related()`, `prefetch_related()`,
`only()`, and `defer()`.

Retrieving Objects – Building List and Detail Views


In Django, retrieving objects and building list and detail views are fundamental tasks for displaying data
from your models on your website. Here's how you can achieve these tasks:

1. Retrieving Objects

To display data, you need to retrieve objects from the database using Django's ORM. Here’s a breakdown of
common retrieval operations:

# Basic Retrieval Operations

1. Retrieve All Objects :

```python

# myapp/views.py

from django.shortcuts import render

from .models import Post

def post_list(request):

posts = Post.objects.all()

return render(request, 'post_list.html', {'posts': posts})

```

2. Retrieve a Single Object by ID :

```python
148
# myapp/views.py

from django.shortcuts import get_object_or_404

from .models import Post

def post_detail(request, post_id):

post = get_object_or_404(Post, id=post_id)

return render(request, 'post_detail.html', {'post': post})

```

`get_object_or_404()` : This method retrieves an object and raises a `404 Not Found` error if the object
does not exist.

2. Building List and Detail Views

# List View

A list view displays a collection of objects, often with pagination if there are many objects.

1. Create a List View Function :

```python

# myapp/views.py

from django.shortcuts import render

from .models import Post

def post_list(request):

posts = Post.objects.all()

return render(request, 'post_list.html', {'posts': posts})

```

2. Create a List View Template :

```html

<!-- templates/post_list.html -->

<!DOCTYPE html>
149
<html>

<head>

<title>Post List</title>

</head>

<body>

<h1>Post List</h1>

<ul>

{% for post in posts %}

<li><a href="{% url 'post_detail' post.id %}">{{ post.title }}</a></li>

{% endfor %}

</ul>

</body>

</html>

```

`{% for post in posts %}` : Iterates over the posts in the template.

`{% url 'post_detail' post.id %}` : Generates the URL for the detail view of each post.

# Detail View

A detail view displays the details of a single object.

1. Create a Detail View Function :

```python

# myapp/views.py

from django.shortcuts import get_object_or_404

from .models import Post

def post_detail(request, post_id):

post = get_object_or_404(Post, id=post_id)

return render(request, 'post_detail.html', {'post': post})

150
```

2. Create a Detail View Template :

```html

<!-- templates/post_detail.html -->

<!DOCTYPE html>

<html>

<head>

<title>{{ post.title }}</title>

</head>

<body>

<h1>{{ post.title }}</h1>

<p>{{ post.content }}</p>

<p>Published on: {{ post.published_date }}</p>

<a href="{% url 'post_list' %}">Back to List</a>

</body>

</html>

```

3. Setting Up URLs

To connect your views to URLs, define URL patterns in `urls.py`.

1. Define URL Patterns :

```python

# myapp/urls.py

from django.urls import path

from . import views

urlpatterns = [

path('', views.post_list, name='post_list'),

path('post/<int:post_id>/', views.post_detail, name='post_detail'),

151
]

```

2. Include App URLs in Project URL Configuration :

```python

# myproject/urls.py

from django.contrib import admin

from django.urls import path, include

urlpatterns = [

path('admin/', admin.site.urls),

path('', include('myapp.urls')),

```

4. Using Django’s Class-Based Views (Optional)

For more complex views or if you prefer class-based views (CBVs), Django provides built-in generic views
for listing and detailing objects.

# ListView

1. Create a ListView Class :

```python

# myapp/views.py

from django.views.generic import ListView

from .models import Post

class PostListView(ListView):

model = Post

template_name = 'post_list.html'

context_object_name = 'posts'

```

2. Update URL Patterns :


152
```python

# myapp/urls.py

from .views import PostListView

urlpatterns = [

path('', PostListView.as_view(), name='post_list'),

```

# DetailView

1. Create a DetailView Class :

```python

# myapp/views.py

from django.views.generic import DetailView

from .models import Post

class PostDetailView(DetailView):

model = Post

template_name = 'post_detail.html'

context_object_name = 'post'

```

2. Update URL Patterns :

```python

# myapp/urls.py

from .views import PostDetailView

urlpatterns = [

path('post/<int:pk>/', PostDetailView.as_view(), name='post_detail'),

153
]

```

Retrieving Objects : Use methods like `all()`, `filter()`, `get()`, and `get_object_or_404()` to retrieve data
from the database.

List Views : Display a list of objects, typically with pagination.

Detail Views : Show detailed information about a single object.

URLs Configuration : Map URLs to view functions or class-based views.

Class-Based Views : Use `ListView` and `DetailView` for more structured and reusable views.

154

You might also like