8 votes

Regroupement par semaine et compensation des semaines "manquantes".

Dans mon modèle Django, j'ai un modèle très simple qui représente une seule occurrence d'un événement (comme l'apparition d'une alerte serveur) :

class EventOccurrence:
    event = models.ForeignKey(Event)
    time = models.DateTimeField()

Mon objectif final est de produire un tableau ou un graphique qui montre combien de fois un événement s'est produit au cours des dernières années. n semaines.

Ma question comporte donc deux parties :

  • Comment puis-je group_by la semaine du time champ ?
  • Comment puis-je "rembourrer" le résultat de ce group_by d'ajouter une valeur nulle pour les semaines manquantes ?

Par exemple, pour la deuxième partie, j'aimerais transformer un résultat comme celui-ci :

| week | count |                   | week | count |
| 2    | 3     |                   | 2    | 3     |
| 3    | 5     |   —— becomes —>   | 3    | 5     |
| 5    | 1     |                   | 4    | 0     |
                                   | 5    | 1     |

Quelle est la meilleure façon de faire cela dans Django ? Les solutions générales en Python sont également acceptables.

6voto

Secator Points 9827

Le programme de Django DateField ainsi que datetime ne supporte pas l'attribut semaine. Pour tout récupérer en une seule requête, vous devez faire ce qui suit :

from django.db import connection

cursor = connection.cursor()
cursor.execute(" SELECT WEEK(`time`) AS 'week', COUNT(*) AS 'count' FROM %s GROUP BY WEEK(`time`) ORDER BY WEEK(`time`)" % EventOccurrence._meta.db_table, [])

data = []
results = cursor.fetchall()
for i, row in enumerate(results[:-1]):
    data.append(row)

    week = row[0] + 1
    next_week = results[i+1][0]
    while week < next_week:
        data.append( (week, 0) )
        week += 1
data.append( results[-1] )

print data

0voto

danihp Points 15682

Après avoir fouillé dans la documentation de l'API de requête de Django, je n'ai pas trouvé de moyen de faire des requêtes à travers le système ORM de Django. Curseur est une solution de contournement, si votre marque de base de données est MySQL :

from django.db import connection, transaction
cursor = connection.cursor()

cursor.execute("""
   select 
      week(time) as `week`, 
      count(*) as `count` 
   from EventOccurrence 
   group by week(time)
   order by 1;""")

myData = dictfetchall(cursor)

C'est, à mon avis, la solution la plus performante. Mais notez que cela ne compense pas les semaines manquantes.

EDITED Solution de marque de base de données indépendante via python (moins de performance)

Si vous cherchez un code indépendant de la marque de la base de données, vous devriez prendre les dates jour par jour et les agréger via python. Si c'est votre cas, le code peut ressembler à ceci :

#get all weeks:
import datetime
weeks = set()
d7 = datetime.timedelta( days = 7)
iterDay = datetime.date(2012,1,1)
while iterDay <= datetime.now():
    weeks.add( iterDay.isocalendar()[1] )
    iterDay += d7

#get all events
allEvents = EventOccurrence.objects.value_list( 'time', flat=True )

#aggregate event by week
result = dict()
for w in weeks:
    result.setdefault( w ,0)

for e in allEvents:
    result[ e.isocalendar()[1] ] += 1

(Disclaimer : non testé)

0voto

Hauzer Lee Points 81

Comme je dois interroger plusieurs tables en les joignant, j'utilise la vue de la base de données pour répondre à ces exigences.

CREATE VIEW my_view
  AS
  SELECT
    *, // <-- other fields goes here
    YEAR(time_field) as year,
    WEEK(time_field) as week
  FROM my_table;

et le modèle comme :

from django.db import models

class MyView(models.Model):
    # other fields goes here
    year = models.IntegerField()
    week = models.IntegerField()

    class Meta:
        managed = False
        db_table = 'my_view'

    def query():
        rows = MyView.objects.filter(week__range=[2, 5])
        # to handle the rows

après avoir obtenu des lignes de cette vue de la base de données, utilisez la méthode de @danihp pour ajouter 0 pour les semaines/mois "vides".

NOTE : ceci n'est testé que pour le backend MySQL, je ne suis pas sûr que cela soit valable pour MS SQL Server ou autre.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X