Ací es mostren les diferències entre la revisió seleccionada i la versió actual de la pàgina.
Ambdós costats versió prèvia Revisió prèvia Següent revisió | Revisió prèvia | ||
opendata_pandas [2023/07/04 22:09] miquel_angel_amoros [Exemple aplicació web] |
opendata_pandas [2023/07/04 23:23] (actual) miquel_angel_amoros [Per què triem Pandas ?] |
||
---|---|---|---|
Línia 8: | Línia 8: | ||
En aquesta guia veurem com obtenir dades obertes d'un servei web i guardar-les, | En aquesta guia veurem com obtenir dades obertes d'un servei web i guardar-les, | ||
- | Per aconseguir-ho, | + | Per aconseguir-ho, |
+ | |||
+ | Posteriorment, | ||
<WRAP center round important 90%> | <WRAP center round important 90%> | ||
Línia 16: | Línia 18: | ||
======= Per què triem Pandas ? ======= | ======= Per què triem Pandas ? ======= | ||
- | * Ens proporciona una col·lecció molt útil, inexistent a Python, el DataFrame. És una taula bidimensional que podem indexar amb la/les columna/es que més ens interessin en cada monment | + | * Ens proporciona una col·lecció molt útil, inexistent a Python, el DataFrame. És una taula bidimensional que podem indexar amb la/les columna/es que més ens interessin en cada moment |
* Pandas detecta automàticament inconsistències de tipus i contingut en les dades. Aconseguim avantatges similars als llenguatges de tipat fort (C, Java). | * Pandas detecta automàticament inconsistències de tipus i contingut en les dades. Aconseguim avantatges similars als llenguatges de tipat fort (C, Java). | ||
* Ens permet tractar grans volums de dades (superiors a 10.000 lines) amb molts mètodes per filtrar la informació. | * Ens permet tractar grans volums de dades (superiors a 10.000 lines) amb molts mètodes per filtrar la informació. | ||
Línia 22: | Línia 24: | ||
* Si organitzem les dades del Dataframe convenientment podem crear gràfics amb llibreries com Matplotlib o Seaborn, amb menys codi que si el posessim en llistes o diccionaris. | * Si organitzem les dades del Dataframe convenientment podem crear gràfics amb llibreries com Matplotlib o Seaborn, amb menys codi que si el posessim en llistes o diccionaris. | ||
* També facilita la feina si usem llibreries de Machine Learning. | * També facilita la feina si usem llibreries de Machine Learning. | ||
- | * És una molt bona alternativa al llenguatge R, amb una corba d' | ||
- | |||
======= Instal·lació llibreria Pandas ======= | ======= Instal·lació llibreria Pandas ======= | ||
Línia 39: | Línia 39: | ||
Exemple: Volem obtenir el llistat dels equips de la lliga de futbol femení 2022-2023, que hem trobat a: | Exemple: Volem obtenir el llistat dels equips de la lliga de futbol femení 2022-2023, que hem trobat a: | ||
+ | |||
https:// | https:// | ||
En aquest cas potser acabem abans copiant i enganxant en un full de càlcul els resultats; però a la hora de la veritat no sempre funciona. | En aquest cas potser acabem abans copiant i enganxant en un full de càlcul els resultats; però a la hora de la veritat no sempre funciona. | ||
+ | |||
A més a més, ens interessa automatitzar les tasques. | A més a més, ens interessa automatitzar les tasques. | ||
+ | |||
+ | **Exemple codi local, arrencat en un fitxer IPYNB** | ||
+ | |||
+ | El codi que mostro a continuació el podeu veure arrencat en directe a: | ||
+ | |||
+ | https:// | ||
+ | |||
+ | [[https:// | ||
+ | en qualsevol IDE, fins i tot llibreries per generar gràfics. Els seus fitxers tenen la extensió **.IPYNB** | ||
**Pas 1. Seleccionar la font de dades.** | **Pas 1. Seleccionar la font de dades.** | ||
En el nostre cas, dins de la pàgina indicada la taula que ens interessa té un atribut HTML " | En el nostre cas, dins de la pàgina indicada la taula que ens interessa té un atribut HTML " | ||
- | valor " | + | valor " |
+ | El mètode read_html ha de conèixer la pàgina i atributs html per identificar la taula | ||
que volem extreure. | que volem extreure. | ||
**Pas 2. Obtenir les dades amb Pandas** | **Pas 2. Obtenir les dades amb Pandas** | ||
- | < | + | < |
import pandas as pd | import pandas as pd | ||
from io import StringIO | from io import StringIO | ||
Línia 70: | Línia 82: | ||
**Pas 3. Filtrar dades i obtenir resultats.** | **Pas 3. Filtrar dades i obtenir resultats.** | ||
- | < | + | < |
# Seleccionem la llista dels noms dels equips. | # Seleccionem la llista dels noms dels equips. | ||
classificacio_df[' | classificacio_df[' | ||
Línia 87: | Línia 99: | ||
- | ======= | + | ======= |
- | Pas 4. Inserim el codi que hem comprovat a la aplicació django, dins de l' | + | **Pas 4. Inserim el codi que hem comprovat a la aplicació django, dins de l' |
- | Hem de substuïr aquest | + | En primer lloc, posem el codi que hem provat abans: |
- | < | + | < |
+ | def handle(self, | ||
+ | print(" | ||
+ | # https:// | ||
+ | # Web scrapping: obtenció de dades externes al nostre sistema. | ||
+ | url: str = " | ||
+ | classif: pd.DataFrame = pd.read_html(url, | ||
+ | # Per algún motiu que desconec la informació que m' | ||
+ | # la primera posició d'una llista. | ||
+ | classificacio_df = classif[0] | ||
+ | # Seleccionem la llista dels noms dels equips. | ||
+ | classificacio_df[' | ||
+ | list_aux = classificacio_df[' | ||
+ | llista_equips = sum(list_aux, | ||
+ | print(llista_equips) | ||
+ | print(" | ||
+ | |||
+ | # | ||
+ | titol_lliga = options[' | ||
+ | ... | ||
</ | </ | ||
- | Per aquest | + | Després del codi que crea la lliga si no existeix, comento el codi de creació d' |
+ | i poso aquest, que crea i guarda els equips pel seu nom: | ||
+ | |||
+ | <code python> | ||
+ | for nom_equip in llista_equips: | ||
+ | equip = Equip(nom=nom_equip) | ||
+ | # | ||
+ | equip.save() | ||
+ | lliga.equips.add(equip) | ||
+ | |||
+ | # Seleccionarem algunes jugadores. | ||
+ | print(" | ||
+ | ... | ||
+ | </ | ||
+ | |||
+ | |||
+ | --> Codi complet del seeder i el model # | ||
+ | Aquí podreu veure un exemple del codi de l' | ||
+ | |||
+ | <file python / | ||
+ | from django.db import models | ||
+ | |||
+ | # Observació: | ||
+ | # com a clau primària. Hi ha formes de canviar aquest | ||
+ | # comportament però per ara ens sembla OK. | ||
+ | |||
+ | # Hauria de posar una relació ManyToMany per tal que un mateix equip | ||
+ | # pugui compatir en diverses lligues en un any (Champions) | ||
+ | # equips = models.ManyToManyField(Equip) | ||
+ | class Equip(models.Model): | ||
+ | nom = models.CharField(max_length=100) | ||
+ | ciutat = models.CharField(max_length=100, | ||
+ | estadi = models.CharField(max_length=100, | ||
+ | #lliga = models.ForeignKey(Lliga, | ||
+ | def __str__(self): | ||
+ | return self.nom | ||
+ | |||
+ | |||
+ | class Lliga(models.Model): | ||
+ | nom_temporada = models.CharField(max_length=100) | ||
+ | equips = models.ManyToManyField(Equip) | ||
+ | def __str__(self): | ||
+ | return self.nom_temporada | ||
+ | class Meta: | ||
+ | verbose_name_plural = ' | ||
+ | |||
+ | |||
+ | class Jugadora(models.Model): | ||
+ | nom = models.CharField(max_length=100) | ||
+ | cognom = models.CharField(max_length=100) | ||
+ | dorsal = models.IntegerField() | ||
+ | equip = models.ForeignKey(Equip, | ||
+ | |||
+ | def __str__(self): | ||
+ | return f' | ||
+ | class Meta: | ||
+ | verbose_name_plural = ' | ||
+ | |||
+ | |||
+ | class Partit(models.Model): | ||
+ | class Meta: | ||
+ | unique_together = [" | ||
+ | local = models.ForeignKey(Equip, | ||
+ | related_name=" | ||
+ | visitant = models.ForeignKey(Equip, | ||
+ | related_name=" | ||
+ | lliga = models.ForeignKey(Lliga, | ||
+ | detalls = models.TextField(null=True, | ||
+ | inici = models.DateTimeField(null=True, | ||
+ | def __str__(self): | ||
+ | return "{} - {}" | ||
+ | def gols_local(self): | ||
+ | return self.event_set.filter( | ||
+ | tipus=Event.EventType.GOL, | ||
+ | def gols_visitant(self): | ||
+ | return self.event_set.filter( | ||
+ | tipus=Event.EventType.GOL, | ||
+ | |||
+ | |||
+ | class Event(models.Model): | ||
+ | # Solució subòptima i provisional. | ||
+ | # tipus_event = models.CharField(max_length=100) | ||
+ | # el tipus d' | ||
+ | class EventType(models.TextChoices): | ||
+ | GOL = " | ||
+ | AUTOGOL = " | ||
+ | FALTA = " | ||
+ | PENALTY = " | ||
+ | MANS = " | ||
+ | CESSIO = " | ||
+ | FORA_DE_JOC = " | ||
+ | ASSISTENCIA = " | ||
+ | TARGETA_GROGA = " | ||
+ | TARGETA_VERMELLA = " | ||
+ | partit = models.ForeignKey(Partit, | ||
+ | temps = models.TimeField() | ||
+ | tipus = models.CharField(max_length=30, | ||
+ | jugador = models.ForeignKey(Jugadora, | ||
+ | on_delete=models.SET_NULL, | ||
+ | related_name=" | ||
+ | equip = models.ForeignKey(Equip, | ||
+ | on_delete=models.SET_NULL) | ||
+ | # per les faltes | ||
+ | jugador2 = models.ForeignKey(Jugadora, | ||
+ | on_delete=models.SET_NULL, | ||
+ | related_name=" | ||
+ | detalls = models.TextField(null=True, | ||
+ | def __str__(self): | ||
+ | return f' | ||
+ | </ | ||
+ | |||
+ | |||
+ | <file python models.py> | ||
+ | from django.db import models | ||
+ | |||
+ | # Observació: | ||
+ | # com a clau primària. Hi ha formes de canviar aquest | ||
+ | # comportament però per ara ens sembla OK. | ||
+ | |||
+ | # Hauria de posar una relació ManyToMany per tal que un mateix equip | ||
+ | # pugui compatir en diverses lligues en un any (Champions) | ||
+ | # equips = models.ManyToManyField(Equip) | ||
+ | class Equip(models.Model): | ||
+ | nom = models.CharField(max_length=100) | ||
+ | ciutat = models.CharField(max_length=100, | ||
+ | estadi = models.CharField(max_length=100, | ||
+ | #lliga = models.ForeignKey(Lliga, | ||
+ | def __str__(self): | ||
+ | return self.nom | ||
+ | |||
+ | |||
+ | class Lliga(models.Model): | ||
+ | nom_temporada = models.CharField(max_length=100) | ||
+ | equips = models.ManyToManyField(Equip) | ||
+ | def __str__(self): | ||
+ | return self.nom_temporada | ||
+ | class Meta: | ||
+ | verbose_name_plural = ' | ||
+ | |||
+ | |||
+ | class Jugadora(models.Model): | ||
+ | nom = models.CharField(max_length=100) | ||
+ | cognom = models.CharField(max_length=100) | ||
+ | dorsal = models.IntegerField() | ||
+ | equip = models.ForeignKey(Equip, | ||
+ | |||
+ | def __str__(self): | ||
+ | return f' | ||
+ | class Meta: | ||
+ | verbose_name_plural = ' | ||
+ | |||
+ | |||
+ | class Partit(models.Model): | ||
+ | class Meta: | ||
+ | unique_together = [" | ||
+ | local = models.ForeignKey(Equip, | ||
+ | related_name=" | ||
+ | visitant = models.ForeignKey(Equip, | ||
+ | related_name=" | ||
+ | lliga = models.ForeignKey(Lliga, | ||
+ | detalls = models.TextField(null=True, | ||
+ | inici = models.DateTimeField(null=True, | ||
+ | def __str__(self): | ||
+ | return "{} - {}" | ||
+ | def gols_local(self): | ||
+ | return self.event_set.filter( | ||
+ | tipus=Event.EventType.GOL, | ||
+ | def gols_visitant(self): | ||
+ | return self.event_set.filter( | ||
+ | tipus=Event.EventType.GOL, | ||
+ | |||
+ | |||
+ | class Event(models.Model): | ||
+ | # Solució subòptima i provisional. | ||
+ | # tipus_event = models.CharField(max_length=100) | ||
+ | # el tipus d' | ||
+ | class EventType(models.TextChoices): | ||
+ | GOL = " | ||
+ | AUTOGOL = " | ||
+ | FALTA = " | ||
+ | PENALTY = " | ||
+ | MANS = " | ||
+ | CESSIO = " | ||
+ | FORA_DE_JOC = " | ||
+ | ASSISTENCIA = " | ||
+ | TARGETA_GROGA = " | ||
+ | TARGETA_VERMELLA = " | ||
+ | partit = models.ForeignKey(Partit, | ||
+ | temps = models.TimeField() | ||
+ | tipus = models.CharField(max_length=30, | ||
+ | jugador = models.ForeignKey(Jugadora, | ||
+ | on_delete=models.SET_NULL, | ||
+ | related_name=" | ||
+ | equip = models.ForeignKey(Equip, | ||
+ | on_delete=models.SET_NULL) | ||
+ | # per les faltes | ||
+ | jugador2 = models.ForeignKey(Jugadora, | ||
+ | on_delete=models.SET_NULL, | ||
+ | related_name=" | ||
+ | detalls = models.TextField(null=True, | ||
+ | def __str__(self): | ||
+ | return f' | ||
+ | </ | ||
+ | |||
+ | <-- | ||
+ | |||
+ | Resultat final, quan arrenquem la aplicació: | ||
+ | |||
+ | Si heu canviat el model, primer executeu: | ||
< | < | ||
+ | (env) nomusuari@linux: | ||
+ | (env) nomusuari@linux: | ||
+ | </ | ||
+ | Per omplir les dades de l' | ||
+ | |||
+ | < | ||
+ | (env) nomusuari@linux: | ||
+ | (env) nomusuari@linux: | ||
+ | (env) nomusuari@linux: | ||
</ | </ | ||
+ | |||
+ | Si anem a veure la taula de partits podem comprovar que s'han generat correctament, | ||
+ | dels locals i visitants. | ||
+ | |||
+ | {{: | ||
======= Conclusions ======= | ======= Conclusions ======= | ||
- | Més info pendent. | + | Publicar i fer ús de dades públiques d' |