Визуализация статистики данных Mongodb с помощью matplotlib

1

Я хочу получить визуализированную статистику из моих данных в mongodb, используя matplotlib, но способ, которым я сейчас пользуюсь, действительно странный.

Я запросил mongodb 30 раз для получения ежедневных данных, которые уже медленны и грязны, особенно когда я получаю результат откуда-то еще, а не на сервере. Интересно, есть ли лучший/чистый способ получать почасовые, изо дня в день, по месяцам и по годам статистику?

Вот код, который я использую сейчас (получаю статистику по дням):

from datetime import datetime, date, time, timedelta
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
from my_conn import my_mongodb

t1 = []
t2 = []
today = datetime.combine(date.today(), time())
with my_mongodb() as m:
    for i in range(30):
        day = today - timedelta(days = i)
        t1 = [m.data.find({"time": {"$gte": day, "$lt": day + timedelta(days = 1)}}).count()] + t1
        t2 = [m.data.find({"deleted": 0, "time": {"$gte": day, "$lt": day + timedelta(days = 1)}}).count()] + t2

x = range(30)
N = len(x)

def format_date(x, pos=None):
    day = today - timedelta(days = (N - x - 1))
    return day.strftime('%m/%d')

plt.bar(range(len(t1)), t1, align='center', color="#4788d2") #All
plt.bar(range(len(t2)), t2, align='center', color="#0c3688") #Not-deleted

plt.xticks(range(len(x)), [format_date(i) for i in x], size='small', rotation=30)
plt.grid(axis = "y")

plt.show()
  • 0
    это виртуализированная статистика или визуализированная статистика?
  • 0
    визуализируемый, извините за мой плохой английский :-P
Показать ещё 6 комментариев
Теги:
matplotlib

2 ответа

0
Лучший ответ

Благодаря @Blubber, я нашел способ, который лучше справляется с этой целью, используя Map/Reduce.

Часть выборки данных была переписана на:

from dateutil import parser
parse_time = lambda s: parser.parse(s, ignoretz = True)

func_map = """
function() {
    if (this.hasOwnProperty("time"))
        emit(this.time.getUTCFullYear() + "/" + (this.time.getUTCMonth() + 1) + "/" + this.time.getUTCDate(),
        {
            count: 1,
            not_deleted: (1 - this.deleted)
        });
}
"""

func_reduce = """
function(key, values) {
    var result = {count: 0, not_deleted: 0};

    values.forEach(function(value) {
        result.count += value.count;
        result.not_deleted += value.not_deleted;
    });

    return result;
}
"""

with my_mongo() as m:
    result = m.data.inline_map_reduce(func_map, func_reduce)
    dataset = {parse_time(day['_id']): day['value']['not_deleted'] for day in result}
    dataset2 = {parse_time(day['_id']): day['value']['count'] for day in result}

Поскольку я новичок в JS, лучше написать JS-функции :)

2

ОБНОВИТЬ:

Я принципиально неправильно понял проблему. Феликс просил mongoDB выяснить, сколько предметов попало в каждый диапазон; поэтому мой подход не сработал, потому что я пытался спросить mongoDB для предметов. У Феликса много данных, поэтому это совершенно необоснованно.

Феликс, здесь обновленная функция, которая должна делать то, что вы хотите:

def getDataFromLast(num, quantum):
    m = my_mongodb()
    all = []
    not_deleted = []
    today = datetime.combine(date.today(), time())
    for i in range(num+1)[-1]: # start from oldest
        day = today - i*quantum
        time_query = {"$gte":day, "$lt": day+quantum}
        all.extend(m.data.find({"time":time_query}).count())
        not_deleted.extend(m.data.find({"deleted":0, "time":time_query}).count())
    return all, not_deleted

Квант - это "шаг", чтобы оглянуться назад. Например, если бы мы хотели посмотреть последние 12 часов, я бы установил quantum = timedelta(hours=1) и num = 12. Обновленное использование примера, в котором мы получаем последние 30 дней, будет:

from datetime import datetime, date, time, timedelta
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
from my_conn import my_mongodb

#def getDataFromLast(num, quantum) as defined above

def format_date(x, N, pos=None):
    """ This is your format_date function. It now takes N
        (I still don't really understand what it is, though)
        as an argument instead of assuming that it a global."""
    day = date.today() - timedelta(days=N-x-1)
    return day.strftime('%m%d')

def plotBar(data, color):
    plt.bar(range(len(data)), data, align='center', color=color)


N = 30 # define the range that we want to look at

all, valid = getDataFromLast(N, timedelta(days=1)) # get the data

plotBar(all, "#4788d2") # plot both deleted and non-deleted data
plotBar(valid, "#0c3688") # plot only the valid data

plt.xticks(range(N), [format_date(i) for i in range(N)], size='small', rotation=30)
plt.grid(axis="y")
plt.show()  

Оригинал:

Хорошо, это моя попытка рефакторинга для вас. Blubber предложил изучить JS и MapReduce. Там нет необходимости, если вы следуете его другим предложениям: создайте индекс в поле времени и уменьшите количество запросов. Это моя лучшая попытка, наряду с небольшим рефакторингом. У меня есть куча вопросов и комментариев.

Начиная с:

with my_mongodb() as m:
    for i in range(30):
        day = today - timedelta(days = i)
        t1 = [m.data.find({"time": {"$gte": day, "$lt": day + timedelta(days = 1)}}).count()] + t1
        t2 = [m.data.find({"deleted": 0, "time": {"$gte": day, "$lt": day + timedelta(days = 1)}}).count()] + t2

Вы делаете запрос mongoDB для поиска всех данных за каждый день за последние 30 дней. Почему бы вам просто не использовать один запрос? И как только у вас есть все данные, почему бы просто не отфильтровать удаленные данные?

with my_mongodb() as m:
    today = date.today() # not sure why you were combining this with time(). It the datetime representation of the current time.time()

    start_date = today -timedelta(days=30)
    t1 = m.find({"time": {"$gte":start_date}}) # all data since start_date (30 days ago)
    t2 = filter(lambda x: x['deleted'] == 0, all_data) # all data since start_date that isn't deleted

Я действительно не знаю, почему вы делали 60 запросов (30 * 2, один для всех данных, один для не удаленных). Есть ли какая-то конкретная причина, по которой вы ежедневно создавали данные?

Затем у вас есть:

x = range(30)
N = len(x)

Почему нет:

N = 30
x = range(N)

len(range(x) равен x, но занимает время, чтобы вычислить. То, как вы написали его изначально, просто немного... странно.

Здесь моя трещина в этом, с изменениями, которые я предложил сделать таким образом, насколько это возможно вообще.

from datetime import datetime, date, time, timedelta
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
from my_conn import my_mongodb

def getDataFromLast(delta):
    """ Delta is a timedelta for however long ago you want to look
        back. For instance, to find everything within the last month,
        delta should = timedelta(days=30). Last hour? timedelta(hours=1)."""
    m = my_mongodb() # what exactly is this? hopefully I'm using it correctly.
    today = date.today() # was there a reason you didn't use this originally?
    start_date = today - delta
    all_data = m.data.find({"time": {"$gte": start_date}})
    valid_data = filter(lambda x: x['deleted'] == 0, all) # all data that isn't deleted
    return all_data, valid_data

def format_date(x, N, pos=None):
    """ This is your format_date function. It now takes N
        (I still don't really understand what it is, though)
        as an argument instead of assuming that it a global."""
    day = date.today() - timedelta(days=N-x-1)
    return day.strftime('%m%d')

def plotBar(data, color):
    plt.bar(range(len(data)), data, align='center', color=color)

N = 30 # define the range that we want to look at
all, valid = getDataFromLast(timedelta(days=N))
plotBar(all, "#4788d2") # plot both deleted and non-deleted data
plotBar(valid, "#0c3688") # plot only the valid data

plt.xticks(range(N), [format_date(i) for i in range(N)], size='small', rotation=30)
plt.grid(axis="y")
plt.show()  
  • 0
    Ааа, ОГРОМНОЕ СПАСИБО в первую очередь. Что касается datetime.combine , я использовал этот метод для извлечения данных из MySQL, поскольку date.today () делает вещи странными, так что в любом случае это плохая практика, когда используется pymongo. Но набор данных может быть слишком большим, поэтому извлечение их всех, а затем разделение с использованием python не является хорошей идеей, так как передача с сервера на меня может занять много времени, а затем снова много времени для итерации python, как я упоминал в одном из комментариев в вопросе.
  • 0
    О, date.today() странный, потому что это местная дата? Извините, я этого не понял! Тем не менее, не могли бы вы попробовать использовать datetime.utcnow() ?
Показать ещё 6 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню