Python Virtual Training For Arcesium - Module II - Day 1¶

Feb 13-17, 2023 Vikrant Patil

All notes are available online at https://notes.pipal.in/2023/arcesium_finop_jan/

Please login to https://engage.pipal.in/ and launch jupyter lab

For today create a notebook with name module2-day1

notebook names are case sensitive. Make sure you give correct name

© Pipal Academy LLP

Topics for today

  • Iteration Patterns
  • List Comprehesions
  • Filtering list with some condition
  • Dictionary Comprehensions

Iteration Patterns¶

  • Iteration is something that you repeat!
  • for
  • while
In [1]:
nums = [2, 3, 5, 7, 11, 13, 17]
In [2]:
for n in nums:
    print(n*n, end=",")
4,9,25,49,121,169,289,
In [4]:
for n in nums[::-1]: # it created a new list
    print(n*n, end=",")
289,169,121,49,25,9,4,

reversed¶

In [5]:
for n in reversed(nums): # this creates an iterator of nums which starts from end and goes till the start of list
    print(n, end=",")
17,13,11,7,5,3,2,

Some things that you should remember about these interators

  • it is for one time use
  • it does not create new list (in the backend it makes use of same list data)
In [6]:
rnums = reversed(nums)
In [7]:
for n in rnums:
    print(n, end=",")
17,13,11,7,5,3,2,
In [8]:
for n in rnums: # this is empty because above loop consumed the iterator
    print(n, end=",")

1 2 3 4 5


reversed
1 2 3 4 5
        ^

inside for loop
it give where the poiter is
5

next itereration
1 2 3 4 5
      ^
it give where the poiter is
4
next iteration

1 2 3 4 5
    ^
it give where the poiter is
3

next iteration

1 2 3 4 5
  ^
it give where the poiter is
2

next iteration

1 2 3 4 5
^
it give where the poiter is
1

next iteration

 1 2 3 4 5
^
it give where the poiter is
pointing to start of list
In [9]:
a = 5
In [10]:
a = [1, 2, 3]
In [11]:
a.append(5) 
In [12]:
a
Out[12]:
[1, 2, 3, 5]
In [13]:
a = 6
In [15]:
a = "hello"
In [16]:
a = "different"
In [17]:
a = "hello"
b = a
a = "different"
In [18]:
a
Out[18]:
'different'
In [19]:
b
Out[19]:
'hello'
In [20]:
"hello".append("d")
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[20], line 1
----> 1 "hello".append("d")

AttributeError: 'str' object has no attribute 'append'
In [21]:
nums
Out[21]:
[2, 3, 5, 7, 11, 13, 17]
In [22]:
nums.append(19)
In [23]:
nums
Out[23]:
[2, 3, 5, 7, 11, 13, 17, 19]
In [24]:
nums
Out[24]:
[2, 3, 5, 7, 11, 13, 17, 19]
In [25]:
nums
Out[25]:
[2, 3, 5, 7, 11, 13, 17, 19]
In [26]:
numsitr = iter(nums)
In [27]:
next(numsitr)
Out[27]:
2
In [28]:
next(numsitr)
Out[28]:
3
In [29]:
next(numsitr)
Out[29]:
5
In [30]:
next(numsitr)
Out[30]:
7
In [31]:
next(numsitr)
Out[31]:
11
In [32]:
next(numsitr)
Out[32]:
13
In [33]:
next(numsitr)
Out[33]:
17
In [34]:
next(numsitr)
Out[34]:
19
In [35]:
next(numsitr)
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
Cell In[35], line 1
----> 1 next(numsitr)

StopIteration: 
In [36]:
rnums # this is already consumed
Out[36]:
<list_reverseiterator at 0x7f6a5807cb80>
In [37]:
next(rnums)
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
Cell In[37], line 1
----> 1 next(rnums)

StopIteration: 
In [38]:
for i in rnums:
    print(i)
In [39]:
for i in reversed(nums):
    print(i, end=",")
19,17,13,11,7,5,3,2,
In [40]:
for i in reversed(nums):
    print(i, end=",")
19,17,13,11,7,5,3,2,
In [42]:
for i in reversed(nums): # every time with new for loop we are creating new reversed iteration
    print(i, end=",")
19,17,13,11,7,5,3,2,
In [43]:
rnums = reversed(nums) # this is not recommended way of using rversed iterator
for i in rnums:
    print(i, end=",")
19,17,13,11,7,5,3,2,
In [44]:
for i in reversed(nums): # this is the convention
    print(i, end=",")
    
19,17,13,11,7,5,3,2,

enumerate¶

In [46]:
poem = """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."""
In [47]:
lines = poem.split("\n") # this a list
linecount = len(lines)
for i in range(linecount):
    print(i, lines[i]) # indexing!
0 Beautiful is better than ugly.
1 Explicit is better than implicit.
2 Simple is better than complex.
3 Complex is better than complicated.
4 Flat is better than nested.
5 Sparse is better than dense.
6 Readability counts.
7 Special cases aren't special enough to break the rules.
8 Although practicality beats purity.
9 Errors should never pass silently.
In [48]:
for i, line in enumerate(lines): #index and item is given in each iteration
    print(i, line)
0 Beautiful is better than ugly.
1 Explicit is better than implicit.
2 Simple is better than complex.
3 Complex is better than complicated.
4 Flat is better than nested.
5 Sparse is better than dense.
6 Readability counts.
7 Special cases aren't special enough to break the rules.
8 Although practicality beats purity.
9 Errors should never pass silently.
In [49]:
lines = poem.split("\n")
linecount = len(lines)
for i in range(linecount):
    print(i+1, lines[i]) # indexing!
1 Beautiful is better than ugly.
2 Explicit is better than implicit.
3 Simple is better than complex.
4 Complex is better than complicated.
5 Flat is better than nested.
6 Sparse is better than dense.
7 Readability counts.
8 Special cases aren't special enough to break the rules.
9 Although practicality beats purity.
10 Errors should never pass silently.
In [50]:
for i, line in enumerate(lines, start=1): #index and item is given in each iteration
    print(i, line)
1 Beautiful is better than ugly.
2 Explicit is better than implicit.
3 Simple is better than complex.
4 Complex is better than complicated.
5 Flat is better than nested.
6 Sparse is better than dense.
7 Readability counts.
8 Special cases aren't special enough to break the rules.
9 Although practicality beats purity.
10 Errors should never pass silently.
In [52]:
for line in lines:
    print(line)
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.
In [54]:
stocks = {"IBM":125, "AGILLENT":150,"AT&T":160}

for key, value in stocks.items(): # items is method in dictionary thats why stocks.items
    print(key, value)
IBM 125
AGILLENT 150
AT&T 160
In [55]:
for i, n in enumerate(nums):
    print(i, n)
0 2
1 3
2 5
3 7
4 11
5 13
6 17
7 19
In [56]:
words = "Lets make some words".split()
for i, word in enumerate(words, start=1):
    print(i, word)
1 Lets
2 make
3 some
4 words
In [57]:
 prices = [('IBM', 'Monday', 111.71436961893693),
              ('IBM', 'Tuesday', 141.21220022208635),
              ('IBM', 'Wednesday', 112.40571010053796),
              ('IBM', 'Thursday', 137.54133351926248),
              ('IBM', 'Friday', 140.25154281801224),
              ('MICROSOFT', 'Monday', 235.0403622499107),
              ('MICROSOFT', 'Tuesday', 225.0206535036475),
              ('MICROSOFT', 'Wednesday', 216.10342426936444),
              ('MICROSOFT', 'Thursday', 200.38038844494193),
              ('MICROSOFT', 'Friday', 235.80850482793264),
              ('APPLE', 'Monday', 321.49182055844256),
              ('APPLE', 'Tuesday', 340.63612771662815),
              ('APPLE', 'Wednesday', 303.9065277507285),
              ('APPLE', 'Thursday', 338.1350605764038),
              ('APPLE', 'Friday', 318.3912296144338)]
In [58]:
for ticker, day, value in prices:
    print(ticker, day, value)
    
IBM Monday 111.71436961893693
IBM Tuesday 141.21220022208635
IBM Wednesday 112.40571010053796
IBM Thursday 137.54133351926248
IBM Friday 140.25154281801224
MICROSOFT Monday 235.0403622499107
MICROSOFT Tuesday 225.0206535036475
MICROSOFT Wednesday 216.10342426936444
MICROSOFT Thursday 200.38038844494193
MICROSOFT Friday 235.80850482793264
APPLE Monday 321.49182055844256
APPLE Tuesday 340.63612771662815
APPLE Wednesday 303.9065277507285
APPLE Thursday 338.1350605764038
APPLE Friday 318.3912296144338
In [59]:
for i, c in enumerate("This is some text"):
    print(i, c)
0 T
1 h
2 i
3 s
4  
5 i
6 s
7  
8 s
9 o
10 m
11 e
12  
13 t
14 e
15 x
16 t
In [60]:
for i, c in enumerate(("a","b", "c")):
    print(i, c)
0 a
1 b
2 c
In [62]:
for i, key in enumerate(stocks): # dictionary... it gives key in for loop
    print(i, key)
0 IBM
1 AGILLENT
2 AT&T
In [64]:
for key in stocks: # dictionary... it gives key in for loop
    print(key)
IBM
AGILLENT
AT&T
In [65]:
for item in prices:
    print(item)
('IBM', 'Monday', 111.71436961893693)
('IBM', 'Tuesday', 141.21220022208635)
('IBM', 'Wednesday', 112.40571010053796)
('IBM', 'Thursday', 137.54133351926248)
('IBM', 'Friday', 140.25154281801224)
('MICROSOFT', 'Monday', 235.0403622499107)
('MICROSOFT', 'Tuesday', 225.0206535036475)
('MICROSOFT', 'Wednesday', 216.10342426936444)
('MICROSOFT', 'Thursday', 200.38038844494193)
('MICROSOFT', 'Friday', 235.80850482793264)
('APPLE', 'Monday', 321.49182055844256)
('APPLE', 'Tuesday', 340.63612771662815)
('APPLE', 'Wednesday', 303.9065277507285)
('APPLE', 'Thursday', 338.1350605764038)
('APPLE', 'Friday', 318.3912296144338)
In [66]:
for t, d, v in prices:
    print(t, d, v)
IBM Monday 111.71436961893693
IBM Tuesday 141.21220022208635
IBM Wednesday 112.40571010053796
IBM Thursday 137.54133351926248
IBM Friday 140.25154281801224
MICROSOFT Monday 235.0403622499107
MICROSOFT Tuesday 225.0206535036475
MICROSOFT Wednesday 216.10342426936444
MICROSOFT Thursday 200.38038844494193
MICROSOFT Friday 235.80850482793264
APPLE Monday 321.49182055844256
APPLE Tuesday 340.63612771662815
APPLE Wednesday 303.9065277507285
APPLE Thursday 338.1350605764038
APPLE Friday 318.3912296144338

Q: can we start at custom point and end in range

In [67]:
words
Out[67]:
['Lets', 'make', 'some', 'words']
In [68]:
for i, w in enumerate(words[:2]):
    print(i, w)
0 Lets
1 make
In [69]:
for i, w in enumerate(words[:2], start=5):
    print(i, w)
5 Lets
6 make

zip¶

In [73]:
firstname = ["Alice", "Elsa", "Alex", "Elisa"]
lastname = ["Wondergirl", "Frozen", "Lion", "Hacker"]
In [74]:
l = len(firstname) # what is lastname has less entries!
for i in range(l):
    print(firstname[i], lastname[i])
Alice Wondergirl
Elsa Frozen
Alex Lion
Elisa Hacker
In [75]:
for name, surname in zip(firstname, lastname):
    print(name, surname)
Alice Wondergirl
Elsa Frozen
Alex Lion
Elisa Hacker
In [76]:
xs = ["x1", "x2", "x3"]
ys = ["y1", "y2", "y3"]
zs = ["z1", "z2", "z3"]
In [77]:
for x, y, z in zip(xs, ys, zs):
    print(x, y, z)
x1 y1 z1
x2 y2 z2
x3 y3 z3
In [78]:
v1 = [1, 2, 3, 4]
v2 = [1, 1, 1, 1]
for a,b in zip(v1, v2):
    print(a+b)
2
3
4
5
In [79]:
def vector_add(v1, v2):
    s = []
    for a,b in zip(v1, v2):
        s.append(a+b)
        
    return s
In [80]:
vector_add([1, 2, 3], [1,1,1])
Out[80]:
[2, 3, 4]
In [81]:
tickers = ["AB", "SDSD", "HGHG", "SSD"]
values =  [102, 100, 200, 130]
In [83]:
stocks = dict(zip(tickers, values))
In [86]:
stocks
Out[86]:
{'AB': 102, 'SDSD': 100, 'HGHG': 200, 'SSD': 130}
In [85]:
stocks
Out[85]:
{'AB': 102, 'SDSD': 100, 'HGHG': 200, 'SSD': 130}
In [87]:
stocks
Out[87]:
{'AB': 102, 'SDSD': 100, 'HGHG': 200, 'SSD': 130}
In [88]:
for k,v in zip(tickers , values):
    print(k, v)
AB 102
SDSD 100
HGHG 200
SSD 130
In [90]:
zipiter = zip(tickers , values)
for k,v in zipiter:
    print(k, v)
AB 102
SDSD 100
HGHG 200
SSD 130
In [91]:
for k,v in zipiter:
    print(k, v)
In [92]:
rnums
Out[92]:
<list_reverseiterator at 0x7f6a4180e7d0>
In [93]:
rnums = reversed(nums) # it is fresh!
In [94]:
r = list(rnums) # new list is created
In [95]:
r
Out[95]:
[19, 17, 13, 11, 7, 5, 3, 2]
In [98]:
r2 = list(rnums) # becuase this already consumed
In [97]:
r2
Out[97]:
[]

problem:

  • Write a function char_linenumbers which takes list of items and prints line number as char (a, b, c, d) and item
>>> char_linenumbers(lines)
a Beautiful is better than ugly.
b Explicit is better than implicit.
c Simple is better than complex.
d Complex is better than complicated.
e Flat is better than nested.
f Sparse is better than dense.
g Readability counts.
h Special cases aren't special enough to break the rules.
i Although practicality beats purity.
j Errors should never pass silently.
In [99]:
[a,b,c,d,e]
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[99], line 1
----> 1 [a,b,c,d,e]

NameError: name 'e' is not defined
In [100]:
["a","b","c","d","e"]
Out[100]:
['a', 'b', 'c', 'd', 'e']
In [103]:
def char_linenumbers(items):
    chars = "abcdefghijklmnopqrstuvwxyz"
    for c, item in zip(chars, items):
        print(c, item)
In [104]:
char_linenumbers(lines)
a Beautiful is better than ugly.
b Explicit is better than implicit.
c Simple is better than complex.
d Complex is better than complicated.
e Flat is better than nested.
f Sparse is better than dense.
g Readability counts.
h Special cases aren't special enough to break the rules.
i Although practicality beats purity.
j Errors should never pass silently.
In [105]:
def char_linenumbers(items, start="a"):
    chars = "abcdefghijklmnopqrstuvwxyz"
    s = chars.index(start)
    for c, item in zip(chars[s:], items):
        print(c, item)
In [106]:
char_linenumbers(lines, start="c")
c Beautiful is better than ugly.
d Explicit is better than implicit.
e Simple is better than complex.
f Complex is better than complicated.
g Flat is better than nested.
h Sparse is better than dense.
i Readability counts.
j Special cases aren't special enough to break the rules.
k Although practicality beats purity.
l Errors should never pass silently.
In [107]:
import itertools
In [109]:
chr(67) # it converts askii value to actual string
Out[109]:
'C'

List comprehensions¶

We can see a pattern in these loops

In [114]:
def findlens(words):
    lens = []
    for w in words:
        lens.append(len(w))
        
    return lens


def squares(nums):
    sqrs = []
    for n in nums:
        sqrs.append(n*n)
        
    return sqrs

import datetime

def trange(n):
    """
    generate next n dates starting from today
    """
    dates = []
    start = datetime.datetime.today()
    for i in range(n):
        dates.append(start + datetime.timedelta(days=i))
        
    return dates

"""
words -> lens
nums -> sqrs
range(n) -> list of dates
"""


def findlens(words):
    return [len(w) for w in words]  # for loop is reduced one line!


def squares(nums):
    return [n*n for n in nums]

def trange(n):
    start = datetime.datetime.today()
    return [start+datetime.timedelta(days=i) for i in range(n)]
existing_items
newlist = []
for item in existing_items:
   newlist.append(do_something(item))

this translates to


newlist = [do_something(item) for item in existing_items]
In [115]:
def even(a):
    return a%2==0

def evens(nums):
    e = []
    
    for n in nums:
        if even(n):
            e.append(n)
            
    return e


def find_words_of_len(words, n):
    words_of_len = []
    
    for word in words:
        if len(word) == n:
            words_of_len.append(word)
            
    return words_of_len
In [116]:
def evens(nums):
    return [e for e in nums if even(e)]

def find_words_of_len(words, n):
    return [w for w in words if len(w) == n]
In [117]:
evens(range(20))
Out[117]:
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
In [119]:
words = "one two three four five six seven".split()
find_words_of_len(words, 3)
Out[119]:
['one', 'two', 'six']

problem

  • How will you put a list comprehension on nums if we have find square of even numbers?
In [120]:
prices
Out[120]:
[('IBM', 'Monday', 111.71436961893693),
 ('IBM', 'Tuesday', 141.21220022208635),
 ('IBM', 'Wednesday', 112.40571010053796),
 ('IBM', 'Thursday', 137.54133351926248),
 ('IBM', 'Friday', 140.25154281801224),
 ('MICROSOFT', 'Monday', 235.0403622499107),
 ('MICROSOFT', 'Tuesday', 225.0206535036475),
 ('MICROSOFT', 'Wednesday', 216.10342426936444),
 ('MICROSOFT', 'Thursday', 200.38038844494193),
 ('MICROSOFT', 'Friday', 235.80850482793264),
 ('APPLE', 'Monday', 321.49182055844256),
 ('APPLE', 'Tuesday', 340.63612771662815),
 ('APPLE', 'Wednesday', 303.9065277507285),
 ('APPLE', 'Thursday', 338.1350605764038),
 ('APPLE', 'Friday', 318.3912296144338)]
In [121]:
[record for record in prices if record[0]=="IBM"]
Out[121]:
[('IBM', 'Monday', 111.71436961893693),
 ('IBM', 'Tuesday', 141.21220022208635),
 ('IBM', 'Wednesday', 112.40571010053796),
 ('IBM', 'Thursday', 137.54133351926248),
 ('IBM', 'Friday', 140.25154281801224)]
In [124]:
def mean(nums):
    return sum(nums)/len(nums)


def values(data, symbol):
    return [record[2] for record in data if record[0]==symbol]


def values(data, symbol):
    return [value for symbol_, day, value in data if symbol_==symbol]

def weeklyaverage(data, symbol):
    return mean(values(data, symbol))
In [125]:
weeklyaverage(prices, "IBM")
Out[125]:
128.62503125576717
In [126]:
weeklyaverage(prices, "APPLE")
Out[126]:
324.51215324332736
empty = []
for item in items:
    # do something
    # do more stuff
    # finaly derive with one value
    empty.append(v)

def do_stuff(item):
    # do something
    # do more stuff
    # finaly derive with one value
    return v

[do_stuff(item) for item in items]
In [ ]:
def weeklyaverage(data, symbol):
    s = 0
    count = 0
    for record in data:
        if record[0] == symbol:
            s += record[2]
            count += 1
            
    return s/count
    

problem

  • find sum of multiples of 7 or 11 below 1000
  • Write a function which finds all factors of given number(include 1 and self)
  • Write a function is_prime which checks if given number is prime or not
  • Write a list comprehension to generate prime numbers

bonus

  • There is a string "abrakadabra", we want to capitalize alternate character from it. how can we do it? can a list comprehension be used to do this?
  • Write a function to extract a column from 2D list
>>> data = [[11, 12, 13],
            [21, 22, 23],
            [31, 32, 33]]
>>> column(data, 0)
[11, 21, 31]
In [ ]:
list_