bytes.cat

La wiki d'FP d'informàtica

Eines de l'usuari

Eines del lloc


opendata_pandas

Diferències

Ací es mostren les diferències entre la revisió seleccionada i la versió actual de la pàgina.

Enllaç a la visualització de la comparació

Ambdós costats versió prèvia Revisió prèvia
Següent revisió
Revisió prèvia
opendata_pandas [2023/07/04 22:53]
miquel_angel_amoros [Exemple aplicació web]
opendata_pandas [2023/07/04 23:23] (actual)
miquel_angel_amoros [Per què triem Pandas ?]
Línia 18: 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 (com a SQL).+  * 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 (com a SQL).
   * 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 24: 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'aprentatge inferior si tenim coneixements bàsics de Python. 
- 
 ======= Instal·lació llibreria Pandas ======= ======= Instal·lació llibreria Pandas =======
  
Línia 47: Línia 45:
  
 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: El codi que mostro a continuació el podeu veure arrencat en directe a:
Línia 52: Línia 52:
 https://colab.research.google.com/drive/1vKHXmy5e9KFGv3EnRHwJpzeHTP5zp6I5#scrollTo=ROmuDJnMryJL https://colab.research.google.com/drive/1vKHXmy5e9KFGv3EnRHwJpzeHTP5zp6I5#scrollTo=ROmuDJnMryJL
  
-JuPyTeR (Julia, Python i R) Notebook és un servei que permet executar codi i llibreries Python al navegador o  +[[https://jupyter.org/try-jupyter/retro/notebooks/?path=notebooks/Intro.ipynb|JuPyTeR (Julia, Python i R) Notebook]] és un servei que permet executar codi i llibreries Python al navegador o  
-en qualsevol IDE, fins i tot llibreries per generar gràfics.    +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 "summary" amb el  En el nostre cas, dins de la pàgina indicada la taula que ens interessa té un atribut HTML "summary" amb el 
-valor "Clasificación". El mètode read_html ha de conèixer la pàgina i atributs html per identificar la taula+valor "Clasificación" 
 +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**
  
-<code>+<code python>
 import pandas as pd import pandas as pd
 from io import StringIO from io import StringIO
Línia 81: Línia 82:
 **Pas 3. Filtrar dades i obtenir resultats.** **Pas 3. Filtrar dades i obtenir resultats.**
  
-<code>+<code python>
 # Seleccionem la llista dels noms dels equips. # Seleccionem la llista dels noms dels equips.
 classificacio_df['EQUIPO'] = classificacio_df['EQUIPO'].astype("string") classificacio_df['EQUIPO'] = classificacio_df['EQUIPO'].astype("string")
Línia 100: Línia 101:
 ======= Codi integrat a la aplicació web amb Django ======= ======= Codi integrat a la aplicació web amb Django =======
  
-Pas 4. Inserim el codi que hem comprovat a la aplicació django, dins de l'script seeder.+**Pas 4. Inserim el codi que hem comprovat a la aplicació django, dins de l'script seeder.**
  
 En primer lloc, posem el codi que hem provat abans: En primer lloc, posem el codi que hem provat abans:
Línia 141: Línia 142:
             ...             ...
 </code> </code>
 +
 +
 +--> Codi complet del seeder i el model #
 +Aquí podreu veure un exemple del codi de l'script i també el model.py que s'utilitza. 
 +
 +<file python /management/commands/crea_lliga.py>
 +from django.db import models
 +
 +# Observació: Django genera per defecte camps id autonumèrics
 +# 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,null=True)
 +    estadi = models.CharField(max_length=100,null=True)
 +    #lliga = models.ForeignKey(Lliga, on_delete=models.CASCADE)
 +    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 = 'Lligues'
 +
 +
 +class Jugadora(models.Model):
 +    nom = models.CharField(max_length=100)
 +    cognom = models.CharField(max_length=100)
 +    dorsal = models.IntegerField()
 +    equip = models.ForeignKey(Equip, on_delete=models.CASCADE)
 +
 +    def __str__(self):
 +        return f'{self.dorsal} - {self.nom}'
 +    class Meta:
 +        verbose_name_plural = 'Jugadores'
 +
 +
 +class Partit(models.Model):
 +    class Meta:
 +        unique_together = ["local","visitant","lliga"]
 +    local = models.ForeignKey(Equip,on_delete=models.CASCADE,
 +                    related_name="partits_local",null=True,blank=True)
 +    visitant = models.ForeignKey(Equip,on_delete=models.CASCADE,
 +                    related_name="partits_visitant",null=True,blank=True)
 +    lliga = models.ForeignKey(Lliga,on_delete=models.CASCADE)
 +    detalls = models.TextField(null=True,blank=True)
 +    inici = models.DateTimeField(null=True,blank=True)
 +    def __str__(self):
 +        return "{} - {}".format(self.local,self.visitant)
 +    def gols_local(self):
 +        return self.event_set.filter(
 +            tipus=Event.EventType.GOL,equip=self.local).count()
 +    def gols_visitant(self):
 +        return self.event_set.filter(
 +            tipus=Event.EventType.GOL,equip=self.visitant).count()
 +
 +
 +class Event(models.Model):
 +    # Solució subòptima i provisional.
 +    # tipus_event = models.CharField(max_length=100)
 +    # el tipus d'event l'implementem amb algo tipus "enum"
 +    class EventType(models.TextChoices):
 +        GOL = "GOL"
 +        AUTOGOL = "AUTOGOL"
 +        FALTA = "FALTA"
 +        PENALTY = "PENALTY"
 +        MANS = "MANS"
 +        CESSIO = "CESSIO"
 +        FORA_DE_JOC = "FORA_DE_JOC"
 +        ASSISTENCIA = "ASSISTENCIA"
 +        TARGETA_GROGA = "TARGETA_GROGA"
 +        TARGETA_VERMELLA = "TARGETA_VERMELLA"
 +    partit = models.ForeignKey(Partit,on_delete=models.CASCADE)
 +    temps = models.TimeField()
 +    tipus = models.CharField(max_length=30,choices=EventType.choices)
 +    jugador = models.ForeignKey(Jugadora,null=True,
 +                    on_delete=models.SET_NULL,
 +                    related_name="events_fets")
 +    equip = models.ForeignKey(Equip,null=True,
 +                    on_delete=models.SET_NULL)
 +    # per les faltes
 +    jugador2 = models.ForeignKey(Jugadora,null=True,blank=True,
 +                    on_delete=models.SET_NULL,
 +                    related_name="events_rebuts")
 +    detalls = models.TextField(null=True,blank=True)
 +    def __str__(self):
 +        return f'{self.EventType} - {self.jugador}'
 +</file>
 +
 +
 +<file python models.py>
 +from django.db import models
 +
 +# Observació: Django genera per defecte camps id autonumèrics
 +# 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,null=True)
 +    estadi = models.CharField(max_length=100,null=True)
 +    #lliga = models.ForeignKey(Lliga, on_delete=models.CASCADE)
 +    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 = 'Lligues'
 +
 +
 +class Jugadora(models.Model):
 +    nom = models.CharField(max_length=100)
 +    cognom = models.CharField(max_length=100)
 +    dorsal = models.IntegerField()
 +    equip = models.ForeignKey(Equip, on_delete=models.CASCADE)
 +
 +    def __str__(self):
 +        return f'{self.dorsal} - {self.nom}'
 +    class Meta:
 +        verbose_name_plural = 'Jugadores'
 +
 +
 +class Partit(models.Model):
 +    class Meta:
 +        unique_together = ["local","visitant","lliga"]
 +    local = models.ForeignKey(Equip,on_delete=models.CASCADE,
 +                    related_name="partits_local",null=True,blank=True)
 +    visitant = models.ForeignKey(Equip,on_delete=models.CASCADE,
 +                    related_name="partits_visitant",null=True,blank=True)
 +    lliga = models.ForeignKey(Lliga,on_delete=models.CASCADE)
 +    detalls = models.TextField(null=True,blank=True)
 +    inici = models.DateTimeField(null=True,blank=True)
 +    def __str__(self):
 +        return "{} - {}".format(self.local,self.visitant)
 +    def gols_local(self):
 +        return self.event_set.filter(
 +            tipus=Event.EventType.GOL,equip=self.local).count()
 +    def gols_visitant(self):
 +        return self.event_set.filter(
 +            tipus=Event.EventType.GOL,equip=self.visitant).count()
 +
 +
 +class Event(models.Model):
 +    # Solució subòptima i provisional.
 +    # tipus_event = models.CharField(max_length=100)
 +    # el tipus d'event l'implementem amb algo tipus "enum"
 +    class EventType(models.TextChoices):
 +        GOL = "GOL"
 +        AUTOGOL = "AUTOGOL"
 +        FALTA = "FALTA"
 +        PENALTY = "PENALTY"
 +        MANS = "MANS"
 +        CESSIO = "CESSIO"
 +        FORA_DE_JOC = "FORA_DE_JOC"
 +        ASSISTENCIA = "ASSISTENCIA"
 +        TARGETA_GROGA = "TARGETA_GROGA"
 +        TARGETA_VERMELLA = "TARGETA_VERMELLA"
 +    partit = models.ForeignKey(Partit,on_delete=models.CASCADE)
 +    temps = models.TimeField()
 +    tipus = models.CharField(max_length=30,choices=EventType.choices)
 +    jugador = models.ForeignKey(Jugadora,null=True,
 +                    on_delete=models.SET_NULL,
 +                    related_name="events_fets")
 +    equip = models.ForeignKey(Equip,null=True,
 +                    on_delete=models.SET_NULL)
 +    # per les faltes
 +    jugador2 = models.ForeignKey(Jugadora,null=True,blank=True,
 +                    on_delete=models.SET_NULL,
 +                    related_name="events_rebuts")
 +    detalls = models.TextField(null=True,blank=True)
 +    def __str__(self):
 +        return f'{self.EventType} - {self.jugador}'
 +</file>
 +
 +<--
 +
 +Resultat final, quan arrenquem la aplicació:
 +
 +Si heu canviat el model, primer executeu:
 +
 +<code>
 +(env) nomusuari@linux:~/lligafemprj$ python manage.py makemigrations lliga
 +(env) nomusuari@linux:~/lligafemprj$ python manage.py migrate
 +</code>
 +
 +Per omplir les dades de l'script seeder:
 +
 +<code>
 +(env) nomusuari@linux:~/lligafemprj$ ./manage.py crea_lliga <nom_lliga>
 +(env) nomusuari@linux:~/lligafemprj$ ./manage.py runserver
 +(env) nomusuari@linux:~/lligafemprj$ firefox http://127.0.0.1:8080/admin
 +</code>
 +
 +Si anem a veure la taula de partits podem comprovar que s'han generat correctament, juntament amb els gols 
 +dels locals i visitants.
 +
 +{{:panell_admin_opendata.jpg?600|}}
  
 ======= Conclusions ======= ======= Conclusions =======
opendata_pandas.1688511210.txt.gz · Darrera modificació: 2023/07/04 22:53 per miquel_angel_amoros