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])}")
140313412033832, 140313412033864, 140313412033896, 140313412035528
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#

Exercise: Write a function that receives a list and prints it in reverse order every 2 elements
def printreversed(l):
    # YOUR CODE HERE
    raise NotImplementedError()
printreversed([1, 2, 3])
printreversed([1, 2, 3, 4 , "hola", "mundo"])
---------------------------------------------------------------------------
NotImplementedError                       Traceback (most recent call last)
Cell In[9], line 1
----> 1 printreversed([1, 2, 3])
      2 printreversed([1, 2, 3, 4 , "hola", "mundo"])

Cell In[8], line 3, in printreversed(l)
      1 def printreversed(l):
      2     # YOUR CODE HERE
----> 3     raise NotImplementedError()

NotImplementedError: 
Exercise: Write a function that receives a positive integer and returns a list with all its prime factors
import numpy as np

def primefactors(n):
    # YOUR CODE HERE
    raise NotImplementedError()

# 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):
    # YOUR CODE HERE
    raise NotImplementedError()
print(primefactors(2))
print(primefactors(8))
print(primefactors(10))
print(primefactors(201))
print(primefactors(97))
print(primefactors(1213427))
print(primefactors(1213428))
print(primefactors(1213428987543096))
Exercise: Write a function that receives a list of numbers and returns its mean, max and min values . Use numpy
import numpy as np

def stats(data):
# YOUR CODE HERE
raise NotImplementedError()
stats([1, 2, 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)

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)

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)
%%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} )")
import Point3D as P3D
p3 = P3D.Point3D(2, 5, 0.9)
print (p3)

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:

  • https://realpython.com/python-dicts/

  • https://quickref.me/python#python-data-types

# 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())
%%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=}")
'guido' in tel   # check if a key is in the dictionary

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)

# YOUR CODE HERE
raise NotImplementedError()

Copying a dictionary#

Search how to copy a dictionary (different to making a reference)

# YOUR CODE HERE
raise NotImplementedError()

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.

# YOUR CODE HERE
raise NotImplementedError()

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}

# YOUR CODE HERE
raise NotImplementedError()
print_histo(dict)

Histogram of a text#

Make a program which reads a text and counts the numbers of ocurrences for each word.

# YOUR CODE HERE
raise NotImplementedError()

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\) .

# YOUR CODE HERE
raise NotImplementedError()

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.

# YOUR CODE HERE
raise NotImplementedError()