Professional Documents
Culture Documents
My Book of Python Computing - Abhijit Kar Gupta
My Book of Python Computing - Abhijit Kar Gupta
Python
Computing
(Python 3)
Book: My Book of Python Computing Author: Dr. Abhijit Kar Gupta Cover
Design: Dr. Dipankar Ghosh
Every enchanting journey begins from the beginning. This book offers an
enjoyable and comfortable journey to a beginner. The ideas of Core Python
are introduced in a lucid way. The sections and chapters are supplemented
with numerical examples and graphical demonstrations. Some useful external
packages are Writing clean and minimal codes introduced. in Python
becomes highly effective with various external modules and packages. The
experience of data handling turns magical, and doing scientific computation
becomes greatly simplified. I hope, the students, teachers and any interested
person may be benefitted by this book.
Let me profusely thank all my students, colleagues, friends, and near and dear
ones for supporting me in this project of writing this book. Hope, the keen
readers enjoy reading.
Thank you.
Abhijit Kar Gupta, Kolkata The
Zen of Python
Copyright
Brief Content
Core Python
Appendix 661
Bibliography 675
Index 679
TABLE OF CONTENTS
Core Python
CHAPTER 1: Fundamental Ideas 1
1
Fundamental Ideas
The name ‘Python’ is not from a snake (as the Python logo might suggest)!
The name ‘Python’ originated from the famous BBC comedy serial ‘Monty
Python’. Guido van Rossum, the originator of Python, is very fond of this
popular comedy serial. So, Guido kept this name as he finished his project of
writing a new computing language.
1.1.1 Variables
Name of a variable (or any identifier such as class, function, module, or other
object) can be anything combined with letters, numbers, and some symbols
like underscore (_) in the keyboard. The special symbols @, $, %, * etc. and
the mathematical symbols (+, -, / etc.) , cannot be used. Variables are to begin
with a letter (the upper case or lowercase letters from A to Z) and the names
cannot start with a number. The names of the variables are, of course, case
sensitive. The variable names, ‘Xy’, ‘xy’, ‘xY’ are all different. But the
names cannot be the words given in the following table which are used for
system commands and functions. Those are Python keywords (in lower case
letters) reserved for the use by the system.
Reserved words:
and, assert, break, class, continue, def, del, elif, else, except, exec, finally, for,
from, global, if, import, in, is, lambda, not, or, pass, print, raise, return, try,
while, with, yield
1.1.2 Numbers
There are three different kinds of numbers that a computer program usually
handles: integer, float (real numbers with decimal part) and complex (�� +
����, �� =√−1). Python supports all three kinds.
I ntegers in Python 3 are of unlimited size. In fact, any large integer of any
length which may even occupy the entire memory space of the processor is
possible. [Python 2 has two integer types: - int andlong. There is no 'long
integer' type in Python 3 anymore.] In addition, Booleans are a subtype of
plain integers.
• Integer
[Examples: 236, -123]
• Float (or floating point
numbers)
[Examples: 13.25, 0.0, -13.23, 1.2e-10, 3.85e12 ]
• Complex
[Examples: 5j, 9.8j, 3+2j, 1.2e-10j]
• Long
[Examples: Any integer that ends with ‘l’ or ‘L’. 123L will be treated as long
in Python 2.]
• Boolean
True, False
Note:
The integers are positive or negative whole numbers. Long integers are
integers of unlimited size (written as integers followed by upper- or lower-
case L). The Max value of the default int in Python 2 is 65535, anything
above that will be a long.
In the calculations, as it often happens that there are all kinds of numbers
mixed up. The system will understand in what number the result will be
delivered, in a clever way. If all the numbers are integers, the result is an
integer. If at least one is real in the mixture of real and integer, the result will
be in real. If there is a presence of a complex number, the result will be in
complex. Of course, we can explicitly set the type of a variable to ‘integer’,
‘real’ or ‘complex’.
1.1.3 Operators
Mathematical Operators
Consider two variables ‘��’ and ‘��’. The following operations are with
�� = 10, �� = 2
== Equal to
!= Not equal to
<> Less than and Greater than < Less than
> Greater than
>= Greater than equal to <= Less than equal to
We must type anything from the start (from the first column) on interpreter or
inside any Python shell. We are not allowed to press ‘space bar’ before we
write. In the latter case, it will raise error.
Learning of every Computer language begins with typing ‘Hello World’! Let
us also type:
>>> print(‘Hello World!’) Hello World!
# Double quotes
>>> “I am a Pythonista.” ‘I am a Pythonista.’
Note: The syntax error arises because Python treats ‘I a’ as a string from the
above statement and it does not understand the rest of the text. To include the
apostrophe(‘) inside the single quoted string we need to use an escape
character(\).
Note: If we write the first line and then press Enter to write the second line,
that will result in an error (incomplete string). The escape (\) symbol connects
the two lines and make one.
1.2.2 Writing Multiple Lines Let us write the following two lines:
‘I am a beginner.
I wish to learn Python.’
Note: Creating a new line is possible under triple quotes. Of course, this time
we need to use print() function to write properly as we see below. When
written directly over the interpreter, it produced a line with \n in between.
This means, it recognized a new line but could not represent properly
withoutprint().
Inside the triple quotes, we can put escape characters like \t, \n to create a tab-
space horizontally and a new line respectively.
Exercise: Write the The Zen of Python in the poetry form (with appropriate
spaces and lines) with the above knowledge.
1.3 Namespace
As we open the interpreter (>>>) and type dir(), we see the following in the
directory:
>>> dir()
['__annotations__',
'__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
pythonic way. For now, note only one name ‘__builtins__’ which is a built-
in module. Let us check what it contains.
>>> dir(__builtins__)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException',
'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning',
'ChildProcessError',
'ConnectionAbortedError',
'ConnectionError',
'ConnectionRefusedError',
'ConnectionResetError',
'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception',
'False', 'FileExistsError',
'FileNotFoundError',
'FloatingPointError',
'FutureWarning', 'IOError',
'GeneratorExit',
'TabError',
'UnicodeWarning', 'ValueError',
Note: The names (in alphabetic order) without underscores:abs, dir, id, list,
type etc. are methods to be applied over Python objects. We can use them
without reference when we write codes. The names with double
underscores(__) on both sides are attributes and methods written in Pythonic
ways. Every Python Class (introduced later in chapter 4) keeps built-in
attributes which can be accessed through dot operator like any other attribute
(to be seen later).
We saw some names in the above display. They are objects. In python,
variables and functions or methods are all objects (We shall formally
introduce the concepts of Class and Object later.). Every object has a name:
some are built-in and some we create. A namespace is a container or
environment which contains unique name for each object in Python with
which we work. [In short, namespace is our working table with all the
necessary tools.]
To understand how new names in the namespace are included, let us type
some names (by assigning two variables to two integers) as follows,
>>> x = 2 >>> y = 4
Now let us type the following: >>> dir()
Note that the namespace (directory) now includes the variable names:x and y
along with the built-in names. Later we will see, apart from __builtins__
module, there are many other modules that we can import into our namespace
for various purposes.
9.4
>>> 19 – 6 # Subtraction
13
>>> 12 * 9 # Multiplication
108
>>> 11/2
5
[Python 2: returns integer (quotient only)]
# Modulo
>>> 3 + 2.4j
(3+2.4j) >>> 3 + (2 + 4j) * 2
(9+12j)
>>> (3 + 2j) * (4 + 5j)
(2+23j)
0
1.4.4 Comparison of Numbers
>>> 2 > 3
False
>>> 3 < 9
True
>>> 5 == 6
False
[Double equal (==) used for comparison]
1.5.1 Type
>>> type(True)
<class 'bool'>
>>> type(False)
<class 'bool'>
>>> int(True)
1
[Integer representation of True] >>> int(False)
0
[Integer representation of False]
# Boolean test
True
>>> bool('abc182') True
>>> bool(0) # Zero as argument False
>>> bool() # Nothing as argument False
>>> bool(‘’) # Arg as empty string False
>>> x = 3
[Assign the number 3 to x] # Where is the variable stored?] >>> id(x)
140727110538976 # Memory location >>> y = x # Assign another name
# Delete a Variable
Note: The variable ‘x’ is now deleted and now typing id(x) will result in an
error message: “name 'x' is not defined”.
Up ( ↑) and Down (↓) keys can be mapped for recalling history. Enter key
can be pressed over a previous line to bring it live on the interpreter. The
underscore (_) refers to the last object typed.
>>> 5
5
>>> _ # The _ trick 5
>>> x =_
# Absolute values
23
[Integer part of absolute value] >>> abs(3 + 4j)
5.0
[Absolute value: √32+ 42]
# Power >>> pow(2, 4) # 24 16
# Minimum, Maximum
# Rounding off
>>> round(23.9237, 3)
23.924
[Round off to 3 decimal digits]
The round off rule: The last decimal digit ( ��) till which it is rounded off,
is increased by 1 when (�� + 1)-th digit is >= 5, else it stays the same.
The last output is 2.67, but it is expected to be 2.68 since the third decimal
digit in 2.675 is 5. This anomaly occurs due to decimal number
representation error. To look at the actual representations of the numbers
2.685 and 2.675 by the processor, we import a function, Decimal() from the
module decimal.
[ Note: Sometimes, one exception to the usual round off rule is considered. If
the digit next to the round off place is 5 which is followed either by 0 or
nothing (for example, in 2.685 or 2.675) then 1 is not added to digit at the
round off place. For more on this, see Chapter 0, page 14.]
# Creation of Numbers
We can create a collection of integers by a built-in functionrange().
>>> list(x)
[1, 2, 3, 4, 5, 6, 7, 8, 9] >>> tuple(x)
(1, 2, 3, 4, 5, 6, 7, 8, 9) >>> set(x)
{1, 2, 3, 4, 5, 6, 7, 8, 9}
We have the following data structures (types of data collections): list(),
tuple(), set(). These functions, in general, are called iterables. This means,
they are composed of elements. We can extract the elements when we iterate
over them in a loop. [More on this in chapter 2.]
Note:
A loop is an iterator. The above is a typical syntax of writing a for-loop. The
loop starts with keyword for, followed by some dummy index ‘i’ and then
you put some iterable which ends with a colon. Under this, there is one
indented statement to print. [We demonstrate more on this in the next
chapter.]
# Conversions
>>> bin(12) # Decimal to binary '0b1100'
>>> 0b1100 # Binary to Decimal 12
>>> oct(48) # Decimal to Octal '0o60'
>>> 0o60 # Octal to Decimal 48
>>> hex(31) # Decimal to Hexa '0x1f'
>>> 0x1f # Hexa to Decimal 31
x = 15
y = 12.6
z = -10
x, y, z = 15, 12.6, -10 [Multiple inputs in one line] a = input(‘Enter value’)
[For prompting]
a, b, c = input('Enter values \t') # \t for space a, b, c = input(‘Enter values
Note:
• Alphabetic characters are put inside quotes. Anything surrounded by quotes
is a string. In the above, ‘abhi’ is a string.
• Also note that the input values taken through the input() function will return
as strings. We will discuss about that in detail later.
• Recognize that any name followed by parenthesis () is usually a function.
Here, input() is a built-in function. In Python, we come across many such
built-in functions.
>>> a = 100
[The number 100 assigned to ‘a’.] >>> print(a)
100
>>> a, b, c = 2, 3, 5
[Multiple assignments on one line] >>> a
2
>>> b
3
>>> x, y = 10, 'My name' [A Number, a String] >>> x
10
>>> y
'My name'
‘ \n’) It prints values with gap (if no separation is mentioned and the cursor
stops on the new line due to ‘\n’ as default.)
x, y = 2, 3
print(x, y) Output: 2 3
print('values = ', x, y, sep = ',') Output: values = 2,3
print(x, y, sep = ',', end = ':') Output: 2,3:
Note:In Python 2, we write ‘print x, y’, where the print is a command and not
a function.
>>> a = input()
∎ ← Cursor waits here for input
Note: The value entered through the built-in functioninput()is astring. In fact,
in Python 3, every input entered through the input() function is a string. [The
Python strings are unchangeable. This is an important consideration for input
as data strings.] In Python 2, a number entered through input() function
remains a number only (not a string).
The built-in function eval() is used to evaluate the input string. Thus, we get
back the number from a string of number.
Note: The prompt string, ‘Enter the values \n’ is to display the line: ‘Enter
the values’. The ‘\n’ that follows the string, is to create a new line in the
display.
>>> x, y = 4, 5
>>> print(x, y) # Unformatted 4 5
# Positions: 0 & 1
print('{0} and {1}'.format(x, y)) Output: 4 and 5
# Positions altered
print('{1} and {0}'.format(x, y)) Output: 5 and 4
>>> print('{0:.4f},
{1:.5f}'.format(pi, e))
3.1416, 2.71828
>>> print ('{1:.4f},
{0:.5f}'.format(pi, e))
2.7183, 3.14159
>>> x = 0.0158976
>>> 'Approx value =
>>> a = 805682.47
>>> '{:.4E}'.format(a)
'8.0568E+05'
>>> '{:.2E}'.format(a)
'8.06E+05'
Let us write Python programs of multiple lines (We call it a ‘script’ when we
write in a file.) on Python interpreter. [As we write a line on interpreter and
press Enter key, the line or instruction will be interpreted. Then the
interpreter (command line) will be ready to take next instruction. If there is
something wrong in syntax or some errors, it will return some error message.
We need not compile the entire script at a time to check.]
# Evaluating expressions
>>> x, y = 2, 3
>>> z = x*y - x/y
>>> print(z)
5.333333333333333
x = -31 [output]
Writing in a File
To write a Python code in a file (often called Python script), one can write the
lines of instructions in a file (by clicking the ‘file’ option on interpreter
window or by creating a plain text file with any Windows or Linux editor.).
The file (named assomething.py) is then executed by clicking ‘run’ option on
interpreter window or by writing ‘python something.py’ on Linux/ DOS
command line. The demonstration on IDLE interpreter is as follows:
Other options
• ‘w’→ Opens a file for writing only. Overwrites the file if it exits, else it
creates a new file.
• ‘w+’→ Opens a file for both reading and writing. Overwrites the file if it
exists. If the file does not exist, it creates new file for reading and writing.
• ‘r’→ Opens a file for reading only. The file pointer will be placed at the
beginning of the file.
• ‘r+’→ Opens a file for both reading and writing. The pointer is placed at
the beginning of the file.
• ‘a’→ Opens a file for appending. So, it adds lines or data at the end of the
previously existed lines. If the file does not exist, it creates a new file for
writing.
Write in a file:
print(x, y, f1)
[Write x, y in file, indexed ‘f1’]
Close a file:
f1.close()
Note:
We can refer the files with some index/ identifier. For example, we wrote
‘f1’, ‘f2’. We can give any name. In place of ‘filename’, as argument in
open(), we may write any name, with or without extension. However, it is a
good practice to write filename with extension, such my_file.text etc. This
maintaining file systems.
as test.dat, is helpful in
1.11 Commenting
Example:
“““
This part is for documentation. The following code is to solve a differential
equation by Euler Method.
CHAPTER
2
Built-in Modules
“Tell me and I forget. Teach me and I remember. Involve me and I learn.” -
Benjamin Franklin
2.1 What is a Module?
As it was mentioned earlier, core Python has several modules for various
purposes apart from the built-in module (__buitins__) that we have already
seen. Below, we will make use ofmathand cmath (mathematical modules for
real and complex numbers) and other modules to write some simple codes.
2.2 Import Module
>>> import sys # Import module >>> dir(sys) # Check directory
The built-in function dir()is to display what the module contains. [A module
can display strings, parameters, functions, and classes etc. that are in it.] To
check any of them and to obtain help, we type another built-in function
help(). [This provides us with online manual to read which is always helpful.]
The last line, sys.version returns a string that provides the information about
the version etc.
Let us talk about the verse: ‘The Zen of Python’ which captures the essential
philosophy of Python Computing. We can obtain the poem with the
following import.
Beautiful is better than ugly. Explicit is better than implicit. Simple is better
than complex. Complex is better than complicated. Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity. Errors should never pass silently. Unless
explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea. Namespaces
are one honking great idea
-- let's do more of those!
>>> help(‘modules’)
……
Please wait a moment while I gather a list of all available modules...
[Note: The output returns a huge list which may take some time to appear.]
# Import the module >>> import module
# Check what are there >>> dir(module)
# Help on some name in module >>> help(module.name)
2.3 Math module
Note: Earlier we used pow() function which was taken from the built-in
module (__builtins__). In that case, we did not have to refer the module name
as it was a built-in function. We directly wrote: pow(2, 4).
>>> math.gcd(18, 4)
2
>>> math.log(e)
1.0
# Radian to degree conversion >>> math.degrees(pi)
180.0
>>> math.degrees(1.571)
90.01166961505233
# Hypotenuse
>>> math.hypot(3, 4) 5.0
Python Script:
# To test Stirling’s approximation
import math
n = eval(input('Enter Number \n')) lognfac = sum([math.log(i) for i in
#Proportional error
print (‘Approx value, Exact value, Percentage Error’) print (approx,
lognfac, prop*100)
# Constants from math module
>>> math.e
2.718281828459045
>>> math.pi
3.141592653589793
>>> math.cos(math.pi/4) # cos(��/4)
Note: More than one module cannot be called in one line. Interested readers
can consult PEP 8 – Style guide for Python code.
Applications:
#1 Area of a Circle
import math
r=5
area = math.pi*r**2 print('Area = ', area)
Note: In Python 3, input data taken through input() is a string. So, we need to
evaluate the string into a number by eval().
#2 Value of sin ��
import math
theta = 45 # Angle in degree
# Convert to radian
theta = theta*180/math.pi print(math.sin(theta))
# 3 Area of by Heron’s Formula
Algorithm:
1. Input: ��, �� and �� (in deg.)
>>> cmath.sqrt(-1)
1j
>>> cmath.sin(2)
(0.9092974268256817-0j)
>>> cmath.exp(2 + 3j)
(-7.3151100..+1.0427436562359045j)
# Phase angle
>>> cmath.phase(2 + 3j) 0.982793723247329
Note: Imaginary number(�� =√−1) is 1j. We can write 0j, 1j, 1.5j, -5.96j
etc. but without any prefix Python understands j as a variable name (some
number to be prefixed).
>>> 0j
0j
>>> 1j
1j
>>> j
Traceback (most recent call last):
File "<pyshell#137>", line 1, in <module>
j
NameError: name 'j' is not defined
• sqrt(x)→ √��
Examples: math.sqrt(5), cmath.sqrt(1+2j)
����
Examples: math.exp(3), cmath.exp(2+1j)
Random numbers are those which appear randomly and have no correlations
among them. We come across random numbers in Nature. In electrical noise,
in atomic vibrations or in Brownian motions of molecules, we get random
variations in some quantities that we measure. They do not follow any
definite pattern, or we can say, there is no mathematical law that can bind the
sequence of numbers. The occurrences of random numbers may follow a
certain distribution, but they appear randomly. In the study of many natural
phenomena and in computer modeling of them, the random numbers play
important roles. So, we need to access a set of random numbers for our study.
Moreover, in the subject of Cryptography, random numbers play a big role.
>>> help(random.random)
Help on built-in function random: random() method of random.Random
instance
random() -> x in the interval [0, 1).
Note: Two different random numbers are generated by calling the same
function twice.
range(10)] [0.5350878140843952,
0.43405195589115175,
0.3065609996673083,
0.12325970038943124,
0.7195962696461913,
0.06200758778693172, 0.6430032231626809, 0.4271828905197539,
0.26735110979806964, 0.41317992315825525]
Note:normalvariate(mu, sigma)
generates random number from Normal distribution with given mu (mean)
and sigma (standard deviation).
>>> L = range(10)
>>> random.choice(L)
4
>>> random.choice(L)
7
>>> x = [‘Apple’, ‘Banana’,
Application:
Let us imagine a Random Walk on a plane.
The choice is out of the following 4 pairs: ∆��, ∆�� = [(��, ��),
(−��, ��), (��, ��), (��, −��)]
# For plotting
import matplotlib.pyplot as plt plt.plot(X, Y)
plt.show()
Fig. 1 A Random Walk on a square grid generated through Python code.
2.5.3 Random Sampling
# Random sample of size 5 >>> random.sample(p, 5) [97, 38, 18, 72, 54]
# Another sample of size 5 >>> random.sample(p, 5) [72, 84, 14, 90, 17]
2.5.4 Random Shuffling
>>> import os
>>> r = os.urandom(4)
>>> r
b'\xef\xe2\x1fA'
>>> len(r)
4
[ASCII code string of length 4]
# To decode
>>> struct.unpack('d',
os.urandom(8))
(7.233012407285722e-05,)
>>> struct.unpack('h',
os.urandom(2))
(-25353,)
Time is ever changing, as we can see from the above by repeatedly typing the
same command on interpreter. Thus, we can calculate the difference in time
between two instants. This is very useful for knowing the run time of a
Python program.
If we import the time module inside a python script and keep track of initial
time (execution of first line of the program) and final time (when the program
is over), we can know how long a program took to run. Typically,
import time
begin = time.time()
Instruction Block
end = time.time()
run_time = end – begin
# For local time and Date (in hours, min, sec etc.)
# Calendar
July 2020
Mo Tu We Th Fr Sa Su
12345
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Note: By default, these calendars have Monday as the first day of the week,
and Sunday as the last (the European convention). Various methods can
create calendar of various styles.
Exercises
1. Check the datatypes of (i) 346.89, (ii) 14/5, (iii) 20/3.0, (iv) 2(3+6j), (v)
‘12345’
2. Are the following statements alright? Type and check. Find the errors, if
any. (i) 6x = 6x, (ii) x8 = x8, (iii) x5 = x*5, (iv) x*3 = 3*x
3. Write a Python program to compute ��, where �� = ��(�� −
��)/(�� + ��) with �� = 5, �� = 9. Print the output and check the
datatype of��.
4. Supposewe have three strings, x = ‘Hello friends!’, y = ‘That is why I feel
good.’, z = ‘I learnt Python.’ Can you print the three strings together to have
a meaningful conversation?
5. The real and imaginary parts of a complex number �� are 3.5 and 1.8
respectively. Construct the complex number and find the value of����∗.
6. If��1= 2 + �� and ��2= 3 − 2��, compute |3��1− 4��2|.
7. Take three numbers, integer, float and a complex: 34, -56.98, 5 + 2j. Take
the product of the three numbers and find out the type of the output.
8. Prove the following identity numerically for any angle:cos5�� =
16cos5�� − 20cos3�� + 5cos��
9. Given, the Cartesian coordinates of two points: ��1= (2, 3, 5) and
��2= (9, 0, −1). Write a Python code to compute the distance between the
two points.
10.Import a module ‘sys’ on your namespace. Check the directory and find
out a possible function there in the directory to check your Python version.
11. Print the calendar of March 2021.
12. Convert the decimal number 5983 into a (i) binary, (ii) hexadecimal, and
(ii) octal number.
13. Use the built-in function to find the maximum and minimum among the
following numbers: −1, 2, 10, 13, 3.5, 71.9, 23, −34, 0, 0.98, 11.3, 10
14. Create 5 random numbers from a Normal distribution with �� = 0.5,
�� = 1.
15. Create 3 random fractions between 2 and 3 and find out the mean of those
numbers.
16. Create all the odd integers between 3 and 17.
17. Write a Python program to write ‘area = 50’ with 2 spaces each before
and after the equality symbol.
18. Accept three numbers as user input and then find the mean of them.
19. Print0.000053 in exponential format.
fraction and print that up to 5 decimal points. 21. Import the special
numbers�� and �� frommath
module. Round off each number up to 3
decimal points and then find the value of����2. 22. Check the famous
Euler identity:������= −1.
[Take �� frommath module.]
23. There is a module called, ‘platform’ in core
Python. Import this module and use a function
system() from it to check which operating
system you are using.
24. A square is inscribed inside a circle of radius 5
unit. Find the ratio of the areas between the
circle and square.
25. Consider the following 10 numbers:0.2, 0.4,
0.9, 0.1, −0.3, −0.4, 0.6, 0.4, −0.5, −0.1.
Take a random sample of 5 numbers from this
and find the maximum out them.
26. Verify:ln 100! ≈ 100 ln 100 − 100.
30. Write a Python script which accepts the first and last name of the user and
print them in reverse order with a gap between them.
31. Write a Python script to print the calendar of March’19. [Use calendar
module.]
32. If��1= 4 − 3�� and ��2= −1 + 2��, evaluate: (a) |��1+ ��2|,
(b)��1∗+ ��2∗ and find out the real and imaginary components in each
case.
33. Print the The Zen of Python poem.
CHAPTER
3
Loops and Conditions
“Logic will get you from A to B. Imagination will take you everywhere.” -
Albert Einstein
3.1 Loops
3.1.1 For-Loop
# Structure of for-loop
Note on Syntax:
• The loop starts with the keyword,‘for’ followed by some dummy index (‘i’,
in this case) and then another keyword, ‘in’ which is followed by an iterable.
• In this case,range() is an iterable. The loop iterates over it. This is followed
by a colon (:) which is to connect the next lines of instructions.
• The instructions must beindented under the loop. Also, all the indented
instructions are to begin from the same column.
Fig. 1: Concept of indentation: how the nested blocks are indented.
range()
The object range() is as an iterable. Some other iterables are:string, list, tuple,
set, dict.
# Conversion
>>> range(1, 10, 2) range(1, 10, 2)
# Convert to a list
>>> list(range(1, 10, 2)) [1, 3, 5, 7, 9]
# Convert to a tuple
>>> tuple(range(2, 10, 3)) (2, 5, 8)
Python code #1
for n in range(1, 4): print(‘Very good’)
Output:
Note: The function, range(1, 4) generates integers from 1 to 3. So, the for-
loop runs forn = 1, 2, 3 [The index n is a dummy index. We can write
anything.].
Python code #2
for x in [0, 1, 2]: print(x, "\t", 2**x)
Output:
01
12
24
Note: In the print() function, “\t” (creates one space by tab) is used to
display the output in the gap separated way.
Python code #3
# Sum of integers from 1 to 100 s = 0 # Initial value for n in range(1, 101):
1 times 5 is 5
2 times 5 is 10
3 times 5 is 15
4 times 5 is 20
5 times 5 is 25
Note: Loops can also be written with other kinds of iterables like tuple, string
etc. in the same way. For example,
\ n’)) fac = 1
for i in range(2, n+1):
fac = fac*i
print (‘Factorial = ‘, fac)
Exercises:
Nestedfor-loop:
A nested loop is a loop inside a loop. We can have any number of loop-
blocks inside a loop.
x=x+i
print(n, x)
Output:
5 10
6 15
7 21
8 28
9 36
Notes on abbreviations:
The sum and update: ‘s = s + n’ can be abbreviated ass += n. Such
abbreviations can also be applied for product(*), subtraction(-)
and division (/). The short-hand mathematical symbols, +=, -=, *=, /= are
often used for iterative computations.
>>> x *= 4>>> x
>>> x5.0
20
3.1.2 While Loop
The while loop is a loop with logical decision making. The loop continues if
some condition is satisfied.
x=1
while x < 10:
x += 2
print(x, end = ‘ ’)
Output:
3 5 7 9 11
Note: print(x, end = ‘ ’) prints the result in one row with a gap after each
element.
# Example: Infinite Loop x = 10
while True:
print(x)
Note: ‘while True’ means loop forever. The while statement takes an
expression and executes the loop body while the expression evaluates to
(Boolean) "true". True always evaluates to Boolean "true" and thus executes
the loop body indefinitely.
3.2 Conditions
The if statement is used for logical decision making with one or more
conditions and instructions under it.
# Logical if
if x == 10:
print(‘x is 10’)
# On a single line
if x == 10: print(‘x is 10’)
Applications:
# Perfect Square Number
[One way is to calculate the square root which will be an integer when perfect
square (√25 = 5) and a float for a non-perfect square (√26 =
5.0990195135927845). In case of a float (and not an integer), the integer part
can be squared again and then to compare with the original number.]
nsqrt = int(sqrt(number))
nsq = nsqrt*nsqrt
if nsq == number: print(‘perfect
square’)
# if...else [Looking for an alternative decision.]
if x == 10:
print(‘x is 10’)
else:
print(‘x is not 10’)
Application:
# Odd or Even
print(‘Even’)
else:
print(‘Odd’)
# Armstrong Number
Exercise:
# if...elif...else
[Decision with more than one conditions]
a, b, c = 2, 3, 4
x = b*b - 4*a*c
if x < 0:
# Nested if
[When one logicalif is embedded inside another
logicalif.]
if n < 0:
print(‘negative’)
else:
print(‘positive’)
if n > 10:
print(‘More than 10’)
# Multiple conditions
Take the list, x = [1, -1, 0, 9, 3, 4, 5, 6, 7, 11, 100, -3, -5].
for i in x:
if i < 10 and i > 0: print(i, end = ' ') Output: 1 9 3 4 5 6 7
for i in x:
if i > 10 or i < 0: print(i, end = ' ') Output: -1 11 100 -3 -5
Break inside a loop
Output:
10 16 18 Done!
try: print(x)
except:
print('Exception Error occurred')
Output:
Exception Error occurred
Note: The error occurred because the argumentx was not initialized.
# With specified error (NameError) try:
print(x)
except NameError:
print('Error: argumemt not defined') except:
print('Unknown Error')
Output:
Error: argument not defined
# Use of ‘else’
try:
print(x)
except:
print('Unknown Error')
else:
print('No Error')
Output:
Unknown Error
# Fix Error
Assign x before the block. It will not now raise error. It will print the value.
x = 10 try:
print(x)
except:
print('Unknown Error')
else:
print('No Error')
Output: 10
No Error
# Use of ‘finally’
This block, if present, will be reached irrespective of the fact that it enters try,
except orelse block.
try: print(x)
except:
print('Error: argument not defined')
else:
Output:
Error: argument not defined Final block reached
Exercises
1. Write a Python script that takes the radius of a sphere from user and
compute the volume.
2. Python script to compute the distance between two points:(2, 3, 5) and (−1,
2, 9) .
3. Given some data: �� = 87, 91, 85, 75, 28, 122, 66, 56, find the
(arithmetic) mean and r.m.s. value of the variable ��.
4. Given the following numbers:6, 12, 17, 15, 32, 64, −2, 100, 103, 13, 2, 0.
Find the maximum, minimum and range of the numbers.
5. A set of 20 numbers are given:1, 0.1, 5, 3, 10, −1, 4, 20, 1000, −9, 2, 14,
4.5, 0.9, 30, 9.8, 11, 22, 48, −10 . Write a computer program to count how
many numbers are there between 0 to 10.
6. Write a computer program to compute ��!, where �� = 10.
7. Write a Python script to verify Stirling’s approximation:ln 100! ≈ 100 ln
100 − 100.
10. Find all the roots for the 5th root of unity. [Hint: 1
1/��
=(������(2������))
1/��
2������
17. Write a Python script to print the alphabet pattern ‘E’ with *.
*****
*
*
*****
*
*
*****
19. Write a Python script to accept any integer (��) and compute:�� +
���� + ������ + ��������
20. Find the value of�� from the infinite series:��=4
s = 0.0
l = [1,2,3,4]
for c in l:
s = s*x + c
return s
23. Write a function that will take a list of the coefficients ��0, ��1,
��2,….���� as one argument and the value of a variable �� as
another and return the value of the polynomial: ��0+ ����1+
��2��2+ ⋯ + ��������
24. Write a program that will use this function print out a table of the values
of the polynomial: ��3− 7��2+ 5 for the values 0, 1, 2, ... 10 for��.
Why do you think that the code written in this problem has an advantage over
simply returning �� ∗∗ 3 − 7 ∗ �� ∗∗ 2 + 5 (direct calculation)?
CHAPTER
4
Python Function
“Minds are like parachutes– they only function when open.” – Thomas
Dewar
A function block begins with the key word ‘def’ followed by the name of the
function with arguments inside the parentheses () and that ends with colon
(:). The instructions (single or multiple lines) are indented under it. The
keyword return is to return the value to be used anytime.
Python Script
# Area of a circle from math import pi
# Argument as String
def my_name(name):
return 'My name is ' + name
# Argument as list
def prod(x):
n=1
for i in x:
n = n*i
return n
# Testing on interpreter
>>> x = ‘Abhijit’
>>> my_name(x)
My name is Abhijit
[Two strings added]
Note: We may also sometimes define a function inside the main block
bracketed by the pair:def main() &main(). Between them, we can place the
indented function block anywhere.
def main():
x = input(‘Enter x’) def f(x):
return x*x
print f(x)
main()
4.1.1 Function with Doc-String
For demonstration, we define the following two functions with and without
docstrings. Let us see the difference when we call the identifiers.
"""This is my defined
function."""
return x**3
The doc-string gets printed out when we call help() on the function.
>>> help(g)
Help on function f in module __main__:
f(x)
This is my defined function.
The docstring is usually written under triple quotes to accommodate the
string of multiple lines.
• A function begins with def header followed by colon (:) which ends the
header.
• We can write variables and parameters as arguments.
• A docstring is optional.
• The function body can consist single or multiple statements.
• A return statement returns a value.
When we call the above functions on interpreter, both returns the same
output. However, when we assign them to some other variables, we see the
difference.
>>> void_func(2) 5
>>> true_func(2) 5
>>> x = 2
>>>
>>> f = true_func(x)
>>> f # The true function returns 5
Note: The assigned object for the void function returns nothing when called
again. However, on interpreter, it prints the value at the time of assignment.
So, a void function with print() may behave like a true function but we have
to be careful when we use that in a Python script.
def arbit(*x):
for i in x:
print(i**2, end = ‘ ’) >>> arbit(2, 3, 4) 4 9 16
The argument x, in the above function, can take any number of values
(numbers). Let us also try the same with names (strings).
def greet(*names):
for name in names:
return 'Hello' + ' ' + name
We called the function with the values of arguments in place. However, when
we can call by the argument names: x, y, z, they are called keyword
arguments.
# Arguments through keywords >>> multi(x = 2, y = 3, z = 4) 14
When we do not know how many keyword arguments will be passed into the
function, we add two asterisks (**) before the parameter name in the
argument field. Thekeyword arguments that we pass into a function, will be
placed into a dictionary. We can examine the keys and values of this
dictionary.
>>> d = arbit_key(a = 1, b = 2, c = 3)
{‘a’: 1, ‘b’: 2, ‘c’: 3}
[Dictionary]
>>> type(d)
<class 'dict'>
s = s + par[i]*x return s
>>> my_func(5, a = 1, b = 2, c = 3) 30
def f(x):
a = 2 # Local parameter return a*x
Even if we change ‘a’ from outside, the function accepts the local value only.
For example,
>>> f(5)
10
>>> a = 10
>>> f(5)
10 ← The value is unchanged.
>>> f(2) 10
>>> a = 10 >>> f(2) 20
>>> f(5)
20
>>> print(b) 4
4.3 Function Inside Function We can call one function inside another
function. For example,
>>> cal(2, 3, 4)
20
Note: The function add() has been called inside the function cal(). In fact, we
can call any number of functions, any number of times inside a function.
def facto(n):
if n == 1: return 1 return n*facto(n-1)
Note: The factorial, ��! = �� ×(�� − 1)! Writing this way makes it
possible to define through a recursive function.
# Single argument
f = lambda x: x**2
Note: The name ‘f’ is an object created through the keyword lambda,
followed by argument x. We can say, we have created a functionf(x).
# Multiple arguments
add = lambda x, y: x + y >>> add(2, 3) 5
>>> fun(1)
0.36787944117144233
4.6 Function Overload
Function overloading is the capacity to hold multiple functions with the same
name. Python does not support function overloading by default.
In Python, when we define two functions with the same name, the last
function overrides the first one in the namespace. However, we can give
same function name to different functions when they are inside different
classes or modules. [It is like having same name for two different persons in
the same family is confusing while the same name for two persons in
different families is ok.]
plot (*args, scalex = True, scaley =True, data = None, **kwargs) …..
Note: A first few lines are displayed here. Let us know about the arguments.
The *args refers arbitrary number of default arguments, **kwargs refers
arbitrary number of keyword arguments and we also find other optional
parameters.
4.8 Python map Function
The map() function returns a map object (which is an iterator) of the results
after applying the given function to each item of a given iterable (list, tuple
etc.).
>>> L = [1, 2, 3, 4]
>>> f = lambda x: x**2
>>> map(f, L)
<map object at 0x0000015A07FD0880> >>> list(map(f, L))
[1, 4, 9, 16]
The filter() function returns a filter object where the items from a list (or
tuple etc.) are filtered through a function under some given condition. We can
see the filtered object only when we convert it to a list or tuple etc.
Note that the action of map() over such function will return in Booleans.
Application: Given a list of numbers, filter out the floats and return the list of
integers.
This built-in function creates an object where the corresponding items from
two lists (or tuples etc.) are paired.
CHAPTER
5
Data Structures
“It is a capital mistake to theorize before one has Data” – Sherlock Holmes
In this chapter, we learn about some very useful Python concepts. We shall
deal with some Python objects which are basically various forms of data
collections. They are iterables. There are the following built-in data structures
in core Python:
Data are represented in various forms. To understand them and to use them in
our study, we need to have clear ideas of the above data structures. Handling
Big Data is an important part in Data Science, Machine Learning, and in
studies of basic and social sciences, in fact virtually everywhere. Python
handles structured data very efficiently.
5.1 List
# List of numbers
>>> A = [3, 2.5, 4.0, -1, 5, 0] >>> type(A)
<class 'list'>
# List of names
>>> B =
[‘Sima’,‘Zakir’,‘Alex’,‘Mita’]
# Mixed list
>>> C = [10, [1, 2], ‘a2’, ‘*@’]
# Print or display
>>> print(A)
[3, 2.5, 4.0, -1, 5, 0]
>>> B
[‘Sima’, ‘Zakir’, ‘Alex’, ‘Mita’]
# List by list()function
>>> x = 'abc123' # String >>> list(x) # String to list ['a', 'b', 'c', '1', '2', '3']
# List from a range of numbers >>> list(range(5))
[0, 1, 2, 3, 4]
>>> list() # Empty List []
The online listing or help can be obtained through dir() orhelp(). We get to
know about various methods and attributes over list. Methods are some
functions that act on the list objects. Some of the methods will equally apply
on other iterables too. Besides, there are some built-in functions in
(__builtins__) which will also apply over list and other iterables.
5.1.1 Indexing
For the list,
A = [3, 2.5, 4.0, -1, 5, 0]
# Elements by indexing
>>> A[0] # position 0
3
>>> A[1] # position 1
>>> A[2]
4.0
>>> A[-1] # The last element 0
>>> A[-2] # The item before the last 4
5.1.2 Slicing
Slicing is to take out a part of a list while keeping the original list unchanged.
Slicing is done through indexing. The general rule of indexing for a list L is
to write:
The sliced list begins from ‘start’, proceeds in step-sizeof ‘step’ and goesup
to ‘stop’ but it won’t touch or cross ‘stop’ [It is as if to stop before the stop
signal!]. The colon (:) is a separator.
>>> A = [3, 2.5, 4.0, -1, 5, 0] # Item index: 1 to 3 in step 1 >>> A[1:4:1]
Note: We need not use a for-loop anymore to compute the sum of numbers.
The functionsum() does the same job over the a list of elements.
>>> max(A)
5
>>> min(A)
-1
# Maximum
# Minimum
# Sorting a list
>>> B =
[‘Sima’,‘Zakir’,‘Alex’,‘Mita’]
Note: For list C above, it sorted first the numbers and then the strings.
Note: D is a list of strings and the strings are of pure numbers. The sorted()
function first looked for the sign, then the digits from left to right and not the
total number inside the quotes.
A methodis a function that “belongs to” an object. (In Python, the term
method is not unique to class instances: other object types can have methods
as well. For example, list objects have methods called append, insert, remove,
sort, and so on.
The names with double underscores (__) are either list-attributes or of the
type of wrapper class [A Wrapper class is a class whose object wraps or
contains primitive data types.]. These are at the background of various
operations. For example, __add__does the ‘+’ operation for two lists, which
is concatenation.
Names without underscores refer to the functions applicable to list. These are
known as methods. Methods can work asattribute references to the object.
That means, we can refer list.method_name() where the method_name()
becomes an attribute to the list object.
[The concepts of class, object, attribute referencing etc. will be clear when
they are introduced in Chapter 4. For now, we may understand that a class is
a block of code written in a style and under which there can be functions,
parameters etc. Object is an instant of a Class. In other words, a Class is a
blueprint of an Object.]
We can seek online help to know about any method. This is called
method_descriptor. For example, if we want to know about the method
append() under the objectlist, we type:
>>> A = [3, 2.5, 4.0, -1, 5, 0] # Location of the element ‘2.5’ >>>
A.index(2.5)
1
>>> B =
[‘Sima’, ‘Zakir’, ‘Alex’, ‘Mita’]
# Location of ‘Mita’
>>> B.index(‘Mita’) 3
Note: The method, index() is in list. So, we can write list.index(A, 2.5),
list.index(B, ‘Mita’) etc. or as A.index(2.5) or B.list(‘Mita’) as attribute.
⟵ Reversed List
# Sorting
>>> A = [3, 2.5, 4.0, -1, 5, 0]
# Ascending order
>>> A.sort()
>>> A
[-1, 0, 2.5, 3, 4.0, 5]
# Descending order
>>> A.sort(reverse = True) [5, 4.0, 3, 2.5, 0, -1]
Note: The built-in function sorted(list) returns a sorted list and leaves the
original list unchanged. But the list methods sort() and reverse() return no
new list but alter the original list.
# Append element
>>> A = [3, 2.5, 4.0, -1, 5, 0]
>>> B =
[‘Sima’, ‘Zakir’, ‘Alex’, ‘Mita’]
>>> B.append(‘Ajanta’)
>>> B
['Sima', 'Zakir', 'Alex', 'Mita', 'Ajanta']
Application:
Output:
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
# Pop: Remove item with index
>>> A = [3, 2.5, 4.0, -1, 5, 0] # Pop out item from position 3 >>> A.pop(3)
-1 ⟵The removed element >>> A
[3, 2.5, 4.0, 5, 0]
[List changed, after item removed.]
# Delete a segment
>>> A = [3, 2.5, 4.0, -1, 5, 0]
Note: The del command is used to delete objects. The list is altered after
deleting the sliced part.
# Insert element
>>> A = [3, 2.5, 4.0, -1, 5, 0]
# Replace an item
>>> A = [3, 2.5, 4.0, -1, 5, 0]
# Count
>>> x = [1, 2, 3, 1, 2, 5, 6, 1]
Find out how many times ‘Head’ appears in 1000 trials. Let us assign two
numbers: Head = 1, Tail = 0. We generated a random list of 0’s and 1’s. Then
we apply count() over the list.
Exercise: Throw a Dice 10000 times. Find out how many times ‘6’ appears.
# Concatenation
>>> x = [2, 3, 5]
>>> 3 * x
[2, 3, 5, 2, 3, 5, 2, 3, 5] [This means: x + x + x]
# Convert to list
>>> list(x)
[2, 5, 8]
>>> list(range(10, 0, -2)) [10, 8, 6, 4, 2]
[In Python 2, range() creates a list directly. But there is another object called
xrange()in Python 2 which is equivalent to range() in Python 3.]
Example # 1:
Suppose, we have a list of numbers and we are asked to generate another list
from it where each item will be the square of the corresponding items of the
old list. How can we do that? The usual way is to write a for-loop as follows.
for i in x:
y.append(i**2) # Appending
print(y) # List of sq elements
Example #3:
# List of ��-values (in radian)
>>> from math import sin, pi
Example #4:
Separate out positive numbers
# With Logical if
Exercises:
1. Create a list of 100 random numbers between -1 and +1 and then create a
list of only positive numbers out of it.
2. Create a list of odd numbers from the list of numbers: 12, 19, 91, 36, 39,
41, 11, 9, 103
3. Create a list of negative numbers from the list: -12, 91, -0.5, 0.9, 31, -102,
1.4, 23, 50,
There can be lists inside a list. For example, consider the following list:M =
[[1, 2, 3], [4, 5, 6]], where M is a nested list. NowM has two elements each of
which is a list itself. But each of the elements contain 3 elements in them. Let
us try to check on interpreter.
The nested listM can be treated as a Matrix with 2 rows and 3 columns.
M = [[1, 2, 3], [4, 5, 6]]
5.2 String
A string, unlike a list, is immutable, i.e., we cannot alter it. We cannot add
any element to a given string. We can slice out a piece from a string but that
is another string. The original string remains unchanged.
>>> str(123)
'123'
>>> str([1, 2, 3]) '[1, 2, 3]'
5.2.1 Indexing
Let us consider the ‘string’. Positions of the characters:
String012345
-6 -5 -4 -3 -2 -1
Note: The indexing or the positioning of characters in a string is just like that
of a list.
>>> x = 'abcd1234'
5.2.2 Slicing
>>> x = 'abcd1234'
>>> x[1:6:2]
'bd2'
[Start = 1, Ends before 6, step = 2]
>>> x[2: ]
'cd1234'
[From index position 2 to end.]
>>> x[ : 6]
'abcd12'
[0, 1, 2, 3, 4, 5 elements]
>>> x[ : -1]
'abcd123'
[From start, stops before end]
# Application:
>>> x = 'abcd1234'
>>> type(x) # Type of the object <class 'str'>
# Length (Size)
>>> eval('234')
234
>>> eval('23.56')
23.56
>>> eval('23 + 56')
79
[Evaluates the string of sum.]
# Enumerate
print(i)
(0, 'a')
(1, 'b')
(2, 'c')
(3, 'd')
(4, '1')
(5, '2')
(6, '3')
(7, '4')
5.2.4 Concatenation
Concatenation means, two strings are joined endto-end.We do this with ‘+’
operator.
>>> x = 'abcd1234'
>>> x + x + x
'abcd1234abcd1234abcd1234' # Three strings to concatenate. >>> 3*x
'abcd1234abcd1234abcd1234'
>>> a = 'This is'
>>> b = ' ' # Space as str character >>> c = 'Python 3.'
>>> a + b + c
'This is Python 3.'
Note: We can know about the methods with online help(). For example, we
can write help(str.title) on interpreter (>>>) to know about this method.
True
# String of literals
>>> s = 'my name is abhijit'
# Convert each digit to str character >>> xx = [str(k) for k in x] ['1', '2', '3',
'4']
Application:
How many sentences are there in a Text? Consider a large text comprising of
many sentences. We can count how many sentences are there.
>>> story = "At the heart of all science – its experiments, its theories, its
mathematics, its discoveries, its interpretations – is the story instinct. The
scientific mind would be impossible without the story DNA, without the
story-seeing brain cells. The mind’s aspects do not operate in isolation. Every
human being immersed in the cyclorama of reality is implicated in the cosmic
story-making nature of reality. Maybe this story-making quality of reality is
what constitutes the heart of our existence. At every moment we are in a
micro or macro 'once upon a time' sea of existence. In every moment we are
part of the infinite stories that the universe is telling us, and that we are telling
the universe."
>>> len(story.split(‘.’))
8 ← The text has 8 sentences.
# Search a pattern
We can search any pattern inside a string through a module called re (re =
regular expression) in core Python.
>>> import re
>>> help(re.search)
Help on function search in module re:
search(pattern, string, flags=0) Scan through string looking for a match to
the pattern, returning a Match object, or None if no match was found.
Note: Look for other useful functions in re which can be used for other kinds
of search over a string. For example, re.findall(), re.match().
5.3 Tuples
# Examples of tuple
x = (2, 3, 0.5, 0)
subjects = ('phys', 'chem', 'math') mixed = (12, 'cold', [1, 2], (2, 3)) V = 2,
4, 8 # Without parenthesis
Note: An empty tuple is written as() and a tuple like (10, ) is a tuple with a
single element. [A comma is a must to indicate a non-empty tuple. Without
comma, (10) is just an integer.]
# Maximum
# Minimum
5.3.2 Indexing
The idea of indexing is the same as that of list or string. The positions of
elements are counted as0, 1, 2,… from left to right and as-1, -2, 3,…from
right to left.
>>> t[2] 6
>>> t[-1] # Element No.4
-9
>>> 6 in t # To search an element True
5.3.3 Slicing
5.3.4 Concatenation
>>> t1 = (1, 2, 3)
>>> t2 = (4, 5, 6)
>>> t1 + t2 (1, 2, 3, 4, 5, 6)
>>> t1*2
(1, 2, 3, 1, 2, 3)
5.3.5 Conversion
>>> x = [1, 2, 3, 4, 5] # List >>> tuple(x) # tuple from list
(1, 2, 3, 4, 5)
>>> y = (1, 2, 3, 4, 5) # Tuple >>> list(y) # list from tuple [1, 2, 3, 4, 5]
Note: Sometimes it is useful to convert tuple into a list because a tuple is not
mutable but a list is. Also note that the built-in functions like type(), len(),
sum(), max(), min() etc. are equally applied over list, string and tuple.
5.3.6 Methods
There are not many methods found on tuple. However, a tuple can be
converted into a list and then the list methods can be applied.
>>> dir(tuple)
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__',
'__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__',
'__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__',
'__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__rmul__', '__setattr__',
'__sizeof__', '__str__', '__subclasshook__', 'count', 'index']
Note: We can also use the built-in methods with double underscores on both
sides. For example, check with help(tuple.__sizeof__).
# Count
>>> t = (2, 3, 4, 1, 2, 5, 3, 2)
5.4 Set
{1, 2, 4}
# Binary String
>>> b = '110011101001'
Note: The{} is not an empty set. It is a different class called dictionary. As set
is unordered, so indexing and slicing cannot be done over a set.
Application:
# Python Script: A die is thrown 100 times. Make a set of outcomes.
import random
x = [random.randint(1, 6) for i in range(100)] print(set(x))
Output: {1, 2, 3, 4, 5, 6}
>>> s = {1, 2, 6, 9}
>>> len(s) # Length 4
5.4.2 Methods
>>> dir(set)
['__and__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__',
'__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__',
'__iand__', '__init__', '__init_subclass__', '__ior__', '__isub__', '__iter__',
'__ixor__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__or__',
'__rand__', '__reduce__', '__reduce_ex__', '__repr__', '__ror__', '__rsub__',
'__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__',
'__xor__', 'add', 'clear', 'copy', 'difference_update', 'intersection',
'intersection_update', 'isdisjoint', 'issubset', 'issuperset', 'pop', 'remove',
'symmetric_difference', 'symmetric_difference_update', 'union', 'update']
'difference',
'discard',
# Update
We can update a set with the union of itself or with any other iterables (tuple,
string, list, dictionary).
>>> s = {1, 2, 6, 9}
# Inserting a set {10}
>>> s.update({10})
>>> s # The set is updated {1, 2, 6, 9, 10}
# Remove an element
>>> s.remove(6)
>>> s # Returns unordered {0, 1, 2, 'x', '12', -1}
# x to add to y
>>> y |= x
>>> y
{1, 2, 3, 5, 6, 7, 8, 10}
>>> x - y
{1, 7}
>>> y - x
{8, 2, 10, 6}
# Symmetric Difference
>>> x ^ y
{1, 2, 6, 7, 8, 10}
>>> x.symmetric_difference(y) {1, 2, 6, 7, 8, 10}
>>> y.symmetric_difference(x) {1, 2, 6, 7, 8, 10}
5.4.5 Subset
>>> x <= y
One can do all set theoretic operations over frozen sets and apply methods as
are done with ordinary sets.
>>> s = {1, 2, 5}
# Frozen set
>>> s = frozenset(s)
<module>
s.update([7, 9])
AttributeError: 'frozenset' object
has no attribute 'update'
5.5 Dictionary
# Example
>>> D = {'a':2, 'b':4, 'c':6,
# Back to dict
>>> dict(D.items())
{'a': 2, 'b': 4, 'c': 6, ‘x’: 10} >>> D.keys() # Keys dict_keys(['a', 'b', 'c', ‘x’])
>>> D.values() # Values dict_values([2, 4, 6, 10])
Note: The attributes:D.items(), D.keys() and D.values() return iterable
objects like dict_items, dict_keys and dict_values respectively. We can print
them under a for-loop.
('a', 2)
('b', 4)
('c', 6)
('x', 10)
The keys and values can be anything - any number or an iterable object. For
example,
>>> C = {(0, 0): 10, (0, 1): 20, (1, 0): 30, (1, 1): 30}
>>> C.keys() # Keys as tuples
dict_keys([(0, 0), (0, 1), (1, 0), (1, 1)])
{}
# update
>>> D
{'a': 2, 'b': 4, 'c': 6, ‘x’: 10, 'd': 8, 'e': 12}
>>> D = {'a':2, 'b':4, 'c':6, ‘x’:10}
>>> D.update([('d', 8), ('e', 12)])
[Arg is a list of tuples.]
>>> D
{'a': 2, 'b': 4, 'c': 6, ‘x’: 10, 'd': 8, 'e': 10}
# Dictionary 1
>>> d1 = {'a': 10, 'b': 20}
# Dictionary 2
>>> d2 = {'c': 30, 'd': 40}
>>> D
{'a': 2, 'c': 6, 'b': 4, 'd': 10}
# Replacing value to an old key
>>> D['a'] = 20
>>> D
{'a': 20, 'b': 4, 'c': 6, 'd': 10}
D
NameError: name 'D' is not defined >>> DD
{'a': 2, 'b': 4, 'c': 6}
[Copy of D remains.]
A dictionary can be created by built-in object dict() from a list of pairs(x, y).
The pairs can be in the form of tuple or list or set etc. Each pair is converted
to a key:value item.
# items
>>> a = [(1, 10), (2, 20), (3, 30),
Examples
Application
# Problem: Given a nested Dictionary, iterate over the items and show the
keys and values.
people = {101: {'Name': 'Bhola', 'Age': '21', 'Sex': 'Male'}, 102: {'Name':
'Puchi', 'Age': '18', 'Sex': 'Female'}}
Output:
Id No.: 101
This is a very useful python concept that we often use in our codes. Any
iterable (list, string, tuple, set or dict) can be thought of as packing of some
elements under a variable name.
We can unpack the elements by assigning the variable name to equal number
of variable names. So, the corresponding elements will be assigned to the
variables one-to-one.
# Packing as dictionary
>>> D = {'x':2, 'y':3, 'z':5} >>> a, b, c = D # Unpacking >>> a
'x'
>>> b
'y'
>>> c
'z'
When two tuples or lists are compared, it checks the first elements of each
only. In case the first elements in both are equal then the second elements are
considered and so on. The output is returned in BooleanTrue and False.
>>> 5 in x1
False
>>> 0 in t2
True
Note:
The bool()of empty object (no argument) or bool(0) returns False, otherwise
True.
>>> x = 2
>>> y = x
>>> id(x)
140720007485120
>>> id(y)
140720007485120
However, this is not a real copy. We created a new reference for the same
object which is evident from the same identity for two names which we do
not realize. If we now assign x to another number, the id(x) becomes new but
the assumed copy, y retains the old value and identity.
The same is not true for a mutable object like list, tuple etc. Let us create a
listx, copy it to y and then alterx to see what happens.
# x changes
>>> x [10, 2, 3]
# y also changes
>>> y [10, 2, 3]
Note:y is not really a copy ofx. For real copy, we use built-in copy() or
importcopy module.
Shallow copy
>>> import copy >>> x = [1, 2, 3]
# Real copy y of x
>>> y = copy.copy(x)
We can now change any element of x or add new element to it, the copied
listy remains unchanged. However, this is just a shallow copy. Let us check
this for a nested list.
The shallow copy poses a problem for objects like a nested list. It creates a
new collection object but the nested elements (the child objects) are not
actually copied (This is only one level deep.), they refer to the old. So,
changing an element will also show up in the copy. However, if new
elements are appended to x, the old elements are not altered. Then y remains
unchanged.
Deep copy
The real deep copy is made when a new collection object is created and it is
recursively populated with the child objects taken from the original.
Exercises:
1. Write a one-line Python instruction to find out sum of all odd integers
between 1 to 1000.
2. Find the sum:∑��3, for�� = 1, … 100 by list comprehension.
3.
Check that the following sum:
∑
2��
3�� converges
towards 2, if we take �� = 1.
4. Given a dictionary {‘a’:10, ‘b’: -5, ‘c’:2,
‘d’:0.5, ‘e’: -1.5, ‘f’:13}, find out the
maximum and minimum values in this. 5. Create a list of odd integers
between 1 to 100.
Compute the sum of the numbers in the list. 6. Begin with an empty list, add
10 given
numbers, one by one, inside a for loop. Print
those numbers. Obtain the sum of squares of
those numbers. Print the sum.
7. Consider the List: L = [1, 4, 9, 16, 21, 25, 32,
48, 51]. Write a simple python program (may
be in one line) to obtain a new list which will
consist of only the odd integers.
8. A group of musicians named: ‘Ravi’, ‘Bikram’,
‘Zakir’, ‘Laksmi’, ‘Bhimsen’, ‘Rashid’, and
‘Ajay’, and their code numbers are 001, 002,
003, 004, 005, 006, and 007 respectively.
Create a dictionary with this information and
print to check the code of a certain musician. 9. Given the list of numbers:X =
[0, 3, -1,
10, 8, 7, 11, 100, -10, 36] and
find out the mean and median of the numbers. 10. Define the function
��(��)= ��2+ �� in Python
lambda function way. Use this function to make
a list of values for x in [0, 10] with list
comprehension.
11. The y-coordinate for a projectile motion:�� =
��
tan
�� −
��
Numbers.
def f(n):
if n == 0: return 0
elif n == 1: return 1 else: return f(n-1) + f(n-2)
n = int(input())
print(f(n))
Now modify the script to generate a list of 20
Fibonacci numbers.
34. The code:print(sum[(i**2 for i in
range(1, 20))]) may give errors. Why? Type this out on your interpreter and
try to understand from error message.
35. Write two programs that will ask the user for positive integers �� and
�� (with �� < �� ) as
second should use list comprehension. 36. Type the following lines of code
on the python
interpreter (in the order given).
a = [1, 2, 3, 4]
a.append(7)
a.extend([8])
b = a[1:3]
a.extend(b)
a.reverse()
c = a[-5:-1]
a.append(c[2])
What are the values of the lists a, b, and c now? Explain step by step. (You
may have to look up help(list) in the interpreter to see what the individual
commands do.)
37. Type the following lines of code and check what you get.
s ='He can carry a very big book'
t = s.replace('ry','xyz')
l = [0,1,2]
m = l.append(3)
u = '+'.join(l)
v = eval(u)
w = eval('*'.join[i**2 for i in range(1,5)])
CHAPTER
6
Class, Object, Module
“ Our object in the construction of the state is the greatest happiness of the
whole, and not that of any one class.” - Plato
The concepts of Class and Object are at the heart of any OOP language. A
class is a blueprint from which individual objects are created.
First, we create a Class ( i.e., the blueprint) and then we create objects from
it. So, a class can be thought of as a constructor of an object. On the other
hand, an object is an instance of a Class.
A class is created with the key word class. We can give any name to a class.
To understand better, we think of the Car example. Thousands of cars of
different types are made. Each car was built from the same set of blueprints.
A particular car is an instance (object) of the class Car.
# Empty class
Note:
We have created a trivial empty class Car [Imagine, we have just created the
structure of a car, no parts added yet.]. The key word pass isPython’s way of
saying, ‘move on’. Also, we have created two objectsp and q (at two memory
locations). Note that Car is a class butCar() is an object where nothing is as
yet to put as argument in the parenthesis.
Next, we include__init__() method in a class. This is generally used to create
objects by providing instance attributes. The __init__() method is
automatically invoked when an object is created from the class. We pass
attributes (variables, parameters) for a particular object (i.e., an instance of
the class) through this.
A Class function that begins (and ends) with double underscore (__) is called
built-in special function and it has a special meaning. There are built-in class
attributes like __name__, __doc__ etc.
We now add attributes, color and model through the __init__() method so
that we may have cars of various colors and models. Out of the three
arguments, the first argument self is common and it is a kind of pointer. We
pass two other arguments color and model through this pointer. These can be
thought
whereas wheels
of as instance acted as class attributes attribute.
# Documentation string for the class Car
>>> Car.__doc__
>>> Car.__doc__
wheeler cars.'
To Create Objects:
Now we create individual cars of various colors and models. The doc-string
for the class is the same for that of any object.
>>> C.color
blue
>>> C.model
Sedan
# Modify attribute
class One:
x = 10
class Two:
'This is my Class' x, y = 2, 3
z = x*y
class Three:
x = 10
def __init__(self, y):
def add(self):
z = self.x + self.y return z
def rms(self):
import math
d = math.sqrt(self.x**2 +
self.y**2) return d
>>> p = Five(3, 4)
>>> p.__doc__
"This is even a better Class" >>> p.add()
7
>>> p.rms()
5.0
Note: The first argument in any method can be anything in place ofself. We
may use any name we think asself is a dummy variable. But we prefer to use
self as convention. For example, the following class is as good as the above.
class Five:
"Here is some
modification over Class Five."
par.y**2) return d
6.1.1 Deleting Objects and Attributes
# Deleting variable
>>> del p.x
>>> p.x
Traceback (most recent call last):
<module>
p
NameError: name 'p' is not defined
Note: In the above, we deleted the objectp. In this way, the class remains but
the object is deleted only. One can create another object from the same class.
A class is a blueprint. So, in a way, the blueprint remains intact but the copies
of it can be created or destroyed.
6.1.2 Inheritance
• Parent Class
• Child Class
Inheritance allows us to define a class that inherits all the methods and
properties from another class. A child class is inherited from a parent class.
>>> p.__doc__
‘This is a child class.’ >>> p = Six(2, 3)
>>> p.x, p.y, p.z
(2, 3, 6)
# Method added
class Six(Five):
def __init__(self, x, y, z): # x, y inherited through Five
We have seen that we can print the elements of an iterable under a for-loop.
There are two built-in functions that make this iteration possible. They are
iter() and next(). For example,
>>> x = [2, 4, 6, 8]
# Make an iterator >>> xit = iter(x)
6
>>> next(xit)
8
>>> next(xit)
Traceback (most recent call last): File "<pyshell#405>", line 1, in <module>
next(xit)
StopIteration
In Python, any object, which contains the two methods __iter__(), and
next(), can act as an iterator.
class Numbers:
def __init__(self, n): self.i = 0
self.n = n
else:
raise StopIteration()
>>> x = Numbers(4)
Any Python Script is a module. Let us consider the following simple Python
script that is saved in our directory. This consists of a simple class only.
#1 Python Script:test.py
class MyClass:
"This is a test class" x = 3
y = x*x
s = 'My test Class’
# Attributes
>>> p.x
3
>>> p.y
9
>>> p.s
'My test Class'
class vector:
def __init__(self, vx, vy, vz): self.x = vx
self.y = vy
self.z = vz
Note:
The key word ‘self’ (It can be any name. But customarily, it is given.) is a
kind ofpointer (as used in C++). We incorporate the variables from the
__init__ function through this reference. For the arguments in__init__(self,
vx, vy ,vz), apart from the pointerself, the rest come as attributes.
>>> dir(algebra)
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__',
'__package__', '__spec__', 'math', 'vector']
>>> help(algebra.vector)
Help on class vector in module algebra:
6.3 Package
Packages are namespaces which can contain multiple packages and modules
themselves. They are simply directories, but with a twist.
Note:
Python can be used to write codes for functional computing as well as in the
way of Object Oriented Programming (OOP). In some chapters, we will
mostly concentrate upon writing short functional programming. Writing
codes in OOP way will be demonstrated too. However, anything in Python is
an object. So, we make use of the objects to write clean and simple codes for
our purpose of Scientific Computing.
CHAPTER
7
Drawing with Turtle
“The inside of a computer is as dumb as hell but it goes like mad!” – Richard
P. Feynman
Let us draw some geometrical figures by the Python module, turtle. This
preinstalled Python library enables users to create pictures and shapes by
providing them with a virtual canvas. Imagine that the cursor/ pen is the
‘turtle’. After we import the module, various functions in it are employed to
direct the turtle to move and draw as we wish. As we begin to type
instructions, a new window or a canvas is opened, and the figures are drawn.
# Directory
>>> import turtle
>>> dir(turtle)
Suppose this is a canvas (For demonstration only, not actual). The turtle
draws a line with a default arrowhead that moved from left to right along
xaxis (East to West) by 100 pixels (length of arrow). [Usually, the starting
position is at (0, 0), the centre of the canvas.]
Next, we may instruct the turtle to turn left (the arrow rotates left) by some
angle. Again, we can instruct the turtle to move some steps forward, then turn
right and so on. geometrical figures. Thus, we draw various
def square(L):
for i in range(4): fd(L)
lt(90)
return
# Some functions
fd() # forward: forward() bk() # backward: backward() lt() # left turn: left()
rt() # right turn: right() reset() # Reset Drawing Canvas pos() # Position of
Turtle goto() # Turtle to position home() # Turtle returns home. penup() #
Turtle lifts the pen. pendown() # Turtle downs the pen. reset() # Reset the
Canvas
>>> home()
speed(‘fastest’)
speed(‘fast’)
speed(‘normal’)
speed(‘slow’)
speed(‘slowest’)
Also, numbers from 1 to 10 as argument for various animation speed for
drawing and turning.
# Visibility
fd(100)
rt(90)
fd(100)
lt(90)
bk(100)
rt(90)
def triangle(L):
for i in range(3):
fd(L)
lt(120)
return
# Equilateral triangle of side 100 >>> triangle(100)
# Python Function: Hexagon def hexagon(L):
for i in range(6): fd(L)
lt(60)
return
# Draw hexagon of side 100 >>> hexagon(100)
# A general Polygon
fd(L)
lt(angle)
return
# Drawing polygons
We can create patterns with polygons. The functions square(), polygon() can
be used to create various designs. We can create background colors, draw
with colored pens etc.
speed(10)
for i in range(50):
polygon(100, 4)
lt(10) # Poly turns left by 10 deg
bgcolor('black') width(4)
penup()
goto(-200, 0) pendown()
square(100)
lt(90)
fd(100)
lt(90+30) fd(100)
rt(30+180)
fd(100)
lt(30)
fd(100)
rt(90+30)
fd(100)
rt(60)
fd(100)
Tips: Slow the speed and follow the steps to find out how the cube is being
drawn.
# Python Script: Coiling Squares
bgcolor('black')
pencolor('yellow')
width(3)
# Circle, Dot
# Circle of radius = 100
circle(100)
# Circle on downside
circle(-100)
# Arc that subtends angle 90 circle(100, 90)
# Dot (filled) of size 100
dot(100)
# Python Script: Patterns with Circles
for i in range(30):
circle(5*i)
circle(-5*i)
lt(i)
Fig.
Pattern with circles
# Python Script: Concentric Circles def con_circle():
for radius in range(100, 20,
20): circle(radius)
lt(90)
penup()
fd(20)
pendown()
rt(90)
return
con_circle()
# Python Script: Asterisk
width(2)
for i in range(5):
fd(200)
rt(144)
width(3)
star(6, 100)
# Python Script: Yellow star
begin_fill()
pencolor('black')
while True:
forward(200)
left(170)
if abs(pos()) < 1:
break
color('yellow')
end_fill()
L = 200
for i in range(100): square(L)
lt(10)
L = L*0.98
Fig. Nautilus
Random Walk
The turtle moves randomly with a certain step (step size given) in either of
the four directions (right, left, up and down). To move right or left, the turtle
moves through fd() orbk() functions. To move upwards, the turtle must turn
left by 90 degree and then to move forward. Similarly, to move down, it must
turn right by 90 degree and then to move forward.
# Python Script: RW
import random
dot(5) # Starting: Dot of size 5
for i in range(5000):
# Random integer 1, 2, 3, 4 n = random.randint(1, 4)
if n == 1:
fd(5)
if n == 2:
bk(5)
if n == 1:
rt(90)
fd(5)
if n == 2:
lt(90)
fd(5)
dot(5) # Ending: Dot of size 5
Fig. Random Walk (5000 steps) on a square grid. Two big dots are for
starting position and the end of journey of the turtle.
rt(angle)
Fig. RW with random turns. The turtle moves with fixed step size but it can
rotate by any angle between 0 to 360 degree. Evidently, the walk is not on a
square grid.
For a colorful presentation of the above one may try the following. The pen
color used in the following code is a combination of three primary colors Red
(R), Green (G) and Blue (B):pencolor((R, G, B)). Each of the arguments (R,
G, B) can take any value between 0 and 1 through the uniform random
number generator uniform(). For example, pencolor(0.3, 0.5, 0.1) creates one
particular (mixed) color. We can play around with various combinations.
# Python Script
for i in range(1000):
pencolor((uniform(0, 1), uniform(0, 1), uniform(0, 1))) angle =
randrange(360)
rt(angle)
Fibonacci Spiral
To draw the Fibonacci Spiral, we need to create squares with sides following
Fibonacci series.
We draw one square and then a corner-to-corner arc. The pen starts to draw
another square with arc from the last position and so on.
def sq_arc(L):
square(L)
circle(L, 90)
Fractals
def fact(n):
if(n == 0): return 1 return n * fact(n - 1)
# Koch Curve
Let us create a fractal by taking a line and applying some rule over it
repeatedly. Let the line be of length ��. Divide it into 3 equal parts:��/3.
Now take out the middle portion and replace it with two such segments which
are then joined to form an equilateral triangle. This task is repeated on all
segments through many generations. In the following, we show two
generations that evolved from a straight line on the left. [Named after
Swedish mathematician Helge von Koch.]
Fig. The
figures from left to right can be termed as Koch curves with order: 0, 1, 2.
Algorithm:
Take a straight line. Divide it into two parts: one goes left and the other goes
right. These are branches. Now each of the branches are similarly divided to
create more branches. This process continues up to some level. We form a
recursive function to create such a structure.
Algorithm:
1. Go forward by length: L
2. Turn left by 30 degree and go forward by a fixed fraction of L.
3. Turn right by 60 (30 + 30) degree and go forward by the fraction of L
4. Turn left by 30 degree and move backward by L.
5. Repeat the steps 2-4 until the length L reduces to L < 1.
else:
fd(L)
lt(30)
draw(L*f) # Branch on left rt(60)
draw(L*f) # Branch on right lt(30)
bk(L)
return
else:
fd(L)
color((u(0, 1), u(0, 1), u(0, 1)))
dot(5)
lt(45)
draw(L*f)
rt(90)
draw(L*f)
lt(45)
bk(L)
dot(5)
To save a figure
# For B&W
getcanvas().postscript(file =
# For Color
getcanvas().postscript(file = "tree.eps")
from PIL.Image import open
im = open('c:/Users/xx/tree.eps')
fig = im.convert('RGBA')
fig.save('tree.png')
Note:
• Create a postscript file (Here it is ‘tree.eps’) through getcanvas().
• Use the function calledopen() to open the file (with pathname) from Image
in PIL. Image is a module. PIL = Python Imaging Library.
• For B&W image: Save .eps file into a printable image file with save()
function. For Colored image: Convert the .eps file into a RGBA file (R =
Red, G = Green, B = Blue, A = Alpha). Then save the file. [Alpha for
opaque/transparent].
CHAPTER
8
NumPy: Numerical Python
“Number rules the universe”- Pythagoras
If you are working on Python IDLE interpreter, NumPy (and other external
packages) needs to be installed over it. The procedure is discussed in
Appendix. Various Python distributions like Anaconda, Python (x, y) etc.
come with NumPyand some other common packages like SciPy and
Matplotlib.
>>> type(np.array)
<class
'builtin_function_or_method'>
# Example
# Argument as list
>>> np.array([10, 20, 30]) array([10, 20, 30])
# Argument as tuple
>>> np.array((10, 20, 30)) array([10, 20, 30])
Note: The argument can be any iterable like list, tuple etc. Usually, we put a
list.
# Conversion of array to list >>> a = np.array([10, 20, 30])
>>> np.size(x)
3
Note: The default data type of the array isint32 = a 32-bit integer. The size of
each element in the array is 4 byte (= 32 bit). One remark on the above. The
methods np.size(), np.ndim() etc. are taken from NumPy whereas the
methods type(), len(), sum() are taken from built-in module in core Python,
all of these are basically functions.
>>> M.ndim
2
>>> M.size
6
>>> M.shape
(2, 3)
[Shape of the 2D array as a tuple]
Note: One could also write np.array([(1, 2, 3),(4, 5, 6)], the argument as list
of tuples or np.array(((1, 2, 3),(4, 5, 6))), the argument as tuple of tuples.
Axes
On a 2D array, we can go
along rows and columns.
If we go through different
rows (up-down) it is
[3, 4]],
[[5, 6],
[7, 8]]])
[Collection of two 2 × 2 arrays]
>>> D.ndim 3
>>> D.shape (2, 2, 2)
Note:
A 3D array is like a box (or container) which contains 2D arrays (or
matrices). The axes of a 3D array are shown in the adjacent
figure. The axis = 0, 1are along two directions on the shaded plane (which is
a 2D array) and the axis = 2 is perpendicular to that plane.
• int8 (1 byte = 8-bit: Integer -128 to 127), int16 (-32768 to 32767), int32,
int64
• uint8 (unsigned integer: 0 to 255), uint16, uint32, uint64
• float16 (half precision float: sign bit, 5 bits exponent, 10 bits mantissa),
float32 (single precision: sign bit, 8 bits exponent, 10 bits mantissa), float64
(double precision: sign bit, 11 bit exponent, 52 bits mantissa)
• complex64 (complex number, represented by two 32-bit floats: real and
imaginary components), complex128 (complex number, represented by two
64-bit floats: real and imaginary components)
Acronyms:
>>> y.dtype
dtype('float64')
dtype='<U3')
[Converted to all strings.]
Note:
• As it can be observed from above, the defaultdtype for integers is int32 (32
bit) and that for float is float64 (64 bit) while for complex it is complex128
(64 bits for real, 64 bits for image).
• Python always treats data in the broadest possible data type when it
encounters mixed data. It will treat all floats when floats and integers are
mixed. Complex, when complex and real numbers are mixed. Strings, when
numbers are mixed with strings. This is called upcasting.
We can enforce data type as keyword argument inside the array() function.
>>> x = np.array([10, 99, -6], dtype = int)
>>> x.dtype
dtype('int32')
>>> x = np.array([10, 99, -6], dtype = float)
>>> x
array([10., 99., -6.])
>>> x.dtype
dtype('float64')
Note: The string size is set to dtype =‘S4’ (strings cut down to size 4). Note
the effect of this on the string elements in the second array. In the display,
every string precedes with ‘b’ which stands for binary.
# Cast into float type >>> a.astype('float') array([2., 4., 6., 8.])
# Complex type
>>> a.astype('complex')
array([2.+0.j, 4.+0.j, 6.+0.j,
8.+0.j]) >>> a
array([2, 4, 6, 8])
[Original array unchanged.]
8.1.6 Methods for Creation of Arrays There are various methods in NumPy
to create arrays of numbers.
# 1 Range of numbers
np.arange(start, stop, step)
>>> help(np.arange)
……
arange(...)
Note: The array starts from ‘start’, proceeds in step of ‘step’ and stops before
‘stop’. We can read from the online manual and can understand how it works.
Notice that[start] and [stop] are written in square brackets while stop is not.
This means, the argument stop is only essential. If start andstop are not given,
they will assume default values (start = 0, step = 1).
# Examples
>>> np.arange(2, 10, 3) array([2, 5, 8])
# Default step = 1
>>> np.arange(2, 10)
array([2, 3, 4, 5, 6, 7, 8, 9])
Note: Unlike the built-in method range() in core Python, this NumPy method
arange() can create fractional numbers or floats. Also, we can impose
datatype through keyword (Default datatype is None.).
# 2 Linear space
linspace(start, end, num)
>>> help(np.linspace)
Help on function linspace in module numpy:
Note: The method linspace()creates a linearly spaced array. The array starts
at ‘start’, ends at ‘end’ and ‘num’ is the number of elements to be created.
# Examples
>>> np.linspace(10, 20, 5)
array([10., 12.5, 15., 17.5, 20.])
Note: Step size = (20 – 10)/4 = 2.5 [4 gaps]
# 3 Logarithmic space
logspace(start, end, num)
>>> help(np.logspace)
Help on function logspace in module numpy:
logspace(start, stop, num=50, endpoint=True, base=10.0,
dtype=None, axis=0)
Return numbers spaced evenly on a log scale.……
# Examples: logscale
# default base = 10
Note: The logspace() returns values in powers: values from100.2 to 101 for
L1 and 20.2 to 21 for L2. [Check,100.2=1.58489319 and so on.]
0.73333333, 1. ])
0.73333333, 1. ])
Note: Since the numbers through logspace() returns values in powers of base.
The numbers are back in linear scale when we take log again.
An array can be reshaped into different dimensions. The rows and columns
for a 2D array are referred through axes:axis = 0, axis = 1 respectively. For
3D or any higher dimensional arrays, one can refer axes asaxis = 0, 1, 2 and
so on.
# shape, reshape
[ 8, 10, 12]])
[ 6, 8],
[10, 12]])
>>> A
array([ 2, 4, 6, 8, 10, 12]) [The original array unchanged.]
6
>>> B.size
6
>>> A.resize(2, 3)
>>> A
array([[ 2, 4, 6],
[ 8, 10, 12]])
[The original array modified.]
Note: The reshape() function returns a new array with a modified shape
whereas the resize()function modifies the array itself.
Note: There is a difference between flatten and ravel though they do the same
job.
>>> y = x.ravel()
>>> y
array([1, 2, 3, 4, 5, 6, 7, 8, 9])
[ 4, 5, 6],
[ 7, 8, 9]])
[Original array changed.]
• On the other hand, x.flatten() returns a copy of the original array. So, if we
modify the converted array (flattened array), the original array is not affected.
Note: ravel() is faster than flatten() and it does not occupy any extra memory.
flatten() is comparatively slower than ravel() as it occupies memory.
>>> Y
array([[1, 0, 1],
[0, 0, 1],
[1, 1, 1]])
>>> np.unique(Y)
array([0, 1])
[Unique elements returned 1D array.]
Suppose we have got some large array of some numbers. We want to check
what the unique numbers are there that the array is made of. [To create an
example, we generate random numbers with Normal distribution taken from
the module random in NumPy. This will be discussed towards the end of this
chapter. Numbers are then rounded off with zero decimal point to suit our
purpose.]
>>> x =
np.round(np.random.randn(100), 0) >>> x
array([ 1., 1., -1., -0., 0., 1.,
-1., -1., -1., 1., -2., 0.,
Note: The iterator, np.nditer() iterates over an array to get the entries in a
sequence. The keyword argumentorder = ‘F’ means, it reads in FORTRAN
language style (reads column wise) and order = ‘C’ means, it reads in C
language style (reads row wise).
8.1.11 Indexing
8.1.12 Slicing
The slicing is done through position indices separated by colons [:]. For
example,
>>> x = np.array([ 2, 5, 8, 11,
Note: In the above, the starting index = 1, stops before index = 6, step = 2. It
does not touch or cross the end index. In this example, x[1] = 3, x[3] = 11,
x[5] = 17.
# Sliced in full
>>> x[:]
array([ 2, 5, 8, 11, 14, 17, 21])
# Sliced in full
>>> x[::]
array([ 2, 5, 8, 11, 14, 17, 21])
Note:x[:] and x[::] are the same. Also, we can write, x[2:] in place ofx[2::].
array([ 9, 8, 7, 1])
>>> X[2:] # Slicing of rows array([[ 6, 7, 8, 9],
[ 9, 8, 7, 1]])
>>> X[:3]
array([[0, 1, 2, 3],
[3, 4, 5, 6],
[6, 7, 8, 9]])
>>> X[::2]
array([[0, 1, 2, 3],
[6, 7, 8, 9]])
>>> X[:]
array([[ 0, 1, 2, 3],
[ 3, 4, 5, 6],
[ 6, 7, 8, 9],
[ 9, 8, 7, 1]])
>>> X[:, 0] # Slicing of columns array([0, 3, 6, 9])
>>> X[:, -1] # The last column array([ 3, 6, 9, 1])
Note: The slicing of rows and columns in a 2D array are done with a set of
colons, separated by comma, X[::, ::]
>>> A
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
>>> A
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
# Interchange columns 1 & 2 >>> A[:, [1, 2]] = A[:, [2, 1]] >>> A
array([[1, 3, 2],
[4, 6, 5],
[7, 9, 8]])
Test whether all array elements along a given direction evaluate toTrue. This
means, it is to check if all elements along a given axis is non-zero.
>>> A
array([[3, 6, 5], [4, 7, 6],
[5, 0, 2],
[4, 1, 9]])
>>> A
array([[6, 6, 2, 2],
[5, 1, 5, 5],
[8, 0, 6, 0],
[6, 8, 5, 9]])
>>> np.where(A == 0)
(array([2, 2], dtype=int64), array([1, 3], dtype=int64))
Note: The last output indicates the occurrence of ‘0’ at two places.
8.2.3 Put/Insert New Element
To include new elements in arrays, we can do that in the following two ways.
# Put
>>> a = np.array([0, -1, 2, 5, 10])
# As attribute
>>> a.put(3, 99)
>>> a
array([0, -1, 2, 99, 10]) [The array is changed.]
# Insert
>>> a = np.array([0, -1, 2, 5, 10])
# Inserts item at index 3 >>> np.insert(a, 3, 99)
array([0, -1, 2, 99, 5, 10]) >>> a
array([0, -1, 2, 5, 10]) [Original array unchanged.]
Note: The difference between two methods may be noted. The method
np.put() returns nothing. It alters the array itself. But inserting through
np.insert() returns a new array while keeping the old array unchanged.
= 0) array([[ 1, 2],
[10, 12],
[ 3, 4]])
Note:
For the arrays of higher dimensions (2D or more), the axis reference is
important. For a 2D array (like a matrix), we usually refer through rows and
columns. In a 2D array, if we go up-down (along rows), we call itaxis = 0, if
we go left-right (along columns), we call itaxis = 1. For even higher
dimensional arrays, for example, for a 3D array, we refer another direction by
axis = 2. The axis reference (in all NumPy methods for higher dimensional
arrays) is done through the keyword:axis.
Without axis reference, the output will be a flattened array (1D array).
>>> A = np.array([[1, 2], [3, 4]]) >>> np.insert(A, 1, [10, 12]) array([ 1, 10,
12, 2, 3, 4]) >>> np.insert(A, 2, 10)
array([ 1, 2, 10, 3, 4])
>>> X
array([[ 1, 2, 3, 4], [ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
>>> X
array([[ 1, 2, 3, 4], [ 5, 6, 7, 8],
[ 9, 10, 11, 12]]) [Original array unchanged.]
[ 5, 6, 8],
[ 9, 10, 12]])
array([4]), array([5])]
# Splitting into 2 equal parts
>>> np.split(x, 2)
[array([2, 3]), array([4, 5])]
# For 2D array
>>> X
array([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
>>> np.split(X, 3)
[array([[1, 2, 3, 4]]), array([[5, 6, 7, 8]]), array([[ 9, 10, 11, 12]])]
# Default axis = 0 ↓
Note: The output is a list of arrays resulting from splitting. Each array is of
the same dimension of the original array.
>>> np.vsplit(X, 3)
[array([[1, 2, 3, 4]]), array([[5, 6, 7, 8]]), array([[ 9, 10, 11, 12]])]
# Split horizontally
>>> np.hsplit(A, 4)
[array([[1],
[5],
[9]]), array([[ 2], [ 6],
[10]]), array([[ 3], [ 7],
[11]]), array([[ 4], [ 8],
[12]])]
= 0) ↓ array([[ 1, 2],
[ 3, 4],
[10, 12]])
Note: The arrayX remains unchanged. A new array is created. When axis is
specified, the value (to be appended) must have the correct shape. Otherwise,
there will be error. Without axis reference, no matter what the shape of the
value to be added is, it returns always a flattened array.
8.2.7 Concatenate
Join a sequence of arrays along an existing axis. Unlike that of list or tuple,
NumPy arrays cannot be concatenated with mathematical operator (+).
# For 1D arrays
[3, 4]])
>>> y
array([[5, 6],
[7, 8]])
>>> np.concatenate((x, y), axis = 1) array([[1, 2, 5, 6],
[3, 4, 7, 8]])
>>> np.concatenate((x, y), axis =
0) array([[1, 2],
[3, 4],
[5, 6],
[7, 8]])
# By default axis = 0 >>> np.concatenate((x, y)) array([[1, 2],
[3, 4],
[5, 6],
[7, 8]])
>>> x
array([[1, 2],
[7, 8]])
# Default axis = 0
>>> z = np.stack((x, y)) >>> z
array([[[1, 2],
[3, 4]],
[[5, 6], [7, 8]]])
# Stacking along axis = 1 (horizontal)
[5, 6]],
[[3, 4], [7, 8]]])
# Vertical stack
>>> np.vstack((x, y))
array([[1, 2],
[3, 4], [5, 6], [7, 8]])
[3, 4, 7, 8]])
Note: Thevstack() and hstack() do not lead to any shape change (no
conversion to higher dim.). These are equivalent to concatenation along the
axes. For stacking, the objects must match in shape in the direction of
stacking. Otherwise, it will raise errors. With vstack orhstack, we increase the
size of an array but not dimension.
[ 3, 4],
[10, 12]])
>>> z = np.array([[10],
[ 3, 4, 12]])
>>> x = np.array([10, 20, 30, 40, 50, 60, 70, 80, 90])
>>> np.sum(x)
450
>>> x.sum()
450
# Cumulative Sum
>>> np.cumsum(x) array([10, 30, 60, 100, 150, 210, 280, 360, 450],
dtype=int32)
# Sum of elements
# As attribute
# Cumulative Sum
10
>>> x.max() # Maximum
90
# Methods as attribute
Note: The axis reference is important for any NumPy method to be applied
over higher dimensional NumPy arrays.
# Mean, Median
>>> np.mean(y, 1)
array([20., 50., 80.]) >>> np.mean(y)
50.0
>>> np.median(y, 0) array([40., 50., 60.]) >>> np.median(y, 1) array([20.,
50., 80.]) >>> np.median(y)
50.0
Note: One can use the method, np.average() instead ofnp.mean(). The axis
reference is the same for all the methods. Also, note that ‘median’ cannot be
written as NumPy attribute. Application:
Fig. 1 The triangle with vertices: (1, 1), (2, 3), (3, 1)
The centroid is also shown at (2, 1.666 …). [The plotting can be done with
Polygon(points) or with plot(x, y) function frommatplotlib.pyplot module.]
>>> X = x.reshape(3, 3)
>>> X
array([[ 0. , 1. , -1. ],
[ 2. , 3. , -2. ],
[ 0.5, 1.5, 2.5]])
>>> X.var(0) # Along axis = 0 array([0.72222222, 0.72222222,
3.72222222]) >>> X.var(1) # Along axis = 1 array([0.66666667,
4.66666667, 0.66666667])
>>> X.std(0)
array([0.84983659, 0.84983659,
0.81649658])
>>> X.ptp(0)
array([2. , 2. , 4.5]) >>> X.ptp(1)
array([2., 5., 2.]) # Cov, Correlation Coefficient
[0.04262211, 1. ]])
Note: Two sets of 1000 random data points are taken. Correlation and
Covariance are seen among them. The Correlation Coefficient Matrix is
calculated with Pearson product-moment correlation coefficients between
two sets of data.
Application:
# Python Script
# For plotting
import matplotlib.pyplot as plt
plt.subplot(211) plt.plot(x, y)
plt.subplot(212)
plt.plot(x[:-1], z)
plt.show()
Fig. 2 The upper figure is for x-y curve and the plot below is the x-z curve
(derivative). Note that the maximum in the second curve occurs at the
inflexion point which is expected.
Note: In the above Python script, the slicing x[:-1] is done (while plotting the
figure at the bottom) becausenp.diff() generates an array whose length is one
less than the original. [Details about the plotting style, subplot etc. can be
found in Chapter 9 onMatplotlib.]
Exercise:
The position of a particle is given by the formula:
>>> A
array([[-1, -4, -2, 6], [-8, 3, 6, -4],
[-6, -4, 4, -6],
[ 9, -1, 3, -9]])
8.2.12 Sorting
>>> x = [-2, 0, 3, 1, 10, 5, -7]
# Ascending order
>>> np.sort(x)
array([-7, -2, 0, 1, 3, 5, 10])
# Descending order
>>> np.sort(x)[::-1]
array([10, 5, 3, 1, 0, -2, -7])
Note: The NumPy method sort() creates a new sorted list (or array) in
ascending order. Original list remains unchanged. To get a sorted list in
descending order, we used the NumPy slicing method to reverse the sorted
list (which resulted in ascending order).
>>> L
array([[-6, -4, 7, 5, 6], [ 0, 9, 1, 4, -1],
[ 4, 8, 5, 0, 2],
[ 8, 1, -2, 3, 6]])
>>> x
array([[ 1, 5, 20],
[ 5, 7, 64],
[ 3, 9, 97],
[ 2, 4, 38],
[ 4, 6, 90]])
>>> x[:, 0] # First Column array([1, 5, 3, 2, 4])
# Sorting of indices
>>> ind = np.argsort(x[:, 0]) >>> ind
array([0, 3, 2, 4, 1], dtype=int64)
[ 2, 4, 38],
[ 3, 9, 97],
[ 4, 6, 90],
[ 5, 7, 64]])
[ 1, 5, 20],
[ 4, 6, 90],
[ 5, 7, 64],
[ 3, 9, 97]])
The method argsort() returns the indices that would sort an array.
>>> L = np.array([10, 8, 12])
>>> x/y
array([0.2 , 0.33333333,
dtype=int32)
The operations between higher dimensional arrays are carried out in the same
way. Addition of two 2D arrays can be considered as mathematical Matrix
addition.
# Matrix Addition
>>> A
array([[1, 2],
[3, 4]])
>>> B
array([[5, 6],
[7, 8]])
>>> A + B # Matrix Addition array([[ 6, 8],
[10, 12]])
>>> np.add(A, B) # By NumPy method array([[ 6, 8],
[10, 12]])
Note: We can do operations between NumPy arrays and other iterables. The
items of the iterable will have operations with the corresponding elements of
the array. For example,
[13, 15]])
[13, 15]])
NumPy arrays of various shapes (or an array and a scalar) can be added
through broadcasting.
8.4 Broadcasting
If two arrays are of the same shape, then these operations are smoothly
performed. But if the dimensions of two arrays are dissimilar, elementto-
element operations are not possible. However, operations on arrays of
dissimilar shapes are still possible in NumPy, because of the broadcasting
capability. The smaller array is to broadcast to the size of the larger array so
that they have compatible shapes. Here are a few examples.
>>> x
array([[1, 2, 3], [4, 5, 6],
[7, 8, 9]]) >>> y = 10
>>> x + y
array([[11, 12, 13], [14, 15, 16],
[17, 18, 19]])
>>> x
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
[12]]) >>> y
array([[10],
[11],
[12]])
>>> x + y
array([[11, 12, 13],
[15, 16, 17],
[19, 20, 21]])
# Transpose
>>> M
array([[1, 2, 3],
[4, 5, 6]])
# Row-Column interchange
>>> M.T
array([[1, 4],
[2, 5],
[3, 6]])
[2, 5],
[3, 6]])
[By method: Transpose of array]
# Flipping
# Flipping left-right
>>> np.fliplr(M)
array([[3, 2, 1], [6, 5, 4]])
[1, 2, 3]])
# Rotating
Rotate an array by 90 degrees in the plane specified by axes.
[2, 5],
[1, 4]])
8.6 Matrix Object
The object np.matrix returns a matrix from an array-like object from a string
of data.
matrix([[1, 2, 3],
[4, 5, 6]])
>>> B = np.matrix([[1, 2], [3, 4], [5, 6]])
>>> B
matrix([[1, 2],
[3, 4],
[5, 6]])
# Matrix Product
[49, 64]])
8.6.1 Complex Matrix
# A Complex Matrix
>>> C
matrix([[1.+2.j, 0.+1.j], [2.-3.j, 0.+4.j]])
# Conjugate Matrix
>>> C.conjugate()
matrix([[1.-2.j, 0.-1.j],
[2.+3.j, 0.-4.j]])
[0.-1.j, 0.-4.j]])
# Conditional statement inside NumPy array
>>> X
array([[ 0. , 0.5, 1. ], [ 2. , 3. , 4. ],
[10. , -3. , 5. ]])
>>> np.empty(3)
array([8.48798316e-314,
1.69759663e-313, 2.54639495e-313]) >>> np.empty((2, 2), dtype =
[0., 1., 0., 0.], [0., 0., 1., 0.], [0., 0., 0., 1.]])
[0., 0., 1., 0.], [0., 0., 0., 1.], [0., 0., 0., 0.]])
>>> np.zeros(3)
array([0., 0., 0.])
>>> np.zeros((3, 3)) array([[0., 0., 0.],
[0, 0]])
# Full array (fill array with a number)
# Full Matrix
>>> np.full((3, 3), 5) array([[5, 5, 5],
[5, 5, 5],
[5, 5, 5]])
# Follow the shape of an array, fill with a number.
>>> A
array([[1, 2], [3, 4], [5, 6]])
[10, 10],
[10, 10]])
# Diagonal array
# Main diagonal
>>> np.diag((1, 2, 3)) array([[1, 0, 0],
[0, 2, 0],
[0, 0, 3]])
[0, 0, 2, 0],
[0, 0, 0, 3],
[0, 0, 0, 0]])
# Second off-diagonal (lower) >>> np.diag([1, 2, 3], k = -2) array([[0, 0, 0,
0, 0],
[0, 0, 0, 0, 0],
[1, 0, 0, 0, 0],
[0, 2, 0, 0, 0],
[0, 0, 3, 0, 0]])
Note: The sizes (orders) of the created arrays are automatically determined
by the argument. The length of the argument array is the length of the
diagonal where it fits in.
>>> A
array([[1, 2, 3], [4, 5, 6],
[7, 8, 9]])
>>> np.repeat(5, 8)
array([5, 5, 5, 5, 5, 5, 5, 5])
# Cross Product
>>> np.cross(u, v) array([ 2, -4, 2])
Note: The above methods can be treated asinner product (scalar product) and
cross product (vector product) between two vectors u and v.
Application:
Volume of a parallelepiped whose three sides are given by three vectors:
��⃗= 2��̂ − 3��̂, ��⃗⃗= ��̂ + ��̂ − ��̂, ��⃗= 3��̂ − ��̂
import numpy as np
A = np.array([2, -3, 0])
B = np.array([1, 1, -1])
C = np.array([3, 0, -1])
vol = np.inner(A, np.cross(B, C)) print(vol)
[3, 4]])
>>> B
array([[5, 6],
[7, 8]])
>>> np.inner(A, B)
array([[17, 23],
[39, 53]])
[Product between respective elements]
Note: Inner product between higher dimensional arrays is the sum of product
of corresponding elements on the axes. For example, in the above,
[43, 50]])
>>> np.dot(B, A) # BA array([[23, 34],
[31, 46]])
Note: We can also write as attributes, A.dot(B), B.dot(A).
# Product using special symbol >>> A@B # Matrix like Product
array([[19, 22], [43, 50]])
>>> A = np.array([[1, 2], [3, 4]]) >>> B = np.array([[5, 6], [7, 8]]) >>>
np.vdot(A, B)
70
[ 4, 5, 6],
[ 7, 8, 9]],
>>> T.shape
(2, 3, 3)
Note: Addition and Subtraction between two Tensors are done in the same
way as that between two mathematical matrices (or 2D NumPy arrays). If
there are two tensors �� and �� of same shape, the addition (or
subtraction) is,�� = �� + ��, where �� is a new Tensor of the same
shape.
The element-wise product between two Tensors of the same dimension will
result in a new Tensor with same dimension. Such product is called
Hadamard product and it is symbolically written as�� = �� ∘ ��. In
NumPy, if�� and �� are arrays (3D arrays) then the Hadamard product is
the usual elementwise product between two arrays, written as�� = �� ∗
�� [The same is true for Tensor divisions also.].
Tensor Product
�� = (��
11 ��12
��
21
��
22
), �� = (��11 ��12),
��21 ��22
�� = �� ⨂ �� = (��11∗ �� ��12∗ ��
=
��21∗ �� ��22∗ ��) ��
(
∗ (��11 ��12) ��12∗ (��11 ��12)11 ��21 ��22 ��21 ��22 ) ��21∗
(��11 ��12) ��22∗ (��11 ��12)
��21 ��22 ��21 ��22
Note that�� is represented as a 3D array in NumPy style (each element a
matrix):
��12
[��11∗ (��11 )]
��21 ��22
11 ��12
[�� ∗ (�� )]12 ��21 ��22
11 ��12
[�� ∗ (�� )]21 ��21 ��22
[
[��
22
��
∗ (��11 ��12)]]21 ��22
Note: A and B are tensors of rank 1 and the product is a tensor of rank 2.
Explanation:(1 2 3)⨂(4 5)=
1 × 4 1 × 5 4 5 (2 × 4 2 × 5) = (8 10)
3 × 4 3 × 5 12 15
array([[1, 2, 1],
[1, 0, 2]])
>>> np.tensordot(A, B, axes = 0)
array([[[[ 1, 2, 1],
[ 1, 0, 2]],
[[ 2, 4, 2], [ 2, 0, 4]],
[[ 3, 6, 3], [ 3, 0, 6]]],
[[[ 4, 8, 4], [ 4, 0, 8]],
[[ 5, 10, 5], [ 5, 0, 10]],
[[ 6, 12, 6],
[ 6, 0, 12]]]]) # Result is Tensor of rank 3
Note: Consult the online help for np.tensordot()to look at the other kinds of
tensor products set byaxes keyword:axes = 1 for tensor dot product andaxes
= 2 for tensor double contraction. The axes refer to the axis on which to take
sum of products (the usual axis reference ofndarray in NumPy).
>>> C = B.reshape(3, 2)
>>> D = np.tensordot(A, C, axes =
1) >>> D
array([[ 3, 10],
[ 9, 25]])
[Equivalent Matrix product]
Online help reads: ‘When there is more than one axis to sum over - and they
are not the last (first) axes of A (B) - the argument `axes` should consist of
two sequences of the same length, with the first axis to sum over given first
in both sequences, the second axis second, and so forth. The shape of the
result consists of the non-contracted axes of the first tensor, followed by the
non-contracted axes of the second.’
Applications:
Tensors and Tensor algebra are important mathematical tools widely used in
Mathematics, Physics and Engineering. There are techniques developed in
Deep Learning (in the area of Machine Learning) based on this. Each layer
(input, output, or hidden layer of Neural network) can be thought of as a
NumPy array. The multiple layers are connected through the weighted sum
where all the elements between two arrays (fully connected network) are
connected like a tensor product way. Google’s flagship libraryTensorFlow is
based on this idea.
The mathematical functions such as sin, cos, exp, sqrt etc. in NumPy are
called “universal functions” (ufunc). Within NumPy, these functions operate
elementwise on an array, producing an array as output. In other words,
functions in NumPy are all vectorized. Normally the user defined functions
(or the functions that we imported frommath, cmath modules) operate on
scalar (default argument is a single element). But in NumPy, the argument of
a function can be iterable likearray, list, tuple.
For example,
>>> x = array([1., 1.5, 2., 2.5])
>>> y = np.exp(x)
>>> y
array([ 2.71828183, 4.48168907, 7.3890561 , 12.18249396])
# Vectorized math operations
Note: All the functions in NumPy are vectorized whereas the functions with
the same names in math module (in core Python) are not. To see the
difference,
>>> a**2 # No vectorized op possible Traceback (most recent call last): File "
<pyshell#436>", line 1, in <module>
a**2
TypeError: unsupported operand type(s) for ** or pow(): 'list' and 'int'
Application
# Python Script to plot NumPy function
import numpy as np
# NumPy array created x = np.arange(-2, 2, 0.1)
# Another array with NumPy function y = x*np.exp(-x**2)
# For plotting
import matplotlib.pyplot as plt plt.plot(x, y)
plt.show()
array([0. , 0.47942554,
0.84147098, 0.99749499])
>>> np.ceil(x)
array([ 3., -3., 7., 13.]) [Ceiling of every number]
>>> X
array([[ 0. , 0.5, 1. ],
[ 2. , 3. , 4. ],
[10. , -3. , 5. ]])
>>> np.sin(X)
array([[ 0. , 0.47942554,
0.84147098],
[ 0.90929743, 0.14112001,
-0.7568025 ],
[-0.54402111, -0.14112001,
-0.95892427]]) >>> np.ceil(np.sin(X))
array([[ 0., 1., 1.], [ 1., 1., -0.], [-0., -0., -0.]])
<module>
f(x)
File "<pyshell#469>", line 2, in f return x**
TypeError: unsupported operand type(s) for ** or pow(): 'list' and 'int'
# Not vectorized
>>> math.sqrt(x)
Traceback (most recent call last):
<module>
math.sqrt(x)
TypeError: must be real number, not
list
# Vectorized now
>>> s = np.vectorize(math.sqrt) >>> s(x)
array([1., 1.41421356, 1.73205081,
2.])
8.11 Polynomial in NumPy
Note: In the above,p is an object created from the poly1d class. This is a
function so that we can write p(x). We can know the attributes of the object
as follows.
# Coefficients
>>> p.c array([1, 2, 3])
Fig. 5
Polynomial:
��2− 5�� + 6
[Two roots can
be identified
from the
crossing of
axis.]
x
8.11.4 Derivative
poly1d([2])
8.11.5 Indefinite Integral
1.5, 0., 0. ])
8.12 Scientific Modules in NumPy
• polynomial
• linalg (For Linear Algebra)
The polyfit() function in polynomial module will be used for curve fitting.
This will be discussed in detailin the ‘Numerical Methods’ chapter.
[Fromnumpy.org/doc]
Note: The various routines in the Polynomial package all deal with series
whose coefficients go from degree zero upward, which is the reverse order of
the Poly1d convention. There are polynomial power series, special function
polynomials and all that you can explore.
# Rank of Matrix
>>> np.linalg.matrix_rank(A) 3
# Inverse of Matrix
>>> B = np.linalg.inv(A)
>>> B
array([[ 0.14285714, 0.42857143,
1 2 −1 ��1 1
A = (2 1 4 ), X= (��2), and B= (2)
3 3 4��3 1
Output:
array([ 7., -4., -2.])
# Direct Solve
>>> np.linalg.solve(A, B) array([ 7., -4., -2.])
# Eigenvalues, Eigenvectors >>> A
array([[1, 2],
[3, 4]])
>>> np.linalg.eig(A)
Note: The eig() function returns a tuple of arrays. The first array is a
collection of two eigenvalues and the second array is a collection of two
eigenvectors. The two columns of the second array are two eigenvectors.
>>> u1 # Eigenvalue 1
-0.3722813232690143
>>> u2 # Eigenvalue 2
5.372281323269014
# Eigenvectors
>>> M = np.array([[1, 1+1j, 2j], [1-1j, 4, 2-3j], [-2j, 2+3j, 7]]) >>> M
array([[ 1.+0.j, 1.+1.j, 0.+2.j],
>>> np.linalg.eigh(M)
(array([-0.66765836, 2.91919537, 9.74846299]), array([[
0.78102045+0.j, 0.59215952+0.j, 0.19837883+0.j],
[-0.46839474+0.12569175j,
0.6949742 -0.0055707j,
0.23041631+0.47822181j],
[0.1713515 +0.3541944j,
0.34470175-0.21796245j,
0.35431906+0.74385218j]]))
Note: The function eigh() returns eigenvalues as real and the eigenvectors as
complex as it should be the case for a Hermitian matrix.
# Hermitian Conjugate
matrix([
[ 1.-0.j, 1.+1.j, -0.+2.j], [ 1.-1.j, 4.-0.j, 2.- 3.j], [ 0.-2.j, 2.+3.j, 7.- 0.j]])
[0, 1, 2, 3],
[0, 1, 2, 3]])
>>> Y
array([[0, 0, 0, 0],
[1, 1, 1, 1],
[2, 2, 2, 2]])
Note: The meshgrid(x, y) creates a grid where the coordinates of the grid
points are (0, 0), (0, 1)….(1, 0), (1, 1)….etc. Now we can define a function at
every grid point.
import numpy as np
import matplotlib.pyplot as plt x = np.linspace(-1, 1, 100) y = np.linspace(-1,
1, 100) X, Y = np.meshgrid(x, y)
Z = np.exp(-X**2 + Y**2)
plt.contour(X, Y, Z)
plt.show()
Fig. 6 Contour plot throughmeshgrid.
Dense grids can also be created through some other object in NumPy called
mgrid().
[1, 1, 1, 1],
[2, 2, 2, 2],
[3, 3, 3, 3],
[4, 4, 4, 4]])
>>> y
array([[0, 1, 2, 3],
[0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3]])
# For Contour plot
Plain text or datafiles are some files where data or text are written with space
separated or comma separated way. The files may show .dat and .txt
extensions, where .txt is often used for files containing plain text or data.
These files can be opened and viewed by simple text editor. We may put.dat
as extension to indicate a data file. But file extensions are not mandatory. In
Windows, files can be saved as Text or DAT file.
NumPy methods can handle files with pure numbers. The functionsavetxt() is
to save data in a file andloadtxt() is to read or load data from a file.
[ 0.25, -0.95, 0.07], [-0.09, 0.06, -0.28], [-0.68, 0.99, 0.49], [ 0.78, 0.63,
-0.02], [ 0.65, 0.09, 0.19], [ 0.68, 0.03, 0.09], [-0.17, 0.28, 0.84]])
When data are written with comma (,) separated way in a file, we need to
usedelimiter = ‘,’ as keyword argument.
The important thing is, we can create random arrays of any size and shape
directly by keyword arguments.
>>> dir(np.random)
['BitGenerator', 'Generator', 'MT19937', 'PCG64', 'Philox', 'RandomState',
'SFC64', 'SeedSequence', '__RandomState_ctor', '__all__', '__builtins__',
'__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__',
'__path__', '__spec__', '_bit_generator', '_bounded_integers', '_common',
'_generator', '_mt19937', '_pcg64', '_philox', '_pickle', '_sfc64',
'absolute_import', 'beta', 'binomial', 'bytes', 'chisquare', 'choice', 'default_rng',
'dirichlet', 'division', 'exponential', 'f', 'gamma', 'geometric', 'get_state',
'gumbel', 'hypergeometric', 'laplace', 'logistic', 'lognormal', 'logseries',
'mtrand', 'multinomial', 'multivariate_normal',
'negative_binomial',
'noncentral_chisquare',
'noncentral_f', 'normal', 'pareto', 'permutation', 'poisson', 'power',
'print_function', 'rand', 'randint', 'randn', 'random', 'random_integers',
'random_sample', 'ranf', 'rayleigh', 'sample', 'seed', 'set_state', 'shuffle',
'standard_cauchy', 'standard_exponential',
'standard_gamma', 'standard_normal', 'standard_t', 'test', 'triangular', 'uniform',
'vonmises', 'wald', 'weibull', 'zipf']
# Random number between 0 and 1 >>> import numpy as np
# 1D array of 10 numbers
>>> np.random.random(size = 10) array([0.80202971, 0.2630612,
0.33344721, 0.59599463,
0.73809468,0.73806876, 0.60964991, 0.16738648, 0.96946575, 0.6322321 ])
# Creating 3 × 4 array
>>> np.random.random((3, 4))
array([[0.72084356, 0.88093821, 0.83525373, 0.46332243],
# Uniform Distribution
The function uniform(low, high)draws
samples from a uniform distribution which are uniformly distributed over the
half-open interval [low, high) (includes low, but excludes high).
# Between 1 and 2
>>> np.random.uniform(1, 2) 1.9496816388917961
>>> np.random.uniform(-1, 1, 1000) [1000 random numbers created.]
import numpy as np
x = np.random.uniform(-1, 1, 1000) y = np.random.uniform(-1, 1, 1000) z
= x**2 + y**2 < 1
pi = 4*sum(z)/1000
print(‘pi = ‘, pi)
Output: pi = 3.136
# Normal Distribution
# With mean = 0, variance = 1 >>> np.random.randn(1000)
# With mu = 0.5, sigma = 2
>>> np.random.normal(0.5, 2, 1000)
To understand that the random numbers are drawn from some distributions,
we can collect the randomly generated numbers by some random number
generator and plot them in the form of histograms (Frequency distribution). A
histogram shows the character of the distribution. For the above, we
generated two sets of 100000 random numbers by uniform and Normal
random number generators and plotted side by side.
# Random integers
# 3 × 4 array of integers
>>> np.random.randint(10, 50, (3,
Note: Random integers between 10 and 50 where the upper limit is excluded
(half-open interval).
# Random Choice
>>> x = [1, 2, 3, 4, 'a', 'b', 'c',
Application:
>>> x
array([0, 9, 4, 8, 3, 6, 7, 5, 2, 1])
# Random Permutation
2])
>>> x
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) [Original array unchanged.]
# Start with a seed for the same sequence # Give any seed number
>>> np.random.seed(283)
>>> np.random.random()
0.5974151257398422
>>> np.random.random()
0.32662323744657806
Note: In case we start with any fixed integer as seed, we will get the same
sequence of (pseudo) random numbers.
Exercises
Write Python scripts for the following.
6185
and eigenvectors of this matrix. Form the diagonal matrix ���� out of the
eigenvalues. Check if the eigenvectors are orthogonal or not.
i) Find the norm of the eigenvectors thus obtained, ii) Show that the
eigenvectors are orthogonal.
19. A single data point is measured repeatedly, and the following values were
obtained:5, 5.5, 4.9, 4.85, 5.25, 5.05, 3.25. Find out the mean, variance,
standard deviation, sem (standard error to mean) for the set of values. You
can use appropriate functions fromNumPy package.
CHAPTER
9
Matplotlib: Plotting Library
“It is a capital mistake to theorize before one has data.”- Arthur Conan
Doyle
pip install matplotlib [See Appendix A]. On Linux, it is done through the
command: sudo. For detailed procedure, documentation, and tutorials, one
can look up the official Matplotlib webpage: matplotlib.org.
9.1 Plotting in 2D
9.1.1 X-Y Plots
Among various modules and functions in matplotlib, we here deal with the
pyplot module which contains functions that allow us to generate various
kinds of 2D plots [(x, y)-plot].
Fig. 1 Simple x-y plot. [If nothing is mentioned, the plot is by default with
continuous line and green colour (not reproduced here).]
The x- and y- data may be given aslist, tuple, array etc. We can write,
# Data as tuples
plt.plot((1, 2, 3, 4, 5), (1, 4, 9,
16, 25)) # Plot y using x as 0, 1, 2, .. (default) plt.plot(y)
The ‘ figure’ can be thought of as a single container and the ‘axes’ creates the
frame of axes with ticks etc. After this, we canrefer ‘fig’ and ‘ax’ every time
we add something. Most plt functions translate directly to ax methods such as
plt.plot ⟶ ax.plot(),
plt.xlabel() ⟶ ax.xlabel(), plt.xlim() ⟶ ax.set_xlim(), plt.title() ⟶
ax.set_title().
We can format the plot with various keywords (insideplot function) for line
style, line width, maker style, maker size etc.
Matplotlib functions
# Add title above the frame
title()
# Add title further above
suptitle() # Super title
# Add text at a given pos in figure text()
# Add text at a given pos
figtext() # in relative coordinates
# Add annotation
annotate() # with optional arrow
xlabel() Label to x-axis ylabel() Label to y-axis xlim() x-axis limits to show
ylim() y-axis limits to show xticks() Tick marks along x-axis yticks() Tick
marks along y-axis grid () Plot with grid
plt.xlim(min, max)
plt.ylim(min, max) plt.grid()
Marker Colors
b(blue), r(red), g(green), y(yellow), k(black), m(magenta),c(cyan), w(white)
We can create colors by mixing three primary colors RGB (Red, Green,
Blue). To customize, we need to give (r, g, b) as tuple. Also, we can setmec
= marker edge color.
To use:
• For colors: r, g, b = (0.1, 0.3,
Point ( .), Plus (+), Thick plus (P), Cross (x), Thick cross (X), Star (*), Solid
circle (o), Solid triangle up (^), Solid triangle down (v), Solid triangle left
(<), Solid triangle right (>), Solid square (s), Solid pentagon (p), Solid
hexagon (h), Solid hexagon of different orientation (H), Solid Octagon (8),
Solid diamond (D), Solid diamond thin (d), Vertical line (|), Horizontal line
(_), Tripod down (1), Tripod up (2), Tripod left (3), Tripod right (4).
Line Styles
-‘ (Continuous line), ‘-‘ (broken line), ‘-.’ ‘
(LineDot), ‘:’ (Dashed line)
# Application of colors
# color
Note: The argument dpi (dots per inch) refers to the physical dot density of
an image when it is reproduced as a real physical entity, for example when
printed onto paper.
The keyword arguments inplt.figure() that determine how a figure looks like.
# Example: Plot with styles plt.figure(figsize = (12, 8))
# x, y-axis labels
plt.xlabel(‘X-axis’, size = 20) plt.ylabel(‘Y-axis’, size = 20)
Legend
The legend() function places a legend (a box
inside the graph with label) on the graph (by default at the ‘best’ position).
We can position the legend with loc =‘upper left’, ‘lower right’, ‘upper right’,
‘lower left’ or indicate with numbers.
# Vectorize it
>>> f = np.vectorize(f)
>>> x = range(10)
# plot
>>> plt.plot(x, f(x))
Horizontal line
# Default h-line at y = 0 plt.axhline()
# h-line at y = 2
plt.axhline(y = 2, lw = 2, c = ‘r’)
# Introduce range set
plt.axhline(y = 1, xmin = 0, xmax = 4)
Vertical line
plt.axvhline()
18)
# Semi-Log y
t = np.linspace(0,1,100)
wt = 2*np.pi*t
x = np.sin(4*wt)
y = np.cos(wt)
r = np.linspace(0, 1, 100)
theta = np.linspace(0, 4*np.pi,
100) r = a + b*np.cos(theta)
plt.polar(theta, r, c = 'k', lw =
3) plt.xticks([0]) plt.yticks([0])
Fig. 14 Cardoid
# Polar plot 3 (Trigonometric function)
theta = np.linspace(0, 4*np.pi,
100) r = np.cos(theta)**2
plt.polar(theta, r, c = 'k', lw =
3) plt.yticks([])
plt.xticks([])
#plt.grid(False)
Suppose, we write some x-y data in a simple text/data file:test.dat [Name and
extension do not matter.] and store it in some location. We need to call it
through the path name. The data may typically look like as follows:
#xy
1 2.0
2 4.2
3 5.1
4 9.6
5 3.8
6 2.2
# To load data (with NumPy)
>>> data =
np.loadtxt(‘C:/Users/Abhijit/Deskt op/test.dat’)
>>> type(data)
<class 'numpy.ndarray'>
>>> data
array([[1. , 2. ],
[2. , 4.2],
[3. , 5.1],
[4. , 9.6],
[5. , 3.8],
[6. , 2.2]])
Fig. 16 Demonstration of how text and arrow are written inside a figure. The
labels (labels on x- and y-axes and inside the figure) can be represented by
Greek and Mathematical symbols.
Note:
• We can know about each of the functions xlim(), ylim(), text(), xlabel(),
ylabel(), arrow()by typing the name in help().
For example, type help(arrow)on interpreter.
• We can explore the default and keyword arguments by checking the online
manual. In short, the function text(position, label) writes text label inside the
figure at a position. The function arrow() draws an arrow from a point (x, y),
given by arrow(x, y, dx, dy) and so on.
• Also note that we can write Greek symbols and Mathematical expressions
in Labels as that is done in Latex. For example, we wrote r’$\theta^2$’ as a
raw string to produce ��2.
x, y = [], []
for row in open('test.dat', 'r'): a, b = [eval(k) for k in
row.split()] x.append(a)
y.append(b)
Note: To save a figure, one may also look for some clickable option (often at
the bottom) on the figurewindow.
Note:
The backslashes(\), that are part of Tex/ LaTex
type setting, are also used inside Python strings. So, to avoid conflict, a raw
string (A python string preceded by r) is used. For example, we may use a
Python string where ‘\n’ creates a new line, ‘\t’ a new tab etc.
����= \beta^k
����������= L^{ijkl}
��������= ��_{������} or A_{\rm{max}} [ \rm changes the
text into Roman}
Trigonometry:
sin �� = $\sin\theta$ cos �� = $\cos\pi$
Math Symbols:
∑���� \sum \x_i ∏���� \prod \x^k ∞ \infty ℏ \hbar
Math Expressions:
���� \frac{59}{86}����
(��) \left(\frac{5}{8}\right)��
Fig. 18 Multiple plots in the same figure with legends and line-styles
# Multiple plots with texts in Greek, Math
import matplotlib.pyplot as plt import numpy as np
# c = 0 is darkest black
plt.plot(x, f1, x, f2, c ='0', lw
= 2) plt.xlim(0, 2*np.pi)
plt.text(2.0, 0.55,
r'$\frac{1}{2}\sin\theta$', size =
>>> from itertools import cycle >>> c = cycle([5, 10, 15]) >>> next(c)
5
>>> next(c)
10
>>> next(c)
15
>>> next(c)
5
import numpy as np
from scipy.special import legendre import matplotlib.pyplot as plt from
itertools import cycle
x = np.arange(-1, 1, 0.01)
lines = ['.', '--', '-.',':', '-'] linecycle = cycle(lines)
Note: Two figures are indexed as figure(1) and figure(2) with two
plt.figure() instructions. Instead of this, if we would write plt.figure() once
(or nothing), we would get two plots in the same figure as in previous
example. If we write, for example, plt.figure(‘My Graph’), ‘My Graph’ text
will appear on the top of the frame.
With this figure() function to precede with, we can create as many figures as
we want under this, with the same Python script. We may then add plots to
any of the figures or change just by referring the index or text in the
corresponding figure(). However, if we want to plot a single or multiple
figures in a same graph, we need not use figure() function.
We divide the frame into rows and columns and the third argument is the plot
number. For example, we write subplot(2, 2, 1) orsubplot(221), in short.
This indicates that we have divided the space into �� × �� plots and this
graph is on plot no. 1. The plot numbers are counted row wise, from left to
right.
221 222Subplot: Plot No. 1 – 4 in the 223 224 2× 2 matrix.
# Python Script for subplot
import matplotlib.pyplot as plt import numpy as np x = np.arange(0, 4,
0.01)
plt.subplot(221)
plt.plot(x, x**0.5*np.exp(-x), lw
= 2) plt.subplot(222)
plt.plot(x, np.sin(x**2), lw = 2)
plt.subplot(223)
plt.plot(x, x**2, lw = 2)
plt.subplot(224)
plt.plot(x, x**4*np.exp(-x**2), lw = 2) plt.show()
Note: The plots are indicated as 221, 222, 223, 224, the last digit is the plot
number and the first two digits are number of rows and number of columns.
Four different functions are plotted.
Fig. 21 Subplots
[Plot 1:��1/2��−��,Plot 2: sin ��2,Plot 3: ��2, Plot 4:��4��
−��2.]
We can create Grid space with the module ‘gridspec’ from matplotlib. The
function GridSpec() creates grid layout to place subplots.
Note: GridSpec(3, 3) created a 3×3 layout. Next four lines are creating
subplots by slicing the columns and rows of the space (as in done in NumPy
array slicing).
x = range(5)
y = [1, 4, 16, 25, 42]
plt.errorbar(x, y, fmt = 'o', xerr
= 0.2, yerr = 0.3, c = '0.1')
# Plot Histogram
x = np.random.normal(1, 4, 100000) plt.hist(x, bins = 40, rwidth = 0.8,
histtype = ‘bar’) plt.show()
Note: bins = 40 indicates that 40 bins are created;rwidth = 0.8 indicates the
relative width of the bars as a fraction of bin width. Default histogram-type is
with bars. If we write, histtype = ‘step’, it will show the outline only. There
are many keyword arguments in hist() that can be checked with help(plt.hist)
which may be used to control the display.
Fig. 25 Histogram plotted with different style. The keyword, histtype = ‘step’
plots the outline only.
Note: There are many keyword arguments that can control the histogram. For
example, keyword, normed = 1 will normalize the distribution.
# Stacked Histograms
Stack plot
import pandas as pd
from numpy.random import uniform as u data = {'Kolkata': u(0, 1, 20),
'Delhi': u(0, 1, 20), 'Mumbai': u(0, 1, 20)} index = range(20)
# Data as DataFrame
D = pd.DataFrame(data, index) print(D.head())
y = np.vstack([D['Kolkata'],
# Bar positions
x = np.arange(len(men))
w = 0.4 # Width plt.bar(x, men, width = w, label =
Given a list of numbers (percentages for a set of items), the pie chart can be
made by plt.pie() function.
# Python Script for Pie diagram slices = [43, 20, 12, 9, 16] colors = ['0.7',
'0.3', '0.6',
Note: The meshgrid() function prepares a 2D grid on the x-y plane. In this
case, 10 discrete points each along x- and y-axes.
Fig. 32 The X-Y Grid
'black')
Fig. 33 Contour plot
# Contour map: another example
x = np.linspace(0, 1, 100)
y=x
X, Y = np.meshgrid(x, y)
Z = 2 * np.sin(np.pi*X) *
np.sin(np.pi*Y)
Fig. 35 Contour plot of some other kind with contourf() function. This
function draws the contour lines and fills the contours.
We can open a downloaded spread sheet (Excel file) with the csv file reader
from Pandas package and proceed.
import pandas as pd
D=
22.5 22.2 21.6 20.8 19.3
0 17.0 16.3 14.4 12.9 12.0
1 12.0 12.0 13.2 13.2 15.7
2 16.1 15.6 15.0 14.7 14.6
3 14.8 15.6 15.0 12.4 13.4
4 13.0 12.8 12.0 11.9 11.7
pd.read_csv(r"C:/Users/Abhijit/ Desktop/data_test.csv") D.head() #
Head of DataFrame
N = x.size
xi = np.linspace(x.min(), x.max(),
N) yi = np.linspace(y.min(), y.max(),
N) zi = si.griddata((x, y), z, (xi[None, :], yi[:, None]), method = ‘cubic’)
plt.contour(xi, yi, zi, cmap = 'gray')
Fig. 37 Contour plot with downloaded data Note: The data file can be read
with an appropriate file reader. For a plain text/ data file, we can use loadtxt()
in NumPy. However, for a spread sheet (an Excel file, for example) which
can be saved and treated as CSV (Comma Separated Values) file, can be read
through csv file reader, read_csv() in pandas module. The file read by
pd.read_csv() is a type which is known as DataFrame (a form of structured
data). Next, we converted a DataFrame into a NumPy array. Example #2
In this demonstration, data from a url are directly accessed and plotted. [The
following demonstration is ofjupyter notebook style.]
In [1]:
url =
'https://raw.githubusercontent.com /alexmill/website_notebooks/master
/data/data_3d_contour.csv'
data = pd.read_csv(url)
type(data)
Out[1]:
pandas.core.frame.DataFrame
In[2]:
data.tail()
Out[2]:
xyz
51 0.35 1.0 −0.45
52 0.50 1.0 −0.46 53 0.69 1.0 −0.48 54 0.85 1.0 −0.47 55 1.0 1.0 −0.49
In[3]:
table =
data.pivot_table(index='x', columns='y', values='z')
table.values.max() ,
table.values.min(), table.values.shape etc.
y_unique =
np.sort(data.y.unique())
X, Y = np.meshgrid(x_unique, y_unique)
Z = table.values.T
In [5]:
import matplotlib.pyplot as plt import matplotlib.cm as cm
fig = plt.figure()
ax = fig.add_subplot(111)
Fig. 38 Contour
plot with data from spread-sheet.
9.2 3D Plot
fig = plt.figure()
ax = plt.axes(projection = '3d') ax.contour3D(X, Y, Z, 80, cmap =
'binary') plt.show()
The number 80 as argument is for density of points. Also, there are other
keyword arguments which can be manipulated to create a 3D plot of choice.
We can create 3D scatter plot with ax.scatter(). A 3D triangulated surface can
be created with ax.plot_trisurf().
3D Bar Plot
fig = plt.figure()
ax = fig.add_subplot(111,
projection = '3d')
dy, dz, color = '#32CD32') ax.bar3d(xpos, ypos, zpos, dx, dy, dz, color =
'0.3') plt.show()
Additional Note:
If x- and y-arrays are available in the form that they correspond to the
coordinates of all the grid points, then we can readily plot them without
invoking meshgrid(). Only, criterion is that the x, y, z arrays are to be
reshaped into 2D arrays. For example,
ax.plot_surface(X, Y, Z)
plt.show()
Fig. 43 Surface plot demonstration with arranged data
Exercises:
1. Given a set of (��, ��) data in the form of a dictionary: {1:0.5, 2:3.8,
3:7.9, 4:16.5, 5:27.5}. Plot�� against�� with
4. Read a 2 column data file ‘mydata.d’. (a) Store the values in the array X
and Y, (b) Fit the data by the function �� = �� + �� , (c)��2
Report��, ��, (d) Plot the original data and the fitted curve superposed.
5. Generate a numpy array X within [0, 5]. Plot the following curves using
matplotlib for i) �� = (3��3− 2��4)/ (1 + ��2), ii) �� = sin2��,
iii) �� = ��1exp(−��1��)+ ��2exp ( −��2��) for ��1,
��2, ��1, ��2= [1, −1, 1, 2], [1, −1, 2, 1], [1, 10, 1, 1] and superpose
them with putting legends.
informative
CHAPTER
10
SymPy: Symbolic Python
In this brief chapter on SymPy, we try to introduce some essential ideas and
features. SymPy is a huge independent library. Many important mathematical
projects are developed based on this. The readers may explore them as and
when needed for their work. The following (inside the box) is from SymPy
documentation in
www.sympy.org/en/index.html.
About
>>> x**Rational(2, 3)
��2/3
# Expressions with trigonometric functions, Greek symbols
sin
(��)+
log(����)
√��
# Simplification of Mathematical expressions
3(��2+ 3)
# Evaluating expression
# Substitue 1 in place of x
>>> c.subs(x, 1)
−1
3
# Multiple substitutions
# Many symbols
>>> x, y, z = symbols('x, y, z') >>> a = x**2 + x*y + z
>>> a.subs([(x, 2), (y, -1), (z,
1)]) 3
# Numerical evaluation of a builtin function
>>> x = Symbol('x')
>>> f = lambdify(x, sin(x),
Note: lambdify() converts the function sin() taken from math module.
# Simplification of trigonometric and other expressions
>>> simplify(sin(x)**2 +
cos(x)**2)
1
>>> simplify(4*sin(x)*sin(pi/3 – x)*sin(pi/3 + x))
sin(3��)
# Factor cancellation
# Factorization
# Expansion
>>> expand(x*(2*x+3))
2��2+ 3��
>>> expand((cos(x) + sin(x))**2) sin2(x)+ 2sin(x)cos(x)+ cos2(x)
# Trigonometric Expansion
cot(x)+ cot(y)
# Rewriting Trigonometric expression
# tan(x) written in terms of sin(x) >>> tan(x).rewrite(sin)
2sin2(x)
sin(2x)
# Factorial, Binomial combination
(����)
# Simplification of combinatorial
>>> combsimp(binomial(n, n
1)/binomial(n, n-2)) 2
�� − 1
# Beta, Gamma function, Hypergeometric function
��
>>> gamma(z) # Gamma function Γ(��)
# Hypergeometric function >>> hyper([1,2], [3], z) ��12(1, 23) |��
>>> hyperexpand(hyper([1,2], [3], z))
−2��−2log(1 − ��)
Note: SymPy contains all kinds of special functions, Dirac Delta function,
Heaviside function, class of Singularity functions, all kinds of Gamma
functions (digamma, trigamma, polygama), error integral, Fresnel integral,
Sine integral, logarithmic integral, elliptic integral and many more.
1 −��
24
6+ ��120+ ��(��6)
# Algebraic equation, solve
[5−√13 , 5+√13 ]2 2 2 2
# System of equations, Solve
10.2 Matrix
# Column Matrix
>>> Matrix([1, 2, 3])
1
[2]
3
>>> Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
123
[4 5 6]
789
Matrix operations:
1
[4]
7
# Addition
# Addition of two matrices >>> A + B
224
[575]
10 9 13
# Product
>>> A * B 12 7 11
[27 16 23] 42 25 35
# Transpose
>>> A.T
147
[2 5 8]
369
# Power of Matrix
>>> A**2
30 36 42
[ 66 81 96 ]
102 126 150
# Determinant
>>> A.det()
0
>>> B.det()
4
300
[0 4 0]
001
>>> zeros(3) # Null Matrix
000
[0 0 0]
000
>>> ones(2, 3)
[1 1 1
1 1 1]
10.3 Calculus
10.3.1 Limit
>>> Limit(sin(x)/x, x, 0)
lim(sin(x)
x→0+ x)
# Direct evaluation
>>> limit(sin(x)/x, x, 0) 1
������(��)
# Substitute t = 0
>>> y(t).diff(t).subs(t, 0)
��
������(��)|��=0
# Differentiate twice w.r.t. t >>> y(t).diff(t, t) ��2
����2��(��)
>>> y.diff(t).subs(t, 1) 7
Also, one can write: >>> diff(y, t)
3��2+ 4��
10.3.3 Integration
# Indefinite Integration >>> integrate(exp(x)*sin(x))
����sin (��)−����cos (��) 2 2
# Double Integral
>>> x, y = symbols('x, y')
>>> Integral(exp(- x**2 - y**2), (x, 0, oo), (y, 0, oo))
∞∞
∫ ∫ ��−��2−��2��������
00
x = Symbol('x')
f = lambdify(x, sin(x**2),'numpy') import numpy as np
import matplotlib.pyplot as plt
xvals = np.arange(0, 10, 0.01) yvals = f(xvals)
plt.plot(xvals, yvals)
Fig. 1: Plotting function with lambdify and using Matplotlib library.
Note: The function sin() was taken from NumPy.
>>> x = Symbol('x')
# Directly plots the expression >>> plot(x**2)
Fig. 2: Plotting 2D with SymPy plot
>>> x = Symbol(‘x’)
# Plots Mathematical function
>>> plot(sin(x)/x)
Fig. 3: Plotting 2D with SymPy plot
The online help and the Plotting Function Reference page in the SymPy
documentation shows various arguments through which we can control the
plot: ‘line_color’, ‘title’, ‘label’, ‘xlabel’, ‘ylabel’, ‘xscale’, ‘yscale’,
‘axis_center’, ‘xlim’, ‘ylim’, ‘annotations’, ‘markers’, ‘rectangles’, ‘fill’,
‘adaptive’, ‘depth’,
‘nb_of_points’, ‘size’ etc.
# Control x-scale
>>> plot(x**2*exp(-x), (x, 0, 8))
Note: plot() creates a tuple. The 0-th element is the plot. So, we took p2[0] to
append.
Fig. 6: Plotting 2D with SymPy plot. Merge plots and show.
# Triangle (a, b, c)
# Ellipse
(center, hradius, vradius)
>>> e = g.Ellipse((0, 0), 5, 2) >>> e.area
10.��
# Parameters of Ellipse
20.E(49 )100
>>> e.euation(x, y)
0.078..��2+ (��−1)2− 15 5
>>> m, g, k = 1, 9.8, 1
>>> t = Symbol('t')
>>> v = Function('v')
>>> sol = dsolve(Eq(m*v(t).diff(t)
>>> C1_value =
solve(sol.rhs.subs(t, 0))[0]
>>> C1_value # Value of ��1
-9.8
>>> C1 = Symbol('C1')
>>> sol = sol.subs(C1, C1_value) >>> sol
��(��)= 9.8 − ��1��−1.0��
>>> t = Symbol('t')
>>> y = Function('y')
>>> equation = Eq(y(t).diff(t,t) +
# Derivative of solution
>>> y1 = sol.rhs.diff(t)
>>> y1 # ��′ ��(��)= ��1cos(t)− ��2sin(t)
>>> sol.rhs.subs(t, 0)
��2
>>> sol.rhs.diff(t).subs(t, 0) ��1
CHAPTER
11
Pandas: Structured Data
“All good art is abstract in its structure.” – Paul Strand
We often receive data as spread sheets or as various other tabular forms. The
external module Pandas has mechanism to handle such structured data.
# Imposing index
>>> index = ['a', 'b', 'c', 'd'] >>> s = pd.Series(x, index) >>> s
a 10
b 20
c 30
d 40
dtype: int32
>>> s[0]
10
>>> s[‘a’]
10
dtype(‘int32')
# New row added
>>> index = [‘a’, ‘b’, ‘c’, ‘d’] >>> pd.Series(10, index, int) a 10
b 10
c 10
d 10
dtype: int32
a 10.0
b 20.0
c 30.0
d 40.0
e NaN
Note: NaN = Not a Number. This is Python’s way of putting this object
whenever there is a missing data or misunderstood data.
11.2 DataFrame
# Argument is a 1D list/ array
0
0 10
1 20
2 30
3 40
Note: The tabular structure is created with default row index: 0, 1, 2, 3 and
column index: 0.
# With 2D list as argument
Note: The tabular form with row index: 0, 1 and column index: 0, 1, 2, 3.
# With row and column names
columns)
# DataFrame ‘d’ is created >>> d
ABCD
a 10 20 30 40
b 50 60 70 80
>>> d[‘A’]
a 10
b 50
>>> d[‘A’][‘a’]
10
We can test them on interpreter (or look up the online help). For example,
>>> d.axes
[Index(['a', 'b'],
dtype='object'), Index(['A', 'B', 'C', 'D'], dtype='object')]
>>> data = {'a': 10, 'b': 20, 'c': 30, 'd': 40}
>>> index = [1, 2, 3, 4]
>>> pd.DataFrame(data, index) a b c d
1 10 20 30 40
2 10 20 30 40
4 10 20 30 40
>>> data = {'a': [10, 15, 20, 25], 'b': [30, 35, 40, 45], 'c': [50, 55, 60, 65],
'd': [70, 75, 80, 85]}
>>> d
xy
a 2 10
b 4 20
c 6 30
d 8 40
# Calling by row index and columns
>>> d['x']
a2
b4
c6
d8
Name: x, dtype: int64
>>> d['x'][‘b’] 4
# DataFrame from Dictionary of Series
>>> index = ['a', 'b', 'c', 'd'] >>> s1 = pd.Series([5, 8, 9, 11], index)
# Slicing
>>> D[1:3]
A B C b 8 6 20
c 9 4 30
>>> D[1:3]['A']
b8
c9
Name: A, dtype: int64
# Append
>>> D1 = pd.DataFrame([[-1, 0, 2]], index = ['e'], columns = ['A', 'B',
'C']) >>> D1
ABC
e -1 0 -2
# Append DataFrame
>>> D.append(D1)
ABC
a 5 7 10
b 8 6 20
c 9 4 30
d 11 3 40
e -1 0 -2
# Delete
'to_msgpack',
'to_pickle',
We can seek help of any file reader by typing on interpreter. Type example.
Let us
Note: Before the file to be read byread_csv(), the Excel sheet should be saved
in the csv format. [If the file is not in the same directory, we need to write the
file name along with the path name: ~/Downloads/covid.csv.]
The data-file that is read by the csv reader, is a DataFrame. We can check the
head and tail [by default first and last 5 lines] of the DataFrame. We may
check any number of lines from the top or end throughdf.head(10),
df.tail(50). # Convert DataFrame to NumPy array
>>> L = df.to_numpy()
>>> L
array([[nan, 'Thailand', 15.0, ..., 411L, 599L, 599.0],[nan, 'Japan', 36.0, ...,
1007L, 1086L, 1086.0],[nan, 'Singapore', 1.2833, ..., 432L, 455L, 455.0],
...,
[nan, 'Republic of the Congo', 1.44, ..., 0L, 0L, 0.0],[nan, 'The Bahamas',
24.25, ..., 0L, 0L, 0.0],[nan, 'The Gambia', 13.4667, ..., 0L, 0L, 0.0]],
dtype=object)
# Reading a plain text/ data file Following Data File (a plain text file:
test.dat):
Note: Once the data file is read asDataFrame, one can convert this into a
NumPy array or a Python list. [FromDataFrame to List:
df.values.tolist()]
# 1 Prime Numbers
Prime numbers are those integers which are not divisible by any number
except 1 and the same number. For example, 3, 5, 7, 11, 13…. are prime
numbers.
By Primitive Algorithm
Algorithm:
1. Check if the numbern is divisible by any integer between2 to n-1. If so, the
number is not prime. Break away.
return ‘Prime’
Note: To exist from the loop, we used sys.exit() fromsys module. We can
also use the built-in key word break (operates under a loop) instead of
importing sys module.
def prime(n):
for i in range(2, n): if n%i == 0:
return 'Not Prime' break
else:
return 'Prime'
Note: In this case, else is used with respect to the for loop.
With Faster Algorithm
36 12
2 × 18 2 × 6
3 × 12 3 × 4
4 × 9 √12 ×√12
6 × 6 ←√36 4 × 3
9×46×2
12 × 3
18 × 2
# Python Script:
# Prime Number (faster algorithm) from math import sqrt
def prime_fast(n):
max = int(sqrt(n))
for i in range(2, max+1):
Exercise: Find out how many prime numbers are there within a given range
[100, 100000].
Count Prime Numbers from a List
# Python Script:
# Number of primes in a range
“““
To count number of primes up to some number, we should count only odd
numbers, for a faster computation, as even numbers are not prime. We start
from 3, as 1 is not a prime. We print count + 1 as 2 is even but a prime. The
computer time is also checked.
”””
import time
t1 = time.time() # Clock starts L = range(3, 100000, 2)
count = sum([prime_fast(n) for n in
# 2 Strong Numbers
A strong number is some integer where the sum of factorials of its digits
equals to the number itself. For example: 145 = 1! + 4! + 5! We find out
strong numbers between 1 and some large number.
Algorithm:
in N]) if x == N: print(‘Strong’)
Exercise: Discover how many strong numbers are there up to 1000000.
# 3 Max, Min
Algorithm:
# Python Script: # To Find Max and Min L = [2, 6, 9, 0, -1] max, min =
L[0], L[0]
for i in L[1:]:
if i >= max: max = i if i <= min: min = i
print('Max = ', max, 'Min = ', min)
Note: L[0] = 2, is the 0th element of the list. L[1:] is the sliced list of
elements from index 1 to the end.
def maxi(L):
mx = L[0]
for i in L[1:]:
list (or tuple). We note the execution time. For a small list, the difference may
not be noticeable. But for a very large list of numbers, the difference (in
execution time by the two ways) will be appreciable.
# Final time
t2 = time.time()
return maximum, t2 - t1
Exercise:
Use the test_time() function to compute difference in times taken bymaxi()
function (user defined) and max() function (built-in).
Note:
The‘while True:’ is an infinite loop. It can
x=1
while x < 5: print(‘Continue’) while True:
print(‘Continue’)
# 4 Sorting a List
Bubble Sort
First Pass
[6, 2, 9, 0, 1] ⟶ [2, 6, 9, 0, 1] Swap
[2, 6, 9, 0, 1] ⟶ [2, 6, 9, 0, 1] No swap
[2, 6, 9, 0, 1] ⟶ [2, 6, 0, 9, 1]
Swap
[2, 6, 0, 9, 1] ⟶ [2, 6, 0, 1, 9]
Swap
Second Pass
[2, 6, 0, 1, 9] ⟶ [2, 6, 0, 1, 9]
No swap
[2, 6, 0, 1, 9] ⟶ [2, 0, 6, 1, 9]
Swap
[2, 0, 6, 1, 9] ⟶ [2, 0, 1, 6, 9]
Swap
Third Pass
[2, 0, 1, 6, 9] ⟶ [0, 2, 1, 6, 9]
Swap
def bubble(L):
n = len(L)
for i in range(n):
for j in range(n-i-1):`
if L[j] > L[j+1]:
# Swap
L[j], L[j+1] =
The bubble sort algorithm can be implemented with a flag that will keep track
of the swaps. If no more swap is required for a subsequent pass, it can break
away. This way we can save computation time. This is only refinement.
def bubble_flag(L):
n = len(L)
for i in range(n):
flag = 0
for j in range(n-i
1): if L[j] >
L[j+1]:
flag = 1
L[j], L[j+1]
1. Start from the 2nd element. If it is smaller than the 1st, then swap.
2. Go to the 3rd element. First, compare it with the 2nd, if smaller, then
swap. Next, compare with the 1st and do the same.
3. Repeat Step 2 for all subsequent elements. [Each element must be checked
with all preceding elements.]
def sort_insert(L):
n = len(L)
for i in range(1, n):
key = L[i]
j=i-1
while j >= 0 and L[j] >
# Descending
>>> sorted(L, reverse =
True) [100, 15, 10, 9, 1, 0, -2,
12]
>>> L
[10, -2, 0, 1, 100, 15, 9,
# Descending
>>> L.sort(reverse = True) >>> L
[-12, -2, 0, 1, 9, 10, 15,
We can test various sorting functions by comparing the execution times taken
to complete a job. Let us create a large array of numbers by random number
generator inNumPy (This external package will be introduced in the next
chapter.). Also, let us import the module time to keep track of time.
import numpy as np
import time
L = np.random.uniform(-1, 1,
[Evidently, the built-in function and list method for sorting must be based on
some better algorithm than bubble and insertion sort algorithms. There are
many efficient sorting algorithms like Quick sort, Bucket sort, Tim sort and
others which are fascinating but those are beyond the scope of this book.]
Exercise: Create a list of 100,000 random numbers between 0.5 and 2.5 and
sort them in ascending (and in descending) order. Use the defined functions
by Bubble sort, Insertion sort and the built-in methods. Note the execution
times taken by various methods and conclude.
# Python Script:
# Roots of Quadratic Equation from math import sqrt
a, b, c = eval(input('Enter a, b,
if X > 0:
print('Distinct Real roots = ', (-b + p)/q, (-b - p)/q) if X == 0:
print('Equal Real roots = ', b/q, -b/q) if arg < 0:
print 'Complex roots = ', complex(-b, p)/q, complex(b, - p)/q
mean = sum(x)/n
var = sum(y)/n - mean**2 stdev = math.sqrt(var)
n = len(x)
xav = sum(x)/n yav = sum(y)/n
Output
Corr Coefficient = 0.910366477463
Note: The zip(x,y) function makes a list of (x, y) tuples taking respective
elements from the corresponding lists x and y.
# Example: zip()
>>> x = [1, 2, 3]
>>> y = [4, 5, 6]
>>> zip(x, y)
[(1, 4), (2, 5), (3, 6)]
12]) r = np.corrcoef(x, y)
print (‘Corr Coeff = ‘, r)
Output
Corr Coeff =
[[1. 0.91036648] [0.91036648 1. ]]
Note: In the last output, the 4 elements (in matrix form), are to be read
as:������, ������, ������, ������.
# 8 Infinite Series
Exponential Series
Taylor series expansion:
exp(��)= 1 + �� +
��2 ��3
2!+3!+⋯.∞
Algorithm:
1. Input:��, tolerance value (accuracy level)
2. Initial values: sum = 0, term = 1.0
3. Loop starts.
4. Inside loop: update sum, update term
new = old
×
��
��
5. Loop continues until condition is fulfilled.
# Python Script: Exponential Series import math
x, tol = eval(input('Enter x and tolerance \n'))
sum += term
term = term*x/n
n += 1
print('Calculated Value, Actual Value') print(sum, math.exp(x))
Input/Output
Cosine Series
Taylor series:
cos(��)= 1 −
��2 ��4 ��6
2!+4!−6!+⋯∞
Algorithm:
45, 0.001
Calculated Value, Actual Value
0.70742920671 0.707106781187
Output:
3 2.5
4 2.6666666666666665
5 2.708333333333333
6 2.7166666666666663
7 2.7180555555555554
8 2.7182539682539684
9 2.71827876984127
#
Cosine series:
cos �� =∑
����2�� ��=0 (2��)!
∞ (−1)
n=n+1
print(n, C(x, n))
Output:
3 0.501796201500181
4 0.4999645653289127
5 0.500000433432915
6 0.4999999963909432
Exercises:
Write python codes to compute the following series sums. Set tolerance value
= 0.0001.
1. Sine Series:
sin (x) = �� −��
357
3!+��5!−��7!+ ⋯ ∞2.
Logarithmic Series:
(1 + ��)= �� −��
23
log�� 2+��3− ⋯ ∞
Verify this:log��2 = 1 −1+1−1+ ⋯ ∞2 3 4 3. Walli’s formula:
��= lim (2.2.4.4.6…2��−2. 2��. 2��). 2 ��→∞ 1 3 3 5 5 2��−1 2��−1 2��+1
Verify this numerically.
4.
Verify the Fourier
series:
�� = 2 (
sin�� 1 − sin
2�� sin3��
+3 − ⋯ ) 2
# 9 Digits of a Number
This means that in the Gregorian calendar, the years 2000 and 2400 are leap
years, while 1800, 1900, 2100, 2200, 2300 and 2500 are not leap years.
Algorithm:
# 11 Collatz Sequence
Algorithm:
Input/Output
Enter Number
12
6 3 10 5 16 8 4 2 1
This is to find the Greatest Common Divisor (GCD) and Lowest Common
Multiplier (LCM) of two positive integers by Euclid method. First, we divide
the bigger number by the smaller number and consider the residue. If the
residue is zero, then the smaller number is the GCD. If this is not the case, we
take the nonzero residue as the smaller number and previous smaller number
to be the bigger number. This is continued until we get a zero residue. Then
at the last step, the divisor is called the GCD. LCM is just obtained by
dividing the product of two original numbers by GCD.
Algorithm:
1. Input two numbers m and n.
2. Find residue by dividing “m” by “n” [m > n]
3. If the residue (in step 2) is zero, GCD= n
4. If the residue is not zero, redefine:m = n, n = residue
5. Repeat the steps 2 to 4.
6. Calculate LCM = m*n/GCD
m, n = n, r
r = m%n
return n print('GCD = ', gcd(n), 'LCM = ', nprod/gcd(n))
Note: For any two numbers �� and ��, we can always swap them to
have m > n.
>>> m, n = 4, 18
>>> if m < n: m, n = n, m >>> m, n
(18, 4)
# 13 Pythagorean Triplets
Algorithm:
1. Input: a positive integer��
2. Take an integer�� in the range [1, (�� − 1)]
3. Find GCD between �� and ��
4. Test if GCD = 1, calculate ��, ��, �� from the formulas.
Output:
Give a number
3
Pythagorean triplets 8 6 10
5 12 13
# 14 Number Conversion
Decimal Integer to Binary
Base 10 system ⟶ Base 2 2)156 0system. 2)78 0To convert, we divide the 2)39 1decimal number by 2
and 2)19 1continue downwards, divide 2)9 1each new quotient by 2 and
2)4 0
def bin_frac(n):
b = str()
i=1
while n > 0:
n = n*2
frac, intg = modf(n) b += str(int(intg)) n = frac
i=i+1
In this case we must take each digit in respective positions and sum the terms
with 2 to the power of that position. For example, �������� →
�� × ����+ �� × ����+ �� × ����+ �� × ����.
s, i = 0, 0
while n > 0:
res = n%10
n = n//10
print(‘Decimal Number:', s)
Input/Output:
'0b1100'
>>> bin(-123)
'-0b1111011'
>>> 0b1100 # Binary to Decimal 12
Note: For the above expressions, the prefix ‘b’ in the binary string stands for
byte string.
Exercises:
# 15 Fibonacci Numbers
Fibonacci series is a series of numbers where each number is the sum of two
previous numbers:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89….
The rule: ����+2= ����+ ����+1
Algorithm:
2])
Output:
# 16 Binary Search
Binary search algorithm is used to find any number in a sorted list. The given
list must be sorted first (in descending or ascending order). If the wanted
number is found in the sorted list, then its index is returned. This is to find the
position of the number in the list. This algorithm works on‘divide and
conquer’ principle!
Algorithm:
# Python Script:
# Binary Search by Algorithm L = [1, 0, 10, -9, 6, 100, -23, 7] SL =
sorted(L)
x = eval(input('Number to search:
\n')) start = 0
end = len(SL)-1
Output:
[-23, -9, 0, 1, 6, 7, 10, 100]
Number to search:
6
start = 4 end = 7 mid = 3
start = 4 end = 4 mid = 5
Position in sorted List: 4
Note: The above output shows the steps how the position of a given number
is searched through the bracketing of starting and end points. Note that the
position of an element (here the number 6) is given in the sorted list (and not
in the original list).
<module>
nlist.index(50)
ValueError: 50 is not in list
# 17 Matrix Operations
Two dimensional lists (lists inside a list) and 2D NumPy arrays can be treated
as Matrix. [NumPy arrays will be introduced in detail in Chapter 7 on
NumPy.]
Algorithm:
Output:
Matrix C [6, 8, 7] [7, 9, 8]
Note: Every 2D list can be arranged to look like a mathematical Matrix. For
example,
A = [[1, 2, 3],
[4, 5, 6]]
The list A has two elements where each element is a list itself. The size of list
A (number of elements) is equal to rows. The size of a row is equal to
columns.
The nested 2D lists A, B, C are treated as matrices and they have same shape.
# Number of elements = rows
>>> len(A)
# First element
>>> A[0] [1, 2, 3]
A = [[1, 2, 3], [4, 5, 6]] B = [[5, 6, 4], [3, 4, 2]] row, col = len(A), len(A[0])
# m = rows, n = columns m, n = 2, 3
print ('Matrix elements for
two matrices, the number of columns of the first matrix must be equal to the
number of rows of the second matrix.
Algorithm:
1. Input: Two matrices as nested lists
2. Nested loops with indices:i, j, k (say,)
3. Inside the innermost loop (k say,) the (��,
��) -th element of 1st matrix is multiplied with the (��, ��)-th element
of 2nd matrix, and then added.
<zip object at
0x0000028B1935AF40>
[New iterable object from two lists]
# Convert to list
>>> list(zip(x, y))
[(1, 4), (2, 5), (3, 6)]
Transpose of a Matrix
By transpose of a matrix A, the rows become the columns and the columns
become rows. If B is the transposed matrix: B =����, the elements of the
two matrices will be related as������= ������
Algorithm:
1. Input: Matrix
2. Find the number of rows and columns
3. Nested Loops for i and j: i for rows (outside), j for columns
4. Inside the inner loop:������= ������
# Transpose of a Matrix
for i in range(row):
for j in range(col): B[j][i] = A[i][j]
for r in B: print(r)
# Transpose by List comprehension
A = [[1, 2, 3], [4, 5, 6]] row = len(A)
col = len(A[0]) B = [[A[i][j] for i in range(row)] for j in range(col)]
print('Transposed Matrix') for r in B:
print(r)
Output:
Transposed Matrix
[1, 4]
[2, 5]
[3, 6]
Matrix operations can be greatly simplified and made faster with the help
ofarrays in NumPy. NumPy is an external package developed for Python
which consists of various useful modules, functions and other objects suited
to do efficient numerical computations. We shall introduce NumPy in detail
in the next chapter.
In this chapter, we briefly demonstrate how we can do matrix operations
witharray() function in NumPy package. Like nested lists, NumPy arrays can
be of any dimension. A brief demonstration follows:
[4, 5, 6]])
[Two-dimensional array]
# Shape of array
>>> x.shape
(3, ) # Tuple of single element
It should be noted that the product of two 2D arrays is not a matrix product.
Matrix product is a special kind of operation where we take row elements of
left matrix and multiply with corresponding column elements of the right
matrix. For matrix product, there is a NumPy method called dot().
(1 2 3 1 2
AB = 4 5 6) (3 4)
56
[49, 64]])
# Transpose by NumPy
[2, 5],
[3, 6]])
# Matrix object in NumPY
Exercises
Write Python scripts for the following problems.
Collatz sequence. another function which adds the absolute values of the two
complex numbers.
6. Take a binary number ‘100110’ as a string and convert this into decimal
integer.
7. A ‘Harshad number’ is an integer that is divisible by the sum of its digits.
Define a functionharshad() to determine whether the given number is
Harshad number or not.
8. Count how many odd integers are there between 253 and 837.
9. Given a collection numbers: 10, 1, -10, 10, 4, 5, 0, 1, 9, 2, 0, prepare a list
containing all the duplicate numbers.
10. Calculate the following sum: ∑∞ 1 for��=0���� �� = 2 with an
accuracy level of 4 decimal places.
11. Find �� from: ��= 1 −1+1−1+1+ ⋯ ∞4 3 5 7 9 up to 6th decimal place
accurate.
12. Compute the following logarithmic series up to an accuracy of 3 decimal
places:
series sum:
�� =∑
20 1
��=1����2
19. Legendre’s conjecture: It says that there is always one prime number
between the two consecutive natural numbers squared. For example, 2 and 3
are prime numbers between 12 and 22, 5 and 7 are between 22 and 32 and so
on. Write a code to test the conjecture between �� and �� + 1, with any
��.
21. Given two lists: a = [1, 2, 7, 9, 11, 0, 21] and b = [3, 7, 1, 0, 12], write a
Python code to find out if there is at least one element common between
them.
22. Write a Python function that merges two lists [‘a’, ‘b’, ‘c’, ‘d’] and [1, 2,
3, 4]to form a list that takes alternate elements, [‘a’, 1, ‘b’, 2, ‘c’, 3, ‘d’, 4].
The rules:
• Move one disk at a time.
• Move only the top most disk from
a stack.
On Matrix
29. Take the array: A = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]. Reshape the array
into a 3 by 4 matrix. Transpose the matrix.
1. Which of the following are all immutable? (a)List, Tuple (b)Tuple, String
(c) String, List (d) List, Dictionary
2. Given, X = [-1, 1, 2, 1, 3, 5, 0,
-1], what is X[-1] * X[1] = ? (a)0 (b) 1 (c) −1 (d) 2
4. What will be the output list if we write the following list comprehension?
print(n)
(a)1234 (b) 1, 2, 3, 4 (c) 1 2 3 4 (d) Error
A.append(range(i))
print(A)
What can be the output?
(a) [[0], [0,1], [1,2], [3,4]] (b)
A = [[0.5, 0.4, 0.2], [0.1, 0.2, 0.3], [0.5, 0.6, 0.3]] print(A[len(A)-1][1])
(a) 0.5 (b) 0.3 (c) 0.6 (d) 0.4
14. What will be the result of12 == 12.0? (a) True (b) False (c) 12 (d) 12.0
15. What will be the output of the following
Python Script?
x1, x2 = [0, 1]
for i in range(5):
x3 = x1 + x2
x1, x2 = x2, x3
print(x3)
(a) 5 (b) 3 (c) 13 (d) 8
16. What will be the output of the following? X = [i**2 for i in range(1,
4)]*2 print (X)
(a) [1, 4, 9] (b) [1, 4, 9, 16] (c) [2, 8, 18] (d) None
17. What is the type of the object is x?
print(i)
(a) 0 (b) no output (c) error (d)
None
19. Which of the following symbols are used for comments in Python?
(a)// (b) “ (c) /**/ (d) #
>= 2, L))) (a) {2} (b) {2, 2.0} (c) [2] (d) [2.0]
22. Output of the following two lines:
>>> f = lambda x: x*x
Appendix
Python Installation
On Windows:
Step #1: Go to Official Python site
(www.python.org) and click ‘Downloads’.
Step #2: Click the ‘view the full list of downloads’ (below the latest version
showing).
Step #3: Click ‘Download’ on the same row (3rd column) of any stable
release (3rd column). Python 3.8.3, for example.
Step #6: Now click the downloaded file to install Python 3.8.3. It will create
a Python directory automatically.
Step #7:Type ‘python’ on the search bar (at the bottom left of the screen) to
see that an IDLE (Python 3.8 64-bit) logo is created. You can create a
shortcut to the Windows taskbar or bring to Desktop by clicking ‘open file
location’. The IDLE logo now appears on the Desktop or taskbar.
Step #8: Click the IDLE logo. The IDLE interpreter Window (Python 3.8.3
Shell) will appear. Begin to type.
# External Python packages
Step #1: Click ‘file’ on the top left corner of the IDLE window, click ‘open’
to see the pathname to the Python directory that is created (location of the
files). In this case, it is: C:\Users\Abhijit Kar
Gupta\AppData\Local\Programs\Python\Python3 8. [You may copy this or
note down.]
Step #2: On the Window search (at the bottom left of screen) type ‘cmd’. A
black DOS window appears. Now copy paste pathname or type ‘cd
AppData\Local\Programs\Python\Python38’ on the command line and press
Enter [cd = change directory]. Now it shows the command line with full
pathname:
Likewise, you can install any external package with ‘pip install’ command.
You can install many packages together by typing pip install numpy scipy
matplotlib.
On Linux:
On most of the Linux distributions, Python 2 is preinstalled. Python 3.8 does
not come by default on Ubuntu or other Linux platforms. To install, open
terminal application and type the following:
On Android phone:
There are several Python App’s available. Any App of your choice can be
installed on mobile phone from Google play store. Some of the versions are
lighter (takes less space on your phone and so quick). You may do most of
the useful python calculations and testing over any kind of android phone.
External packages can also be done from the App’s repository. Some useful
App’s a re QPython, Pydroid etc.
Python Distributions:
There are various Python distributions that come with core Python along with
some common external packages. For example, Python(x, y), Anaconda etc.
Google search and install any of them as you wish. Each of them contains
various Python development environments: IPython Shells, Spyder, Jupyter
Notebook etc.
External Packages:
Consult the following blog article for installation on Windows, Mac, and
Linux:
realpython.com/installing-python
Python Versions
Python has two parallel versions: Python 3.x and Python 2.x. This book is
written with Python 3.x syntaxes.
In Python 3.x, the strings are in unicode whereas in Python 2.x, the strings
are stored as ASCII. At the user level, there are only a few differences
between the two versions. The installation of either of the versions is not
difficult. Also, two parallel versions can coexist on a same machine. One can
work in any version and can switch over to another version with ease where
only a few changes in syntaxes are to be made.
print(x, y) print x, y
input x = input() x = input()
Most of the releases in Python 2.x are stable. The latest releases/ versions in
Python 3.x are in developing stages. In short, Python 2.x is a legacy and
Python 3.x is the future.
input() in Python 2.x and Python 3.x:
# Python 2.x
# Python 3.x
To get the numerical value of the input variable, we need to evaluate the
input string through eval() function. Also, for multiple inputs, we need to
split the string through split() or we applyeval() and then unpack.
Bibliography
Index
2D array, 345
3D Bar Plot, 524
3D Plot, 520, 558
bin, 37
Binary, 38
Binary Search, 634 Boolean, 7
Broadcasting, 374 Bubble Sort, 599 builtins, 31
Calculator, 21
Calculus, 546
Calendar, 84
Cardoid, 472
Child Class, 249
Class, 233
Coiling Squares, 276 Collatz Sequence, 621 Colors, 455
Commenting, 52
Comparison of
Numbers, 25
complex, 31
Complex, 7
Complex Math
Module, 67
Complex Matrices, 422 Complex Matrix, 380 Concatenate, 351
Concatenation, 191 Contour Plot, 506
Conversion, 192
Correlation
Coefficient, 609
Cosine Series, 614
Cross Product, 390
Comprehension, 213
empty, 382
Enumerate, 177 Evaluate, 43
Exponential Series,
612
eye, 383
F
factorial, 61
Fibonacci Numbers,
633
Fibonacci Spiral, 286 fill, 382
filter Function, 140 Flatten, 326
Flip, 377
Float, 7
Floor Division, 9 For-Loop, 92
Formatted, 45
Fractal Tree, 293 Fractals, 289
Frozen Set, 202
Function Overload,
136
G
GCD, 623
Geometrical, 560 Global, 131
Greek Letters, 478
Identities, 26
identity, 383
Identity, 28
immutable, 189
Imported Functions,
138
Indexing, 147, 173,
190, 332
Infinite Series, 612 Inheritance, 249
Inner product, 390 Input and Output, 39 Input from Outside, 42 insert, 162
Insertion Sort, 603 Integer, 7
Integral, 414
Integration, 549
Interpreter, 47
intersection, 201
intertools, 486
Iterator, 330
K
keys, 207
Koch Curve, 290
L
167
List Methods, 152 loadtxt, 429
Local, 131
logspace, 322
Long, 7
lookfor, 303
Loops, 92
Max, 595
Mean, 608
meshgrid, 425
Methods, 192, 196,
233
Methods on String,
180
mgrid, 427
Min, 595
Module, 54
Modulo, 9, 23
monochrome, 455 Multiple Figures, 488 mutable, 204
M
map Function, 139
Marker, 455
Marker Styles, 456
Math module, 59
Math Symbols, 480 Mathematical
Operators, 8
Matplotlib, 446
Matrix, 379, 541
Matrix Operations, 638
Namespace, 16
Nested Dictionary, 215 Nested List, 170
normalvariate, 77
Number Conversion,
627
Numbers, 6, 24
NumPy Arrays, 302
Package, 259
Packing, 217
Pandas, 569
Parent Class, 249 Perfect Square, 103 Pie Chart, 504
pivot_table, 517
Plotting in 2D, 447 Polar Plot, 471
Polygon, 272
Polynomial, 410
pop, 160
Prime Numbers, 589 Product, 362
Pythagorean Triplets,
625
Q
Quadratic Equation, 607
R
randint, 437
Random Choice, 78,
437
Random Sampling, 81 Random Shuffling, 82 Random Walk, 79, 281
randrange, 77
range, 94, 165
Ravel, 326
repeat, 389
Reshape, 324
Resize, 324
Roots, 412
Rotate, 377
round, 33
333
sorted, 151
Sorting, 368
Special Arrays, 382 split, 184, 346
Split Arrays, 346
Stack plot, 500
Stacking Arrays, 352 Statistical Plots, 494 Statistics, 355
Std Deviation, 608 String, 172
Strong Numbers, 594 Subplot, 490
Subset, 202
Swap, 337
SymPy, 531
T
Tensor, 395
V
values, 207
Variables, 5
Variance, 608
Vectorize, 408
version, 56
Vertical line, 463 Void Function, 122 vsplit, 348 vstack, 354
W
While Loop, 101
X
X-Y Plots, 447
Z
zeros, 384
zip Function, 141