问题描述
请问各位大佬比如下面这段代码,怎么在query中用新生成的'avg_pe'进行排序?
计算pe等指标时 ,需要排除掉为负的
q = query(valuation.day,
(func.count()/func.sum(1/valuation.pe_ratio)).label('avg_pe'), # 等权重pe
).filter(
valuation.pe_ratio>0, #排除亏损的
解决方案
在 JoinQuant (聚宽) 平台中,底层使用的是 SQLAlchemy 来构建数据库查询。当您使用 func 进行聚合计算并使用 .label() 命名后,想要对这个新生成的字段进行排序,通常有两种最直接有效的方法。
方法一:直接在 order_by 中重复聚合表达式(推荐)
这是最稳妥且兼容性最好的方法。直接将计算 avg_pe 的表达式放入 .order_by() 中,并调用 .desc()(降序)或 .asc()(升序)。
from jqdata import *
from sqlalchemy.sql import func
# 构建查询
q = query(
valuation.day,
(func.count() / func.sum(1 / valuation.pe_ratio)).label('avg_pe')
).filter(
valuation.pe_ratio > 0
).group_by(
valuation.day
).order_by(
# 直接使用相同的表达式进行排序,这里以倒序(desc)为例
(func.count() / func.sum(1 / valuation.pe_ratio)).desc()
).limit(10)
# 获取数据
df = get_fundamentals(q, date='2023-01-01')
print(df)
方法二:使用 sqlalchemy.text 或 sqlalchemy.desc 引用 label 名称
如果您觉得重复写表达式太长,可以导入 SQLAlchemy 的相关模块,直接通过字符串引用您定义的 label 名称。
from jqdata import *
from sqlalchemy.sql import func
from sqlalchemy import desc, text
# 构建查询
q = query(
valuation.day,
(func.count() / func.sum(1 / valuation.pe_ratio)).label('avg_pe')
).filter(
valuation.pe_ratio > 0
).group_by(
valuation.day
).order_by(
# 使用 text 引用 label 名称进行排序
text('avg_pe DESC')
# 或者使用 desc(text('avg_pe'))
).limit(10)
# 获取数据
df = get_fundamentals(q, date='2023-01-01')
print(df)
注意事项:
group_by是必须的:当您使用了聚合函数(如func.count(),func.sum())并且查询中还包含非聚合字段(如valuation.day)时,必须加上.group_by(valuation.day),否则 SQL 语法会报错或返回非预期结果。- 除零错误:虽然您使用了
valuation.pe_ratio > 0过滤了负数和零,但在实际应用中,如果某一天没有任何符合条件的股票,func.sum可能返回 NULL,导致除法报错。在 JoinQuant 的get_fundamentals中通常会自动处理,但需留意数据清洗。 - 性能考虑:在
get_fundamentals中进行复杂的聚合计算可能会比较慢,如果查询超时,建议先取出基础数据(如pe_ratio),然后使用 Pandas 在内存中进行groupby和聚合计算。