Агрегирование и фильтрация в Джанго
У меня есть эта таблица:
**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?