средние расходы в день - модель Джанго - PullRequest
0 голосов
/ 05 марта 2019

У меня есть модель, которая выглядит примерно так:

class Payment(TimeStampModel):
    timestamp = models.DateTimeField(auto_now_add=True)
    amount = models.FloatField()
    creator = models.ForeignKey(to='Payer')

Как правильно рассчитать средние расходы в день? Я могу агрегировать по дням, но тогда дни, когда плательщик ничего не тратит, не будут учитываться, что неправильно

UPDATE:

Итак, допустим, у меня есть только две записи в моей базе данных: одна с 1 марта и одна с 1 января. Средние расходы в день должны составлять что-то

(Sum of all spendings) / (March 1 - January 1) 

, который делится на 60

однако это, конечно, даст мне только средние расходы на единицу, а количество дней даст мне 2:

for p in Payment.objects.all():
    print(p.timestamp, p.amount)
p = Payment.objects.all().dates('timestamp','day').aggregate(Sum('amount'), Avg('amount'))
print(p

Выход:

2019-03-05 17:33:06.490560+00:00 456.0
2019-01-05 17:33:06.476395+00:00 123.0
{'amount__sum': 579.0, 'amount__avg': 289.5}

Ответы [ 2 ]

1 голос
/ 05 марта 2019

Это зависит от вашего бэкэнда, но вы хотите разделить сумму суммы на разницу в днях между вашей максимальной и минимальной отметкой времени. В Postgres вы можете просто вычесть две даты, чтобы получить количество дней между ними. В MySQL есть функция DateDiff, которая принимает две даты и возвращает количество дней между ними.

class Date(Func):
    function = 'DATE'

class MySQLDateDiff(Func):
    function = 'DATEDIFF'

    def __init__(self, *expressions, **extra):
        expressions = [Date(exp) for exp in expressions]
        extra['output_field'] = extra.get('output_field', IntegerField())
        super().__init__(*expressions, **extra)

class PgDateDiff(Func):
    template = "%(expressions)s"
    arg_joiner = ' - '

    def __init__(self, *expressions, **extra):
        expressions = [Date(exp) for exp in expressions]
        extra['output_field'] = extra.get('output_field', IntegerField())
        super().__init__(*expressions, **extra)

agg = {
    avg_spend: ExpressionWrapper(
        Sum('amount') / (PgDateDiff(Max('timestamp'), Min('timestamp')) + Value(1)),
        output_field=DecimalField())
}
avg_spend = Payment.objects.aggregate(**agg)

Это выглядит примерно правильно для меня, конечно, я не проверял это. Конечно, используйте MySQLDateDiff, если это ваш бэкэнд.

1 голос
/ 05 марта 2019

Вы можете агрегировать минимальную и максимальную временную метку и сумму суммы:

from django.db.models import Min, Max, Sum

def average_spending_per_day():
    aggregate = Payment.objects.aggregate(Min('timestamp'), Max('timestamp'), Sum('amount'))
    min_datetime = aggregate.get('timestamp__min')
    if min_datetime is not None:
        min_date = min_datetime.date()
        max_date = aggregate.get('timestamp__max').date()
        total_amount = aggregate.get('amount__sum')
        days = (max_date - min_date).days + 1
        return total_amount / days
    return 0

Если есть min_datetime, то в таблице БД есть некоторые данные, а также есть максимальная дата и сумма.сумма, в противном случае мы возвращаем 0 или что вы хотите.

...