Clientela

Avís

Per a resoldre aquest exercici no es poden fer servir llistes, tuples, diccionaris ni cap altra estructura de dades per a desar tots els elements dels iterables.

Una botiga d’un centre comercial vol saber quants clients hi ha dins el local en cada instant del dia. A tal fi, ha instal·lat un sensor a cadascuna de les portes de la botiga que detecta el pas de persones. Cada sensor enregistra les dades en un fitxer diferent, cada línia dels quals conté la informació de l’instant (hora i minut, HH:MM), el nombre de persones i un caràcter '+' o '-' en funció de si han entrat o sortit clients. Per exemple:

09:05 2 +
09:10 1 +
09:30 1 -
09:33 3 +
09:44 2 -
10:07 2 -
10:23 1 -

Fixeu-vos que les línies del fitxer estan ordenades temporalment i els instants no estan repetits (si en un mateix minut entren i surten clients per una mateixa porta, el propi sensor ja fa la resta d’entrades i sortides i enregistra un sol valor numèric).

Es demana que dissenyeu les funcions següents i les deseu al fitxer clientela.py.

Generació de la seqüència de moviments d’una porta

clientela.seq_moviments(nomf)
Paràmetres:

nomf (str) – Nom d’un fitxer on hi ha enregistrades les entrades i sortides de clients per una porta, en el format descrit.

Retorna:

Iterador sobre una seqüència de tuples amb dues components: instant de temps (instància de la classe datetime.time) i nombre de clients que han passat per la porta (enter positiu o negatiu depenent de si han entrat o sortit persones). La seqüència estarà ordenada temporalment.

Essent 'porta1.txt' el nom d’un fitxer que conté les dades de l’exemple de més amunt, aquesta funció hauria de respondre així:


>>> from clientela import seq_moviments
>>> it = seq_moviments('porta1.txt')
>>> next(it)
(datetime.time(9, 5), 2)
>>> next(it)
(datetime.time(9, 10), 1)
>>> for temps, nc in it:
...     print(temps, nc, end='...')
... 
09:30:00 -1...09:33:00 3...09:44:00 -2...10:07:00 -2...10:23:00 -1...

Podeu implementar aquesta funciò com un generador o bé utilitzant les funcions predefinidies de python i el mòdul itertools.

Descarregueu-vos els fitxers amb dades de sensors porta1.txt, porta2.txt i porta3.txt. Disposeu de jocs de proves al fitxer tests-moviments.txt, el qual utilitza aquests tres fitxers.

Fusió de dues seqüències de moviments

Avís

La resolució d’aquest apartat ppot resultar de dificultat elevada si no es coneix l’algorisme de fusió de seqüències ordenades.

Ara que ja sabem processar la informació captada pel sensor de qualsevol porta, obtenint un iterador, volem poder fusionar dues seqüències de moviments de dues portes en una sola seqüència. El resultat serà un nou iterador sobre una seqüència de moviments semblant, és a dir, de tuples (datetime.time, int) com la que retorna la funció anterior. Per tant, estarà ordenada temporalment, sense instants repetits, i amb el càlcul realitzat del nombre de clients que han entrat o sortit per les dues portes a cada instant, com si es tractés d’una sola porta.

clientela.fusiona_seqs(it1, it2)
Paràmetres:
  • it1 – Iterador sobre una seqüència de moviments

  • it2 – Iterador sobre una segona seqüència de moviments

Retorna:

Iterador d’una seqüència de moviments, fusió de it1 amb it2

Per exemple,


>>> from clientela import seq_moviments, fusiona_seqs
>>> it1 = seq_moviments('porta1.txt')
>>> it2 = seq_moviments('porta2.txt')
>>> itf = fusiona_seqs(it1, it2)
>>> next(itf)
(datetime.time(9, 5), 2)
>>> next(itf)
(datetime.time(9, 6), 1)
>>> next(itf)
(datetime.time(9, 10), 3)
>>> next(itf)
(datetime.time(9, 21), -1)
>>> for temps, nc in itf:
...     print(temps, nc, end='...')
09:30:00 -1...09:33:00 4...09:44:00 -2...09:45:00 -2...09:51:00 1...10:07:00 -1...10:12:00 -1...10:22:00 4...10:23:00 -2...11:10:00 3...11:21:00 2...11:37:00 -1...11:38:00 -2...11:47:00 -1...12:00:00 -5...

Es recomana implementar aquesta funciò com un generador, seguint l’esquema de fusió de dues seqüències ordenades. Hi ha un cas particular al qual caldrà fer molt atenció: si en un mateix instant determinat per una de les portes han entrat tants clients com han sortit per l’altra, en la seqüència de moviments aquest instant no hi ha d’aparèixer, perquè el nombre de clients dins la botiga no ha variat.

Disposeu de jocs de proves al fitxer tests-fusiona.txt.

Càlcul del nombre de clients dins la botiga

En aquest apartat es demana una funció que calculi el nombre de clients dins la botiga a cada instant a partir d’una única seqüencia de moviments. Doncs, aquest càlcul tindrà sentit només quan s’hagin fusionat totes les seqüències de moviments en una de sola (o bé quan la botiga només té una porta d’entrada/sortida per als clients).

clientela.calcula_clients(it)
Paràmetres:

it – Iterador sobre una seqüència de moviments

Retorna:

Iterador sobre una seqüència de tuples de dues components: instant del dia (datetime.time) i nombre de clients dins la botiga a partir d’aquell instant.

Per exemple,


>>> from clientela import seq_moviments, calcula_clients
>>> it = seq_moviments('porta1.txt')
>>> itc = calcula_clients(it)
>>> next(itc)
(datetime.time(9, 5), 2)
>>> next(itc)
(datetime.time(9, 10), 3)
>>> for temps, nc in itc:
...     print(temps, nc, end='...')
09:30:00 2...09:33:00 5...09:44:00 3...10:07:00 1...10:23:00 0...

Es recomana resoldre aquest problema utilitzant alguna de les funcions del mòdul itertools, si bé també es pot fer una funció generadora.

Disposeu de jocs de proves al fitxer tests-clients.txt.

Estadístiques sobre el nombre de clients

Ara ja sabem quants clients hi ha dins la botiga al llarg del dia. Per tal d’organitzar el personal que els atendrà, el cap de l’establiment vol saber algunes dades que ho resumeixin. Caldrà que implementem aquestes dues funcions:

clientela.maxim_clients(it)
Paràmetres:

it – Iterador sobre una seqüència de clients al llarg del dia, com la que genera la funció anterior

Retorna:

Màxim nombre de clients dins la botiga i primer instant del dia en què s’ha arribat a aquest màxim. En el cas que s’atenyi el màxim en més d’una ocasió, cal retornar el darrer instant corresponent al màxim.

Per exemple,


>>> from clientela import maxim_clients
>>> from datetime import time

>>> it = iter([ (time(9,11), 2), (time(10,4), 7), (time(11,29), 5), (time(12,25), 0) ])
>>> maxim_clients(it)
(datetime.time(10, 4), 7)
>>> it = iter([ (time(9,11), 4), (time(10,4), 2), (time(11,29), 4), (time(12,25), 0) ])
>>> maxim_clients(it)
(datetime.time(11, 29), 4)

Disposeu de jocs de proves al fitxer tests-maxim.txt.

clientela.mitjana_clients(it)
Paràmetres:

it – Iterador sobre una seqüència de clients al llarg del dia, com la rebuda per la funció anterior. L’últim element de la seqüència ha de tenir un zero com a nombre de clients, ja que al final del dia no queden clients dins la botiga.

Retorna:

Mitjana ponderada del nombre de clients, que es calcula com la suma del nombre de clients multiplicat pels minuts que són dins la botiga dividida pel total de minuts entre el primer i l’últim instant de it.

Per exemple,


>>> from clientela import mitjana_clients
>>> from datetime import time

>>> it = iter([ (time(9,11), 2), (time(10,4), 7), (time(11,29), 5), (time(12,25), 3) ])
>>> print(f"{mitjana_clients(it):0.3f}")
5.057
>>> it = iter([ (time(9,11), 4), (time(10,4), 2), (time(11,29), 4), (time(12,25), 1) ])
>>> print(f"{mitjana_clients(it):0.3f}")
3.124

Disposeu de jocs de proves al fitxer tests-mitjanap.txt.