Una seqüència és un conjunt d'elements agrupats un radera de l'altre.

Introducció

Processar seqüències d'elements (números enters, reals, textos...) agrupats un darrera l'altra és molt habitual.

Llista

Pyhton té una estructura de dades de tipus list que es fa servir per guardar els elements que tu vulguis en una llista.

El tipus list equival a l'Array de TypeScript

Imagina't que vols guardar la temperatura de Barcelona dels últims 5 dies (mesurat a les 11 del matí).

Crea el fitxer test.py:

Amb el que saps fins ara ho pots fer d'aquesta manera:

t0 = 23.5
t1 = 25.5
t2 = 21.2
t3 = 27.4
t4 = 25.5

Si vols saber la mitja de la temperatura dels útlims 5 dies:

medium = (t0 + t1 + t2 + t3 + t4) / 5
print(f"{medium:.2f}")

Executa el codi:

$ python3 test.py

Com pots veure el que tenim és una llista de temperatures.

En aquests casos és millor utilitzar una list.

La llista més senzilla és una llista buida:

>>> type([])
<class 'list'>

Modifica el fitxer test.py i utilitza una llista enlloc de les 5 variables:

ts = [ 23.5, 25.5, 21.2, 27.4, 25.5 ]

Per calcular la mitjana has d'accedir als elements de la llista.

Això és fa indicant l'index de l'element, començant des del zero.

medium = (ts[0] + ts[1] + ts[2] + ts[3] + ts[4]) / 5
print(f"{medium:.2f}")

Executa el codi:

$ python3 test.py
24.62

El codi ha millorat perquè ara quedar clar que tots aquests float estan relacionats de manera seqüèncial (tene un ordre).

També has aprés que pots accedir als element d'una llista indicant la seva posició dins de la seqüència.

Accés posicional

Pots accedir a un element d'una llista indicant la seva posició:

>>> xs = ["a","b","c","d","e","f","g","h","i","j","k","l"]
>>> xs[0]
'a'
>>> xs[5]
'f'

També pots accedir començant a contar desde el final amb posicions negatives:

xs = ["a","b","c","d","e","f","g","h","i","j","k","l"]
>>> xs[-1]
'l'
>>> xs[-3]
'j'
>>> xs[-0]
'a'

Pots veure que -0 i 0 és la mateixa posició.

Que passa si intento accedir a una posició que no existeix?

>>> xs = ["a","b","c","d","e","f","g","h","i","j","k","l"]
>>> xs[99]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range
>>> 

Es produeix un IndexError

for

Una de les operacions més habituals és recorrer una llista de principi a fi per obtenir un resultat.

Si vols pots fer un "print" de tots els elements de la llista:

for i in ts:
    print(i)

Com ja vam explicar a Computació, un dels usos més habituals d'una variable és anar acumulant el resultat de operacions que anem fent.

Si volem sumar el contingut de la llista, enlloc d'imprimir el valor de cada element, el podem anar sumant a una variable:

sum = 0
for t in ts:
    sum += t
medium = sum / 5

print(f"{medium:.2f")

Activitats

1.- Donada una llista de temperatures, digues quina és la més alta:

ts = [25.6, 26, 27, 24.3, 28, 24.5, 26.7]

ts = [25.6, 26, 27, 24.3, 28, 24.5, 26.7]

max = 0
for t in ts:
    if t > max:
        max = t

print(max)

2.- Tens una llista de noms. Amb l'operador in pots saber si la "laura" està dins la llista:

ns = ["joan", "laura", "jordi", "esther", "david", "miquel"]

match = "laura" in ns
print(match)

Escriu un codi que enlloc d'utilizar in (la solució simple que has de fer servir en la majoria dels casos), utilizi un for per buscar la "laura":

ns = ["joan", "laura", "jordi", "esther", "david", "miquel"]

match = False
for n in ns:
    if n == "laura":
        match = True
        break

print(match)

Slice

A vegades no vols tots els elements de la llista, o la vols al revés.

L'operador "slice" et permet crear una llista a partir d'una altra llista.

Per exemple, imagina que tinc la llista xs:

xs = [3,5,5,6,3,4,3,7,8,9,7,6,6,4]

Si només vols els tres primers elements de la llista ...

Doncs fas un "slice" de xs amb xs[:3]:

xs = [3,5,5,6,3,4,3,7,8,9,7,6,6,4]
ys = xs[:3]

assert ys == [3,5,5]

Si si vols imprimir els tres primers elements de la llista:

  1. Fas un "slice"
  2. Fas un for del "slice"
xs = [3,5,5,6,3,4,3,7,8,9,7,6,6,4]

for x in xs[:3]:
    print(x)

Sintaxi

La sintaxi bàsica per crear un slice a Python és la següent xs[start:end:step], on:

  • start: Índex on comença el slice, per defecte 0
  • end: Índex on acaba l'slice, per defecte len(xs)
  • step: Mida del pas entre elements de l'slice, per defecte 1.

Si vols pots utilitzar tots els valors per defecte.

xs = [6, 7, -3, 34]
ys = xs[::]

assert ys == xs

Has creat una llista ys identica a xs.

Activitats

1.- Obre un terminal interactiu i fes provant diferents "slices" amb una llista de números.

>>> xs = ["a","b","c","d","e","f","g","h","i","j","k","l"]
>>> xs[2:5]
['c', 'd', 'e']
>>> xs[-3:-1]
['j', 'k']

2.- Fes un "slice" amb el tres útltims elements de la llista:

xs = [1,2,3,4,5,6,7]

xs = [1,2,3,4,5,6,7]
ys = xs[-3:]

assert ys == [5,6,7]

3.- Recorre aquesta llista a la inversa i saltant de 3 en 3.

xs = [3,5,5,6,3,4,3,7,8,9,7,6,6,4]

xs = [3,5,5,6,3,4,3,7,8,9,7,6,6,4]

for x in xs[:-1:3]:
    print(x)

List Comprehension

Molts cops no vols tots els elements d'una llista, només aquells que tenen una característica especial.

Per exemple, tinc aquesta llista:

xs = [6, 7, -3, 34, -8, -10, 3]

Si només vull els números positus:

xs = [6, 7, -3, 34, -8, -10, 3]

ys = [x for x in xs if x >= 0]

assert xs == [6, 7, 34, 3]

Si tens una llista de fruites i en vols crear una de nova que contingui només les fruites amb la lletra "a" al nom:

fruits = ["apple", "banana", "cherry", "kiwi", "mango"]

newlist = [x for x in fruits if "a" in x]

assert newlist == ["apple", "banana", "mango"]

Si tens una llista de preus i els vols incrementar un 20%:

prizes = [1.60, 2.25, 3.50, 4.80, 6.75]

new_prizes = [x for x in prizes]

print(new_prizes)

String

Un str tambés és pot tractar com si fos una seqüència:

msg = "Hello World"

assert msg[:5] == "Hello"

assert ["o","o"] == [c for c in msg if c == "o"]

Altres

Llistes heterogènies.

Una llista és una seqüència de valors que poden ser de qualsevol tipus.

Els valors en una llista són anomenats elements o de vegades ítems.

Hi ha diverses maneres de crear una nova llista; la més simple és tancar els elements en claudàtors ([ i ]):

[10, 20, 30, 40]
["granota", "ovella", "estruç"]

El primer exemple és una llista de 4 int. El segon exemple és una llista de tres str.

Els elements d'una llista no han de ser del mateix tipus.

La llista següent conté una cadena, un flotant, un sencer, i (mira!) una altra llista:

["spam", 2.0, 5, [10, 20]]

Llistes imbricades.

Una llista dins d'una altra llista està imbricada. En castellà aninades, i en anglès nested.

Una llista que no conté elements és anomenada una llista buida; pots crear-ne una amb claudàtors buits, [].

Com pots veure, pots assignar els valors d'una llista a variables:

>>> cheeses = ['Cheddar', 'Edam', 'Gouda']
>>> numbers = [17, 123]
>>> empty = []
>>> print(cheeses, numbers, empty)
['Cheddar', 'Edam', 'Gouda'] [17, 123] []

Les llistes són mutables

Pots accedira als elements d'una llista amb l'operador claudàtor. L'expressió dins dels claudàtors especifica l'índex. Els índexs comencen a 0:

>>> print(cheeses[0])
Cheddar

Les llistes són mutables perquè poden canviar l'ordre dels elements en una llista o reassignar un element en una llista. Quan l'operador claudàtor apareix a la banda esquerra d'una assignació, aquest identifica l'element de la llista que s'assignarà.

>>> numbers = [17, 123]
>>> numbers[1] = 5
>>> print(numbers)
[17, 5]

L'element a la posició 1 de numbers, que abans era 123, ara és 5.

Podeu pensar en una llista com una relació entre índexs i elements. Aquesta relació és anomenada mapeig; cada índex “mapeja” un dels elements.

Els índexs d'una llista:

  • Qualsevol tipus de int es pot utilitzar com a índex.
  • Si intentes llegir o escriure un element que no existeix, obtindràs un IndexError.
  • Si un índex té un valor negatiu, aquest compta cap enrere des del final de la llista.

L'operador in et permet saber si un element està en una llista:

>>> cheeses = ['Cheddar', 'Edam', 'Gouda']
>>> 'Edam' in cheeses
True
>>> 'Brie' in cheeses
False

Operacions de llistes. Concaternació.

L'operador + concatena llistes:

>>> a = [1, 2, 3]
>>> b = [4, 5, 6]
>>> c = a + b
>>> print(c)
[1, 2, 3, 4, 5, 6]

De la mateixa manera, l'operador * repeteix una llista un determinat nombre de vegades:

>>> [0] * 4
[0, 0, 0, 0]
>>> [1, 2, 3] * 3
[1, 2, 3, 1, 2, 3, 1, 2, 3]

Al primer exemple es repeteix quatre vegades. Al segon exemple es repeteix la llista tres vegades.

Si cerqueu altres formes de concaternar, us trobareu el mètode append; que serveix per a afegir un únic element al final de la llista.

noms = ["Manel", "Roser", "Omar", "Laura"]
nou_nom = "David"
noms.append(nou_nom)
print(noms)
["Manel", "Roser", "Omar", "Laura", "David"]

Si proveu de

noms = ["Manel", "Roser", "Omar", "Laura"]
nous_nom = ["Jordi", "Claudia", "Rosa"]
noms.append(nous_noms)
print(noms)

El que passa és que es crea una llista imbricada a la última posició.

["Manel", "Roser", "Omar", "Laura", ["Jordi", "Claudia", "Rosa"]]

Si esteu segurs que únicament voleu afegir un element al final podeu usar append.

En altres casos, és molt més eficient i simple usar l'operador '+'.

Edició de llistes amb slices.

Com que les llistes són mutables, de vegades és útil fer-ne una còpia abans de fer operacions que la modifiquen.

Un operador slice al costat esquerre d'una assignació et permet actualitzar múltiples elements; com substituïr elements de llistes.

>>> t = ['a', 'b', 'c', 'd', 'e', 'f']
>>> t[1:3] = ['x', 'y']
>>> print(t)
['a', 'x', 'y', 'd', 'e', 'f']

Us he ficat, com hem aconseguit reemplaçar els valors ['b' i 'c'] per ['x', 'y'] ??

sort

Ordenar llistes amb Python és molt senzill. Podem utilitzar una funció que ens permet generar la llista ordenada.

list.sort(reverse=True|False, key=myFunc))

La funció sort té 2 paràmetres opcionals:

  • reverse si és igual a True mostra la llista en ordre invers. Per defecte el valor és false.
  • key si volem ordenar amb altres criteris diferents a l'alfabètic o els números ordinals ho podeu fer des d'aquí.Per defecte està buit. Ho veurem més endavant.

Anem a veure dos exemples:

>>> languages = ['Rust', 'Python', 'C', 'PHP', 'Java']
>>> languages.sort()
>>> print(languages)
['C', 'Java', 'PHP', 'Python', 'Rust']
>>> pluges = [0.2, 8.6, 34.4, 16.5, 81.3, 2.8]
>>> pluges.sort(reverse=True)
>>> print(pluges)
[81.3, 34.4, 16.5, 8.6, 2.8, 0.2]
# Retornarà None, ja que la funció sort no retorna cap valor, només ordena.
#print(pluges.sort(reverse=True)) 

Activitats (for, operacions, slices, sort)

1.- Crea la següent llista imbrincada, amb mostres de grup i RH sanguini de pacients en Python.

[['A+', 'A-', 'AB+', 'O+'], ['A+', 'O+'], ['B+', 'O-', 'AB-']]

nested_list = [ ["A+","A-","AB+","O+"],["A+","O+"],["B+","O-","AB-"]]
print(nested_list)

2.- Tenim dues llistes amb el volum en mL d'unes ampolles. Fes que la segona llista estigui després de la primera. Despres, crea una nova llista amb els números ordenats de major a menor.

water_level = [730, 709, 682, 712, 733, 751, 740]
new_water_level = [772, 770, 745]

water_level = [730, 709, 682, 712, 733, 751, 740]
new_data = [772, 770, 745]

combined_list = water_level + new_water_level
combined_list.sort(reverse=True)
print(combined_list)

range, generar seqüències.

La funció range et permet generar automàticament seqüències de números.

Per exemple, si vols mostrar els números de l'1 al 5 sense crear un bucle i posar-los en una llista.

ns = list(range(1,5))
print(ns)

És una forma més eficient i legible que usar un bucle i posar els números un a un, com ho fariem:

i: int = 0
while i < 11:
    numbers[i]=i
    i+=1

print(numbers)

La conversió del range a list és molt menys costosa que el bucle.

Com els slices, la funció range té 3 paràmetres:

  • start: Opcional. Número d'inici del rang. Per defecte és el 0.
  • stop: Obligatori. Valor final del rang.
  • step: Opcional. Per defecte 1. És útil si volem crear un rang seguint un patró (per exemple, de 3 en 3).

Activitats (range i list comprehension)

1. A partir d'una llista, que va del 1 al 10, crea una nova llista amb els valors al quadrat de la primera.

Solució usant List Comprension.

numbers = range(1,11)
llista2 = [ num**2 for num in numbers]
llista2

Solució sense List Comprension

numbers = range(1,11)
i: int = 0
while i < len(numbers):
    numbers[i]=numbers[i]**2
    i+=1

Resultat:

> [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

2. Tenim una llista de productes. Has de crear una nova llista que apliqui el 21% d'IVA i només hi hagi els que tenen un preu inferior a 100.0

preus = [75.50, 88.00, 100.50, 96.75, 69.90]

Resultat esperat:

['91.36', '84.58']

preus = [75.50, 88.00, 100.50, 96.75, 69.90]
nous_preus = [f"{x * 1.21:.2f}" for x in preus if x * 1.21 < 100.0]
print(nous_preus)

Conversió d'string a llista.

En moltes ocasions ens interessa separar els caràcters d'una cadena de caràcters a una llista.

Per exemple, per obtenir les paraules d'una frase simple (per simplificar, suposarem que que no tindrà comes) ens anirà molt bé el mètode split.

frase = "Hola, com estàs?"
paraules = frase.split()
print(paraules)

Resultat:

['Hola,', 'com', 'estàs?']

El mètode split pot tenir 2 paràmetres opcionals:

  • separator el símbol amb el qual volem separar la cadena en elements de llista. És habitual usar ;,,,\n... Si no li posem cap serà l'espai.
  • maxsplit si no volem separar tota la llista podem posar-li el número d'elements que volem. Per exemple 2.

Si us ha entrat curiositat de com separar paraules d'un text sencer (tenint en compte les comes, punts, símbols de pregunta i/o exclamació) necessitem una tècnica més avançada, que veurem més endavant, les expressions regulars.

import re

text = """Hola, com estàs?
Aquest és el segon paràgraf.
Ens veiem després!"""

# Utilitzem \b\w+\b per buscar paraules, ignorant salts de línia i puntuació.
paraules = re.findall(r'\b\w+\b', text)
print(paraules)

Resultat:

['Hola', 'com', 'estàs', 'Aquest', 'és', 'el', 'segon', 'paràgraf', 'Ens', 'veiem', 'després']
11

Una altre cas habitual en el que volem separar una cadena és si volem separarla en blocs d'un número de caràcters.

A Bioinformàtica, per exemple ens serà molt útil separar una cadena amb una seqüència de bases nitrogenades (ARN) de 3 en 3 per tal d'obtenir una llista de codons.

sequence = "AUGGCUUACGGAUCAAGCUA"
codons = []
# Utilitzem un bucle `for` per separar els codons de 3 en 3
for i in range(0, len(sequence), 3):
    codon = sequence[i:i+3]
    codons.append(codon)
print('Codons:', codons)

Resultat:

Codons: ['AUG', 'GCU', 'UAC', 'GGA', 'UCA', 'AGC']

Set (Conjunt)

Una alternativa a les llistes és el set (conjunt); que permeten agrupar elements amb aquestes carecteristiques.

  • S'utilitzen quan vols tenir un conjunt d'elements que no es repeteixin.
  • Els elements no tenen ordre, (realment tenen ordre d'inserció), quan començes a utilitzarlos amb diferents operacions, aquest ordre es pot perdre. És una de les limitacions respecte les llistes.
  • Es poden realitzar operacions d'àlgebra entre conjunts: unió, intersecció, diferència ...

Creem un set, i hi afegim elements, fixem-nos que encara que li afegim elements duplicats simplement no els afegeix.

num_set: set = set() 
num_set.add(1)
num_set.add(1)
num_set.add(1)
num_set.add(2)
num_set.add(2)
num_set

La longitud s'obté amb el mètode len, com la llista.

len(num_set)

2

Si volem treure l'últim element, usem pop.

num_set.pop()

1

Operacions de conjunts

seta:set = {1,2,3}
setb:set = {3,4,5}
seta
setb

Fem la union, i així de pas eliminem els duplicats.

seta.union(setb)
seta | setb

Si volem trobar ràpidament els únics elements que estan als 2 conjunts:

seta.intersection(setb)
seta & setb

Si volem eliminar els duplicats d'una llista, la podem passar a un set.

types_list = ["journal","others","journal","journal","journal","others"]
types_set = set(types_list)
types_set

L'ús de sets és menys habitual que el de les llistes, però ja heu vist com ens facilita l'eliminació de duplicats i operacions de conjunts.

Activitat, set.

1.- Duess ofertes de feina que ens interessen demanen dominar aquestes tecnologies. Mostra el conjunt de tecnologies sencer sense duplicats i el conjunt de tecnologies que demanen en ambdós ofertes.

oferta1 = ["Python", "FastAPI", "Bokeh", "Angular", "PostgreSQL"]
oferta2 = ["Python", "MariaDB", "PostgreSQL", "FastAPI", "Flask", "Plotly", "React"]

Sortida esperada:

Llista tecnologies :
 {'MariaDB', 'Plotly', 'Angular', 'Python', 'PostgreSQL', 'Flask', 'React', 'Bokeh', 'FastAPI'}
Llista tecnologies comunes:
 {'Python', 'PostgreSQL', 'FastAPI'}

soferta1 = set(oferta1)
soferta2 = set(oferta2)
print(f"Llista tecnologies :\n {soferta1.union(soferta2)}")
print(f"Llista tecnologies comunes:\n {soferta1.intersection(soferta2)}")