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/01 14:26] miquel_angel_amoros [Conclusions] |
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%> | ||
| + | Només s' | ||
| + | </ | ||
| ======= 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 18: | 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 35: | 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. | ||
| - | Pas 1. Obtenir dades | + | **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.** | ||
| + | |||
| + | En el nostre cas, dins de la pàgina indicada la taula que ens interessa té un atribut HTML " | ||
| + | valor " | ||
| + | El mètode read_html ha de conèixer la pàgina i atributs html per identificar la taula | ||
| + | que volem extreure. | ||
| + | |||
| + | **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 58: | Línia 80: | ||
| </ | </ | ||
| - | Pas 2. Filtrar dades i obtenir resultats. | + | **Pas 3. Filtrar dades i obtenir resultats.** |
| + | |||
| + | <code python> | ||
| + | # Seleccionem la llista dels noms dels equips. | ||
| + | classificacio_df[' | ||
| + | list_aux = classificacio_df[' | ||
| + | llista_equips = sum(list_aux, | ||
| - | < | ||
| - | # Visualitzem informació rellevant de les dades. | ||
| def print_data(): | def print_data(): | ||
| - | print(classificacio_df) | + | |
| print(classificacio_df.dtypes) | print(classificacio_df.dtypes) | ||
| - | print(classificacio_df.EQUIPO) | + | print(classificacio_df['EQUIPO']) |
| + | | ||
| + | | ||
| print_data() | print_data() | ||
| </ | </ | ||
| - | ======= Exemple a Jupyter Notebook ======= | ||
| - | https:// | + | ======= Codi integrat a la aplicació web amb Django ======= |
| - | Pendent millorar. | + | **Pas 4. Inserim el codi que hem comprovat a la aplicació django, dins de l' |
| + | En primer lloc, posem el codi que hem provat abans: | ||
| - | ======= Exemple aplicació web ======= | + | <code python> |
| + | def handle(self, | ||
| - | Pendent | + | 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(" | ||
| - | ======= Conclusions ======= | + | # |
| + | titol_lliga | ||
| + | ... | ||
| + | </ | ||
| - | <WRAP center round important 60%> | + | Després del codi que crea la lliga si no existeix, comento el codi de creació d' |
| - | Menys parlar | + | i poso aquest, que crea i guarda els equips pel seu nom: |
| - | </WRAP> | + | |
| + | <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 | ||
| + | # 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 | ||
| + | |||
| + | < | ||
| + | (env) nomusuari@linux: | ||
| + | (env) nomusuari@linux: | ||
| + | (env) nomusuari@linux: | ||
| + | </code> | ||
| + | |||
| + | Si anem a veure la taula de partits podem comprovar que s'han generat correctament, | ||
| + | dels locals i visitants. | ||
| + | |||
| + | {{: | ||
| + | |||
| + | ======= Conclusions ======= | ||
| - | Més info pendent. | + | Publicar i fer ús de dades públiques d' |