sqlalchemy запрос данных временных рядов, отформатированных как пары (step, next_step) смежных по времени выборок - PullRequest
4 голосов
/ 19 марта 2019

У меня есть данные временных рядов, где у меня есть наборы временных рядов, каждый экземпляр Timeseries которых имеет отношение один ко многим с экземплярами Point.Ниже приведено упрощенное представление данных.

tables.py:

class Timeseries(Base):
    __tablename__ = "timeseries"

    id = Column("id", Integer, primary_key=True)
    points = relationship("Point", back_populates="ts")


class Point(Base):
    __tablename__ = "point"

    id = Column("id", Integer, primary_key=True)
    t = Column("t", Float)
    v = Column("v", Float)
    ts_id = Column(Integer, ForeignKey("timeseries.id"))
    ts = relationship("Timeseries", back_populates="points")

Вопрос : я пытаюсь найти запрос с такими столбцами: "timeseries_id "," id "," t "," v "," id_next "," t_next "," v_next ".То есть я хочу иметь возможность видеть данные каждой точки вместе с данными следующих точек во временном ряду в хронологическом порядке, но я изо всех сил пытался получить таблицу, в которой нет элементов из неявного соединения?(Изменить: Важным моментом является то, что я хочу получить этот список, используя 100% запросов и объектов подзапросов в sqlalchemy, потому что мне нужно использовать эту запрашиваемую таблицу в дальнейших объединениях, фильтрах и т. Д.)Я понял (обратите внимание, что я не запускал этот код, поскольку это упрощенная версия моей фактической базы данных, но это та же идея):

# The point data actually in the database.
sq = (session.query(
    Timeseries.id.label("timeseries_id"),
    Point.id,
    Point.t,
    Point.v)
.select_from(
    join(Timeseries, Point, Timeseries.id==Point.ts_id))
.group_by('timeseries_id')
.subquery())

# first point manually added to each list in query
sq_first = (session.query(
    Timeseries.id.label("timeseries_id"),
    sa.literal_column("-1", Integer).label("id"), # Some unused Point.id value
    sa.literal_column(-math.inf, Float).label("t"),
    sa.literal_column(-math.inf, Float).label("v"))
.select_from(
    join(Timeseries, Point, Timeseries.id==Point.ts_id))
.subquery())

# last point manually added to each list in query.
sq_last = (session.query(
    Timeseries.id.label("timeseries_id"),
    sa.literal_column("-2", Integer).label("id"), # Another unused Point.id value
    sa.literal_column(math.inf, Float).label("t"),
    sa.literal_column(math.inf, Float).label("v"))
.select_from(
    join(Timeseries, Point, Timeseries.id==Point.ts_id))
.subquery())

# Append each timeseries in `sq` table with last point
sq_points_curr = session.query(sa.union_all(sq_first, sq)).subquery()
sq_points_next = session.query(sa.union_all(sq, sq_last)).subquery()

Предположим, что то, что я сделал до сих пор, полезноЭто та часть, где я застреваю:

#I guess rename the columns in `sq_points_next` to append them by "_next"....
sq_points_next = (session.query(
    sq_points_curr.c.timeseries_id
    sq_points_curr.c.id.label("id_next"),
    sq_points_curr.c.t.label("t_next"),
    sq_points_curr.c.v.label("v_next"))
.subquery())

# ... and then perform a join along "timeseries_id" somehow to get the table I originally wanted...
sq_point_pairs = (session.query(
    Timeseries.id.label("timeseries_id")
    "id",
    "t",
    "v",
    "id_next",
    "t_next",
    "v_next"
).select_from(
    sq_points, sq_points_next, sq_points.timeseries_id==sq_points_next.timeseries_id)
)

Я даже не уверен, что этот последний компилируется в этот момент, так как он снова адаптирован / упрощен из реального кода, но не даеттаблица соседних моментов времени и т.д ..

Ответы [ 2 ]

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

Предполагая, что вы можете получить достаточно последнюю версию работающего модуля Python sqlite3 (например, с помощью Anaconda), вы можете использовать оконную функцию LEAD для достижения вашей цели. Чтобы использовать результаты функции LEAD в дальнейших запросах, вам также потребуется использовать CTE. Следующий подход работал для меня с схемой, которую вы дали:

sq = session.query(
        Timeseries.id.label("timeseries_id"),
        Point.id.label("point_id"),
        Point.t.label("point_t"),
        Point.v.label("point_v"),
        func.lead(Point.id).over().label('point_after_id'),
        func.lead(Point.v).over().label('point_after_v'),
        func.lead(Point.t).over().label('point_after_t')).select_from(
            join(Timeseries, Point, Timeseries.id == Point.ts_id)).order_by(Timeseries.id)

with_after = sq.cte()
session.execute(with_after.select().where(
        with_after.c.point_v < with_after.c.point_after_v)).fetchall()
0 голосов
/ 22 марта 2019

Вместо того, чтобы прыгать через обручи, чтобы получить запрос для получения парных результатов, которые вы ищете, почему бы просто не извлечь все данные points, относящиеся к конкретной строке Timeseries, а затем рекомбинировать данные в пары, которые вы ищете находясь в поиске? Например:

from operator import attrgetter

def to_dict(a, b):
    # formats a pair of points rows into a dict object
    return {
        'timeseries_id': a.ts_id,
        'id': a.id, 't': a.t, 'v': a.v,
        'id_next': b.id, 't_next': b.t, 'v_next': b.v
    }      

def timeseries_pairs(session, ts_id):
        # queries the db for particular Timeseries row, and combines points pairs
        ts = session.query(Timeseries).\
            filter(Timeseries.id == ts_id).\
            first()

        ts.points.sort(key=attrgetter('t'))
        pairs = [to_dict(a, b) for a, b in zip(ts.points, ts.points[1:])]
        last = ts.points[-1]
        pairs.append({
            'timeseries_id': last.ts_id,
            'id': last.id, 't': last.t, 'v': last.v,
            'id_next': None, 't_next': None, 'v_next': None
            })

        return pairs

# pass the session and a timeseries id to return a list of points pairs
timeseries_pairs(session, 1)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...