Python 如何计算SQLAlchemy中一组的百分比?
我正在用Python构建一个“测验应用程序”,我需要将结果存储在SQL数据库中。我想使用SQLAlchemy Python库与数据库交互。 我的应用程序的每个用户都将被问到从预定的100个可能的问题中随机选择的3个问题。每个问题只能回答“是”或“否”(即Python 如何计算SQLAlchemy中一组的百分比?,python,sql,sqlalchemy,Python,Sql,Sqlalchemy,我正在用Python构建一个“测验应用程序”,我需要将结果存储在SQL数据库中。我想使用SQLAlchemy Python库与数据库交互。 我的应用程序的每个用户都将被问到从预定的100个可能的问题中随机选择的3个问题。每个问题只能回答“是”或“否”(即True或False)。 我将答案存储在定义如下的表格中: 课堂答案(基本答案): __tablename_u=“答案” id=列(整数,主键=True) user_id=Column(整数,ForeignKey(“Users.id”),null
True
或False
)。
我将答案存储在定义如下的表格中:
课堂答案(基本答案):
__tablename_u=“答案”
id=列(整数,主键=True)
user_id=Column(整数,ForeignKey(“Users.id”),nullable=False)
问题\u id=列(整数)
答案=列(布尔值,可空=假)
用户=关系(“用户”,back_populates=“answers”)
在所有用户完成测验后,我计算用户回答某个问题的次数:
tot\u每个问题=(db\u会话
.query(答案.question_id,
计数问题。标签(“问题的总答案”)
.分组人(回答问题id)
)
我还可以计算用户回答某个问题“是”(即True
)的次数:
tot\u true\u用于问题=(db\u会话
.query(答案.question_id,
计数问题。标签(“tot\u true\u表示问题”))
.filter(Answer.Answer==True)
.分组人(回答问题id)
)
如何使用SQLAlchemy计算每个问题被用户回答为“是”的百分比?
我可以使用基本Python字典轻松做到这一点:
dict_tot_each_question={row.question_id:row.tot_回答问题
对于tot_中的行,每个_问题.all()}
dict_tot_true_for_question={row.question_id:row.tot_true_for_question
for row in tot_true_for_QUOTE.all()}
dict_percent_true_for_question={}
对于问题id,tot_在dict_tot_中回答每个问题。items():
tot_true=dict_tot_true_for_question.get(question_id,0)
正确百分比=正确总数/答案总数*100
dict_percent_true_for_疑问[question_id]=percent_true
但我更喜欢使用SQLAlchemy功能来获得相同的结果。在炼金术中有可能做到这一点吗?在SQLAlchemy中这样做是否方便高效,或者我基于Python dictionary的解决方案出于任何原因是否会更好?只需将两个查询中的两个表达式组合成一个表达式,即可得到所需的结果:
q = (
session.query(
Question.id,
(100 * func.sum(cast(Answer.answer, Integer)) / func.count(Answer.answer)).label("perc_true"),
)
.outerjoin(Answer)
.group_by(Question.id)
)
正如您在上面看到的,我对所有答案都使用了COUNT
函数
另一项需要注意的是,我的查询从问题开始,然后连接答案表。这样做的原因是,如果有没有答案的问题
,如果只使用答案
表,您仍然会看到返回的(#id,NULL)
,而不会看到任何行。但如果你不在乎我所看到的这个角落案件的处理方式,你可以按照自己的方式处理:
q = (
session.query(
Answer.question_id,
(100 * func.sum(Answer.answer) / func.count(Answer.answer)).label("perc_true"),
)
.group_by(Answer.question_id)
)
最后,我做的另一个假设是,在强制转换为整数
后,您的数据库将把true
作为1
进行处理,以获得适当的SUM
。如果情况并非如此,请参阅本问题中关于如何处理此问题的多个答案:
奖金:
当我发现自己在模型级别上询问一些与聚合相关的问题时,我通常直接使用扩展在模型上实现这些问题
下面的代码将为您提供有关如何将其用于您的案例的信息和指示:
class Answer(Base):
__tablename__ = "answers"
id = Column(Integer, primary_key=True)
# user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
question_id = Column(Integer, ForeignKey("questions.id"))
answer = Column(Boolean, nullable=False)
# user = relationship("User", back_populates="answers")
question = relationship("Question", back_populates="answers")
class Question(Base):
__tablename__ = "questions"
id = Column(Integer, primary_key=True)
question = Column(String, nullable=False)
answers = relationship("Answer", back_populates="question")
@hybrid_property
def answers_cnt(self):
return len(list(self.answers))
@hybrid_property
def answers_yes(self):
return len(list(_ for _ in self.answers if _.answer))
@hybrid_property
def answers_yes_percentage(self):
return (
100.0 * self.answers_yes / self.answers_cnt if self.answers_cnt != 0 else None
)
@answers_cnt.expression
def answers_cnt(cls):
return (
select(func.count(Answer.id))
.where(Answer.question_id == cls.id)
.label("answers_cnt")
)
@answers_yes.expression
def answers_yes(cls):
return (
select(func.count(Answer.id))
.where(Answer.question_id == cls.id)
.where(Answer.answer == True)
.label("answers_yes")
)
@answers_yes_percentage.expression
def answers_yes_percentage(cls):
return (
case(
[(cls.answers_cnt == 0, None)],
else_=(
100
* cast(cls.answers_yes, Numeric)
/ cast(cls.answers_cnt, Numeric)
),
)
).label("answers_yes_percentage")
在本例中,您可以使用python或使用查询进行计算
Python(这将从数据库加载所有答案,因此如果数据尚未加载到内存中,则效率低下)
数据库:这是非常有效的,因为您只需运行一个查询,类似于您正在查找的答案中的单独查询,但结果将作为模型上的属性单独返回
q = session.query(Question, Question.answers_yes_percentage)
for question, percentage in q:
print(question, percentage)
请注意,上面的内容适用于1.4版本的sqlalchemy,但在以前的版本中可能需要其他语法。谢谢@van,它可以完美地工作!我真的很欣赏这些解释和两种选择,以实现预期的结果。请注意:您的方法执行“整数除法”:数字的小数部分被丢弃,数字总是向下舍入。是否有办法执行“浮点除法”,获得与使用Python/
运算符获得的数字相同的数字?请尝试将100
替换为100.1
。如果这还不够,我将使用CAST
操作符更改答案。是的,它工作得很好!使用100.0001
可以忽略不计地更改正确的数字。出于好奇,我还尝试了cast
函数:从sqlalchemy导入cast,Float
。然后,将func.sum(Answer.Answer)
替换为cast(func.sum(Answer.Answer),Float)
返回使用基本Python获得的完全相同的浮点数!不客气。我将用您可能会发现有用的另一种用法来修改答案,请使用sqlalchemy文档进一步探索。
q = session.query(Question, Question.answers_yes_percentage)
for question, percentage in q:
print(question, percentage)