Python sqlalchemy查询格式为成对(步骤,下一步)时间相邻样本的timeseries数据

Python sqlalchemy查询格式为成对(步骤,下一步)时间相邻样本的timeseries数据,python,sql,sqlalchemy,time-series,Python,Sql,Sqlalchemy,Time Series,我有一些时间序列数据,其中我有一组时间序列,每个Timeseries实例都有一对多的关系 使用点实例。下面是数据的简化表示 表格。py: class Timeseries(Base): __tablename__ = "timeseries" id = Column("id", Integer, primary_key=True) points = relationship("Point", back_populates="ts") class Point(Base

我有一些时间序列数据,其中我有一组时间序列,每个
Timeseries
实例都有一对多的关系 使用
实例。下面是数据的简化表示

表格。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\u id”、“id”、“t”、“v”、“id\u next”、“t\u next”、“v\u next”。也就是说,我希望能够按时间顺序查看时间序列中每个点的数据和下一个点的数据,但我一直在努力获得一个不包含隐式连接元素的表?(编辑:重要的一点是,我希望能够使用sqlalchemy中的100%查询和子查询对象获取此列表,因为我需要在进一步的联接、筛选器等中使用此查询表。)以下是我得到的基本开始,(请注意,我没有运行此代码,因为这是我实际数据库的简化版本,但想法相同):

假设我到目前为止所做的是有用的,这就是我遇到的问题:

#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)
)
我甚至不确定最后一个是否会在此时编译,因为它同样是根据实际代码改编/简化的,但它不会在时间上生成相邻点的表,等等

编辑(2019年8月10日)

以下来自Nathan的简化查询无疑是接近工作状态的正确方法,但会给sqlite带来错误

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

print(sq.all())

与其通过跳转获得查询以生成您要查找的成对结果,为什么不检索与特定
时间序列
行相关的所有
数据,然后将数据重新组合到您要查找的成对结果中呢?例如:

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)

假设您可以使用足够新版本的sqlite3 python模块(例如,通过使用Anaconda),您可以使用
LEAD
窗口函数来实现您的目标。为了在进一步的查询中使用
LEAD
函数的结果,您还需要使用CTE。以下方法适用于您提供的模式:

sq=session.query(
Timeseries.id.label(“Timeseries\u id”),
点id标签(“点id”),
点t标签(“点t”),
点v标签(“点v”),
func.lead(Point.id).over().label('Point\u after\u id'),
func.lead(Point.v).over().label('Point_after_v'),
函数导程(Point.t).over().label('Point\u after\u')。从中选择(
join(Timeseries,Point,Timeseries.id==Point.ts_id)).order_by(Timeseries.id)
后加_=sq.cte()
session.execute(在.select()之后加上_)。其中(
在.c.point\u v之后加上“<在.c.point\u之后加上“).fetchall()

所以我想这确实会得到一个成对的列表,但我只想使用子/查询对象。我可以很容易地手动构造一对点,但我想在进一步的连接和过滤器等中直接使用查询/表(还有一个小细节:timeseries实例按
t
(“time”)排序属性,而不是id,这可能会出问题。不过我并不太担心。)@user27886-很公平。如果我以后有更多的时间,我会看一看仅查询的方法。编辑排序以使用
t
值,这样就不会混淆其他任何人。底层数据库支持窗口功能吗?听起来这正是您需要的。我使用的是sqlite,它看起来确实如此当我有机会的时候,我会看看这个:啊,不幸的是,在3.25版本中,sqlite只添加了窗口函数,而默认python发行版中可用的版本是3.13。但是,如果你使用Anaconda,你很幸运,它的版本是3.26。或者如果你使用windows,可能可以访问它-请参阅关于它的第三条评论问题:。这是一个有趣的方法。我不熟悉cte,但今晚我会尝试。我在linux上,所以祈祷好运。我还没有真正检查出这个答案是否有效,但享受赏金lol。如果你遇到麻烦,请随时跟进:)嗨,内森,我有一个错误,出于某种原因,SQLITE似乎不喜欢空的
over()
表达式。我用一个难以置信的简单查询更新了这篇文章,但失败了。我的原始查询和你发布的简化查询都适用于我——在anaconda中。正如我在文章中提到的,默认情况下内置在python中的sqlite3版本不支持这种查询。恐怕您需要使用anaconda或构建自己的python解释器。
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)