def save_marginals(session, X, marginals, training=True):
logger = logging.getLogger(__name__)<EOL>try:<EOL><INDENT>shape = marginals.shape<EOL><DEDENT>except Exception:<EOL><INDENT>marginals = np.array(marginals)<EOL>shape = marginals.shape<EOL><DEDENT>if len(shape) == <NUM_LIT:1>:<EOL><INDENT>marginals = np.vstack([<NUM_LIT:1> - marginals, marginals]).T<EOL><DEDENT>marginal_tuples = []<EOL>for i in range(shape[<NUM_LIT:0>]):<EOL><INDENT>for k in range(<NUM_LIT:1>, shape[<NUM_LIT:1>] if len(shape) > <NUM_LIT:1> else <NUM_LIT:2>):<EOL><INDENT>if marginals[i, k] > <NUM_LIT:0>:<EOL><INDENT>marginal_tuples.append((i, k, marginals[i, k]))<EOL><DEDENT><DEDENT><DEDENT>session.query(Marginal).filter(Marginal.training == training).delete(<EOL>synchronize_session="<STR_LIT>"<EOL>)<EOL>q = Marginal.__table__.insert()<EOL>insert_vals = []<EOL>for i, k, p in marginal_tuples:<EOL><INDENT>cid = X[i].id<EOL>insert_vals.append(<EOL>{<EOL>"<STR_LIT>": cid,<EOL>"<STR_LIT>": training,<EOL>"<STR_LIT:value>": k,<EOL>"<STR_LIT>": float(p),<EOL>}<EOL>)<EOL><DEDENT>session.execute(q, insert_vals)<EOL>session.commit()<EOL>logger.info(f"<STR_LIT>")<EOL>
Save marginal probabilities for a set of Candidates to db.

    :param X: A list of arbitrary objects with candidate ids accessible via a
        .id attrib
    :param marginals: A dense M x K matrix of marginal probabilities, where
        K is the cardinality of the candidates, OR a M-dim list/array if K=2.
    :param training: If True, these are training marginals / labels; else they
        are saved as end model predictions.

    Note: The marginals for k=0 are not stored, only for k = 1,...,K