Django уменьшает количество возвращаемых строк для графика
У меня есть модель с timestamp=DateTimeField() и некоторыми дополнительными числовыми данными. В таблице я храню данные с датчика, собранные за каждую минуту.
У меня есть представление, возвращающее график с данными из упомянутой таблицы, которая параметризуется по дате начала_даты и дате конца_даты. График отлично работает для данных с периодом не более нескольких дней, но все "взрывается", когда период больше (много записей).
Я не хочу изобретать колесо. Я хочу вернуть только n записей представителей за период времени (например, с avg значением). Я думаю, что моя проблема является общей, но я не могу найти готовое решение для нее в гугле.
Это распространенная проблема, но основная причина отсутствия широко распространенного пакета для ее решения заключается в том, что нелегко (возможно, на данный момент невозможно) создать общее решение, подходящее для общих баз данных django.
Есть несколько способов сделать это:
1. PostgreSQL: Материализованные представления
(a) Создайте материализованное представление.
(b) Создайте неуправляемую модель django:
class TimestampChart(models.Model):
timestamp = models.DateTime()
class Meta:
managed = False
# This name has to match the name of the materialized view
db_table = 'timestamp_materialized_view'
(c) Используйте ORM для запроса к представлению. Не забудьте также обновлять представление (либо периодически с помощью cron
или вашего любимого планировщика задач, либо по требованию с помощью необработанного SQL), поскольку PSQL не делает ничего из этого автоматически.
Возможно, также обратите внимание на django-materialized-views. Я не тестировал эту библиотеку, но описание кажется многообещающим.
В других реализациях SQL можно разработать подходы, которые будут выглядеть аналогично. В MySQL, SQLite и т. д. вы можете создавать и поддерживать искусственные материализованные представления, но делать это придется вручную, по сути, используя обычную таблицу и разрабатывая всю операционную логику с нуля.
2. Выгрузка выбранных данных в специализированную базу данных со встроенным инструментарием для данной категории задач
Такая функция, которую вы ищете, - это что-то вроде политики хранения в InfluxDB или управления жизненным циклом индекса в ElasticSearch.
Хотя эти типы баз данных довольно мощны для решения подобных задач, их недостатком является то, что, насколько известно, нет ни официальной поддержки Django, ни широко распространенного & поддерживаемого стороннего пакета для любой из них, так что вам, возможно, придется выполнять основную часть работ самостоятельно. Хорошей новостью является то, что и InfluxDB, и ElasticSearch имеют python-клиенты, поэтому создание ORM-подобных оберток для любого из них, которые вы можете использовать в своем Django-приложении, должно быть вполне осуществимо.
Есть несколько неофициальных пакетов, которые могут оказать дополнительную помощь или вдохновить, например django-elasticsearch-dsl-drf и elasticsearch-django.
3. Для небольших объемов данных предварительно обрабатывайте их в памяти
Если ваши данные умещаются в памяти, вы можете реализовать любую стратегию, которая вам нужна, будь то даунсэмплинг, агрегирование, подавление шума или что-то еще. Если только ваш алгоритм отбора не окажется сверхсложным, то, скорее всего, хватит и Python-носителей.
# Downsample by slicing out only every n-th datapoint
n = 5
res = TimestampedData.objects.order_by('timestamp')[::n]
# Aggregate by keeping only the average of every datapoint-pair
res_list = list(TimestampedData.objects.order_by('timestamp').values_list('timestamp', flat=True)
res = []
for i in range(0, len(res_list) - 1, 2):
avg = (res_list[i] + res_list[i + 1]) / 2
res.append(avg)
Недостатком здесь является то, что вам придется запускать эту логику каждый раз, когда запрашиваются данные.
Если вы храните эти вычисленные данные и затем обновляете их время от времени, вы, по сути, вернулись к созданию собственного ненастоящего материализованного представления, поэтому вам следует проверить, можно ли реализовать ваш алгоритм на уровне базы данных, чтобы получить огромный выигрыш в эффективности и масштабируемости.
Предполагаю, что вы используете для этого базу данных временных рядов, например InfluxDB. Вы можете легко уменьшить количество возвращаемых строк, написав запрос по следующей схеме с использованием функций агрегирования, например
SELECT mean("field") FROM "measurement"
WHERE time >= $start AND time <= $end
GROUP BY time(1h)