Python 如何计算SQLAlchemy中一组的百分比?

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

我正在用Python构建一个“测验应用程序”,我需要将结果存储在SQL数据库中。我想使用SQLAlchemy Python库与数据库交互。 我的应用程序的每个用户都将被问到从预定的100个可能的问题中随机选择的3个问题。每个问题只能回答“是”或“否”(即
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)