Basic data structures#
Lists#
Lists are heterogeneous and dynamic arrays which allows to group data which are not necessarily of the same type. Lists are very easy to use but, be careful, they are not the fastest struct you can use for numerical computations.
A list is declared using the [ ]
characters, and its elements are separated by ,
.
NOTE: Lists are NOT recommended for numerical work, it is better to use numpy arrays.
See: https://quickref.me/python#python-lists
xdata = [] # declares empty list
print (xdata)
xdata = [1,2, 3, 6.5, 'hello'] # heterogeneous list
print (xdata)
[]
[1, 2, 3, 6.5, 'hello']
# Access by indices
print (xdata[0])
print (xdata[3])
print (xdata[4])
print (xdata[4][3]) # Does this make sense?
1
6.5
hello
l
# Slicing operations [start=0, end, increment]
print (xdata[0:2])
print (xdata[0:3])
print (xdata[0:4:1])
print (xdata[:4:2])
print (xdata[::1])
print (xdata[::-1]) # inverse order
print (xdata[:-1])
[1, 2]
[1, 2, 3]
[1, 2, 3, 6.5]
[1, 3]
[1, 2, 3, 6.5, 'hello']
['hello', 6.5, 3, 2, 1]
[1, 2, 3, 6.5]
# From other lists
a = [2,3,4]
b = [3,4,5]
c = a + b
print (c) # Concatenate the lists
print (2*c) # duplicate the list
[2, 3, 4, 3, 4, 5]
[2, 3, 4, 3, 4, 5, 2, 3, 4, 3, 4, 5]
# Check memory address
a.append(55)
print(f"{id(a[0])}, {id(a[1])}, {id(a[2])}, {id(a[3])}")
139754843908104, 139754843908136, 139754843908168, 139754843909800
import numpy as np
a = np.array([2,3,4])
b = np.array([3,4,5])
c = a + b
print (c)
print(2*c)
[5 7 9]
[10 14 18]
# List comprehension
squares = [x**2 for x in range(0, 10)]
print(squares)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Exercises#
def printreversed(l):
### BEGIN SOLUTION
print(l[::-2])
### END SOLUTION
printreversed([1, 2, 3])
printreversed([1, 2, 3, 4 , "hola", "mundo"])
[3, 1]
['mundo', 4, 2]
import numpy as np
def primefactors(n):
### BEGIN SOLUTION
if True == isprime(n):
return [n]
primedivisors = []
# for ii in range(2, n):
# if n%ii==0 and isprime(ii):
# primedivisors.append(ii)
limit = n
ii = 2
while ii <= limit:
if limit%ii==0 and isprime(ii):
primedivisors.append(ii)
limit /= ii
continue
ii = ii + 1
return primedivisors
### END SOLUTION
# def primefactors(n):
# """
# From chatgpt
# """
# factors = []
# divisor = 2
# while divisor <= n:
# if n % divisor == 0:
# factors.append(divisor)
# n /= divisor
# else:
# divisor += 1
# return factors
def isprime(n):
### BEGIN SOLUTION
if n <=1: return False
for ii in range(2, int(np.sqrt(n))+1):
if n%ii == 0:
return False
return True
### END SOLUTION
print(primefactors(2))
print(primefactors(8))
print(primefactors(10))
print(primefactors(201))
print(primefactors(97))
print(primefactors(1213427))
print(primefactors(1213428))
print(primefactors(1213428987543096))
[2]
[2, 2, 2]
[2, 5]
[3, 67]
[97]
[1213427]
[2, 2, 3, 101119]
---------------------------------------------------------------------------
KeyboardInterrupt Traceback (most recent call last)
Cell In[11], line 8
6 print(primefactors(1213427))
7 print(primefactors(1213428))
----> 8 print(primefactors(1213428987543096))
Cell In[10], line 13, in primefactors(n)
11 limit = n
12 ii = 2
---> 13 while ii <= limit:
14 if limit%ii==0 and isprime(ii):
15 primedivisors.append(ii)
KeyboardInterrupt:
import numpy as np
def stats(data):
### BEGIN SOLUTION
xdata = np.array(data)
return xdata.mean(), xdata.min(), xdata.max() # returns a tuple → inmutable list
### END SOLUTION
stats([1, 2, 3.5])
(2.1666666666666665, 1.0, 3.5)
Tuples#
A tuple is like a list, but is inmutable, it cannot change. It is declared by using ()
.
a = (1, 2)
print (a)
print (a[0])
# a[1] = 4 # error, tuple is inmutable
b = () # empty tuple
print (b)
(1, 2)
1
()
You can use tuples to unpack data from functions returning several results
def func(x, y) :
return x + y, x-y # returns a tuple
a, b = func(1, 2)
print (a, b)
3 -1
Classes#
Python is an object oriented language. Everything is an object. You can also create new types by using classes, after defining their attributes and methods. When creating classes, you should is the self
keyword, which is the analogous to the pointer this
in c++. Let’s create a class for a point.
class Point2D :
"""This is a doctring. This allows to embed documentation inside the class definition.
You can split it
across several lines.
"""
def __init__(self, x = 0, y = 0):
""" This is the constructor"""
self.x_ = x # attribute x_
self.y_ = y # attribute y_
def coordinates(self):
return self.x_, self.y_
def __str__(self):
"""Cast method to convert to string"""
return (f"Coordinates : ( {self.x_:25.16e}, {self.y_:25.16e} )")
p1 = Point2D() # constructs a point with default internal attributes
print (p1) # Uses the str cast method
p2 = Point2D(2, -3)
print (p2)
Coordinates : ( 0.0000000000000000e+00, 0.0000000000000000e+00 )
Coordinates : ( 2.0000000000000000e+00, -3.0000000000000000e+00 )
%%html
<iframe width="800" height="500" frameborder="0" src="https://pythontutor.com/iframe-embed.html#code=class%20Point2D%20%3A%0A%20%20%20%20%22%22%22This%20is%20a%20doctring.%20This%20allows%20to%20embed%20documentation%20inside%20the%20class%20definition.%0A%20%20%20%20You%20can%20split%20it%20%0A%20%20%20%20across%20several%20lines.%0A%20%20%20%20%22%22%22%0A%20%20%20%20def%20__init__%28self,%20x%20%3D%200,%20y%20%3D%200%29%3A%20%0A%20%20%20%20%20%20%20%20%22%22%22%20This%20is%20the%20constructor%22%22%22%0A%20%20%20%20%20%20%20%20self.x_%20%3D%20x%20%23%20attribute%20x_%0A%20%20%20%20%20%20%20%20self.y_%20%3D%20y%20%23%20attribute%20y_%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20def%20coordinates%28self%29%3A%0A%20%20%20%20%20%20%20%20return%20self.x_,%20self.y_%0A%20%20%20%20%0A%20%20%20%20def%20__str__%28self%29%3A%0A%20%20%20%20%20%20%20%20%22%22%22Cast%20method%20to%20convert%20to%20string%22%22%22%0A%20%20%20%20%20%20%20%20return%20%28f%22Coordinates%20%3A%20%28%20%7Bself.x_%3A25.16e%7D,%20%7Bself.y_%3A25.16e%7D%20%29%22%29%0A%20%20%20%20%20%20%20%20%0A%0Ap1%20%3D%20Point2D%28%29%20%23%20constructs%20a%20point%20with%20default%20internal%20attributes%0Aprint%20%28p1%29%20%23%20Uses%20the%20str%20cast%20method%0A%0Ap2%20%3D%20Point2D%282,%20-3%29%0Aprint%20%28p2%29&codeDivHeight=400&codeDivWidth=350&cumulative=false&curInstr=0&heapPrimitives=nevernest&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe>
You can save the class to a file, and then later import it for re-use (you can import any python code)
%%file Point3D.py
class Point3D :
"""This is a doctring. This allows to embed documentation inside the class definition.
You can split it
across several lines.
"""
def __init__(self, x = 0, y = 0, z = 0):
""" This is the constructor"""
self.x_ = x # attribute x_
self.y_ = y # attribute y_
self.z_ = z # attribute z_
def coordinates(self):
return self.x_, self.y_, self.z_
def __str__(self):
"""Cast method to convert to string"""
return (f"Coordinates : ( {self.x_:25.16e}, {self.y_:25.16e}, , {self.z_:25.16e} )")
Overwriting Point3D.py
import Point3D as P3D
p3 = P3D.Point3D(2, 5, 0.9)
print (p3)
Coordinates : ( 2.0000000000000000e+00, 5.0000000000000000e+00, , 9.0000000000000002e-01 )
Dictionaries#
Dictionaries are the analogous of associative memories or associative arrays. Basically, they are a generalized container where the key is not necessarily an integer but an arbitrary object of inmutable type, called a key (for example, tuples, a reange of number, etc, nut not a list of integers, since the last is mutable).
Dictionaries can be seen as unordered sets of the pairs key:value, and are sourrounded by curly braces {}
. It is posible to delete/acces/add/etc values by using the corresponding key or key/value pair. The keys()
method for a dictionary returns the keys of that dictionary. To check for a given key, you can use the keyword in
.
Refs:
# creates a dictionary with several key:value pairs. Keys are strings
tel = {'jack': 4098, 'sape': 4139}
print (tel)
# acces by key. If key does not exists, creates a new entry
tel['guido'] = 4127
print (tel)
# Access by key
print (tel['jack'])
# Delete by key
del tel['sape']
print (tel)
# key a list of the keys
print(tel.keys())
# list of values
print(tel.values())
# keys and values
print(tel.items())
{'jack': 4098, 'sape': 4139}
{'jack': 4098, 'sape': 4139, 'guido': 4127}
4098
{'jack': 4098, 'guido': 4127}
dict_keys(['jack', 'guido'])
dict_values([4098, 4127])
dict_items([('jack', 4098), ('guido', 4127)])
%%html
<iframe width="800" height="500" frameborder="0" src="https://pythontutor.com/iframe-embed.html#code=%23%20creates%20a%20dictionary%20with%20several%20key%3Avalue%20pairs.%20Keys%20are%20strings%0Atel%20%3D%20%7B'jack'%3A%204098,%20'sape'%3A%204139%7D%20%20%0Aprint%20%28tel%29%0A%23%20acces%20%20by%20key.%20If%20key%20does%20not%20exists,%20creates%20a%20new%20entry%0Atel%5B'guido'%5D%20%3D%204127%20%0Aprint%20%28tel%29%0A%23%20Access%20by%20key%0Aprint%20%28tel%5B'jack'%5D%29%0A%23%20Delete%20by%20key%0Adel%20tel%5B'sape'%5D%20%20%20%20%0Aprint%20%28tel%29%0A%23%20key%20a%20list%20of%20the%20keys%0Aprint%28tel.keys%28%29%29%20%20%20%0A%23%20list%20of%20values%0Aprint%28tel.values%28%29%29%0A%23%20keys%20and%20values%0Aprint%28tel.items%28%29%29&codeDivHeight=400&codeDivWidth=350&cumulative=false&curInstr=0&heapPrimitives=nevernest&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe>
for key, val in tel.items():
print(f"{key=}, {val=}")
key='jack', val=4098
key='guido', val=4127
'guido' in tel # check if a key is in the dictionary
True
Exercises#
Some based on “A Primer on Scientific Programming with Python”, Lantangen, Springer
Sorted dictionary#
Write a program which prints a sorted key list of a given dictionary. (hint: Check the sorted
function, or try to sort the keys()
function output)
### BEGIN SOLUTION
### END SOLUTION
Copying a dictionary#
Search how to copy a dictionary (different to making a reference)
### BEGIN SOLUTION
### END SOLUTION
Polynomial#
Representing a polynomial by a dictionary : Consider the polynomial \(p(x) = -1 + x^2 + 3x^7\) . It can be represent by means of a dictionary as p = {0:-1, 2:1, 7:3}
(What is the advantage over using a list?). Write a function which gets a dictionary representing a polynomial, an x
value, and returns the polynomial evaluated on that x
value.
### BEGIN SOLUTION
### END SOLUTION
Histogram#
Make a function which, given a dictionary with values
as ints (a simple version of a histogram), prints the histogram counters as =
. For example (the keys can be arbitrary)
A : ========
B : =============
C : =======
D : ==
E : ==
dict = {'A':8, 'B':12, 'C':5, 'D':2, 'E':2}
### BEGIN SOLUTION
def print_histo(data):
for k, v in data.items():
print (k, ':', '='*v)
### END SOLUTION
print_histo(dict)
A : ========
B : ============
C : =====
D : ==
E : ==
Histogram of a text#
Make a program which reads a text and counts the numbers of ocurrences for each word.
### BEGIN SOLUTION
### END SOLUTION
Dictionaries and lists for polynomial#
Modify the previous program for representing a polynomial with a dictionary to represent it with a list. Use both representations for the polynomyal \(-\frac{1}{2} + 2x^{100}\). Print both representations and use both to evaluate that polynomial at \(x = 1.05\) .
### BEGIN SOLUTION
### END SOLUTION
Polynomial derivative#
By using the dictionary plynomial representation of the previous exercises, write a function which computes the derivative of a given polynomial and returns a dictionary representation of the new polynomial representing the derivative. Test it.
### BEGIN SOLUTION
### END SOLUTION