Агрегирование и фильтрация в Джанго

У меня есть эта таблица:

**ID    val1     val2**
1      5         6
2      6         4
3      3         1
4      8         4
5      2         6
6      8         2

Используя фильтр запросов в Django, я хочу обобщить эти данные, чтобы получить средние значения val1 и val2 для каждой n записей. Например, если n=3, тогда:

**ID     val1     val2**
1      4.7      3.7
2      6.0      4.0

4 ответа

Вы должны сначала определить свои модели, затем вы можете использовать Count, Min, Sum, Avg от django.db.models это позволит вам сделать:

Table.objects.aggregate(average_val1=Avg('table__val1'))

Словарь результатов будет иметь ключ под названием "Average_val1". Если такой псевдоним не указан, это будет довольно длинный "table__val1__avg".

Дополнительную документацию можно найти здесь: Документация Django - Агрегация

NB. Вы можете фильтровать, а затем использовать агрегацию для выполнения операций с определенным набором. Яйцо:

Table.objects.filter( pk__in=id_list ).aggregate(average_val1=Avg('table__val1'))

или вы можете каждый раз определять предельные идентификаторы [id1, id2] для каждого n, а затем делать это:

Table.objects.filter( pk__lte=n1, pk__gte=n2).aggregate(average_val1=Avg('table__val1'))

__in, __lte, __gte обязательно отфильтрует только тот набор идентификаторов, который вы хотите, затем вы можете агрегировать по этому набору.

__in: в списке, __lte: Меньше или равно и __gte: Больше или равно.

ind = 0
v1ofn = 0
v2ofn = 0
for row in tname.objects.all():
    if ind >= n:
        result_list.append([v1ofn/3, v2ofn/3])
        v1ofn = row.val1
        v2ofn = row.val2
        ind = 0
    else:
        v1ofn = v1ofn + row.val1
        v2ofn = v2ofn + row.val2
    ind = ind + 1

предполагая, что таблица имеет несколько из 3 элементов, если нет, выполните дополнительную логику после цикла, чтобы обработать дополнительные элементы.

Я предполагаю, что агрегация - это способ Django сделать это, но предложенный пример может привести к деструкции запросов, как сказал Бехести. И я предполагаю, что django ORM действительно не создан для обработки чисел (но я могу ошибаться здесь!)

Я мог бы пойти с NumPy (если у вас действительно огромный массив, я думаю, вам нужно сделать несколько разделов):

Хорошая сторона использования numpy в том, что он обычно намного быстрее, чем "стандартные" операции с python, но плохая сторона в том, что это дополнительная зависимость.

import numpy
raw_array = [ # This is for testing the code, use .values_list( 'val1', 'val2' ) with db
[1   ,   5      ,   6],
[2   ,   6      ,   4],
[3   ,   3      ,   1],
[4   ,   8      ,   4],
[5   ,   2      ,   6],
[6   ,   8      ,   2],
[7   ,   1      ,   1],
]

arr = numpy.array( raw_array )

def sum_up_every_n_items( arr, n ):
   res = numpy.zeros( (numpy.floor( arr.shape[0]/float(n) ), arr.shape[1]) )
   arr = arr[ 0:res.shape[0]*n, : ] # Truncate, take only full N items
   for loop in range(0,n): # Note: this is loop 0,1,2 if n=3 ! We do addition with numpy vectors!
      res = res + arr[ loop::n, : ] # Get every n'th row from starting with offset
   res = res / float(n)
   return res 

res = sum_up_every_n_items( arr, n=3 )
print res

выходы

[[ 2.          4.66666667  3.66666667]
 [ 5.          6.          4.        ]]

Избегайте делать много запросов. Вытащите данные один раз и сделайте все остальное в Python;

n = 3 #yourstep
results = Model.objects.filter(query).only('id', 'val1', 'val2')
avgs = {}
for i in xrange(0, len(results), n): # unsure, but count may end up in 2 queries, and you're going to evaluate the queryset anyway
    avg1, avg2 = 0, 0
    for j in xrange(n):
        avg1 += results[i+j].val1/float(n)
        avg2 += results[i+j].val2/float(n)
    avgs[results[i].id] = (avg1, avg2) # Why is id relevant at all here?
Другие вопросы по тегам