diff --git a/actions/ost-compare-structures b/actions/ost-compare-structures index 8453cf6871fcd931ee3ed60fc7fff383f3150daa..0692c610e9419856ff1d5fb56458c9838bc02c4b 100644 --- a/actions/ost-compare-structures +++ b/actions/ost-compare-structures @@ -359,7 +359,13 @@ def _ParseArgs(): "reports the fraction of reference contacts that are correctly " "reproduced in the model. " "The ICS score (Interface Contact Similarity) available as key " - "\"ics\" combines precision and recall using the F1-measure.")) + "\"ics\" combines precision and recall using the F1-measure. " + "All these measures are also available on a per-interface basis " + "for each interface in the reference structure that are defined " + "as chain pairs with at least one contact (available as key " + " \"contact_reference_interfaces\"). The respective metrics are " + "available as keys \"per_interface_ics_precision\", " + "\"per_interface_ics_recall\" and \"per_interface_ics\".")) parser.add_argument( "--ips", @@ -657,6 +663,10 @@ def _Process(model, reference, args): out["ics_precision"] = scorer.ics_precision out["ics_recall"] = scorer.ics_recall out["ics"] = scorer.ics + out["contact_reference_interfaces"] = scorer.contact_target_interfaces + out["per_interface_ics_precision"] = scorer.per_interface_ics_precision + out["per_interface_ics_recall"] = scorer.per_interface_ics_recall + out["per_interface_ics"] = scorer.per_interface_ics if args.ips: out["ips_precision"] = scorer.ips_precision diff --git a/modules/doc/actions.rst b/modules/doc/actions.rst index 3faf8c62faea9ab5999cf6283739c6cc87b8cea4..eb315c341565fa74641ea3cab001558f802fb1ea 100644 --- a/modules/doc/actions.rst +++ b/modules/doc/actions.rst @@ -270,7 +270,14 @@ Details on the usage (output of ``ost compare-structures --help``): contacts that are correctly reproduced in the model. The ICS score (Interface Contact Similarity) available as key "ics" combines precision and recall using the - F1-measure. + F1-measure. All these measures are also available on a + per-interface basis for each interface in the + reference structure that are defined as chain pairs + with at least one contact (available as key + "contact_reference_interfaces"). The respective + metrics are available as keys + "per_interface_ics_precision", + "per_interface_ics_recall" and "per_interface_ics". --ips Computes interface patch similarity (IPS) related scores. They focus on interface residues. They are defined as having at least one contact to a residue diff --git a/modules/mol/alg/pymod/contact_score.py b/modules/mol/alg/pymod/contact_score.py index 802a2e7c12fda20a5007319ede265d2f208d739f..ef77a183dc714857a4a890a416201ae3a99be5a3 100644 --- a/modules/mol/alg/pymod/contact_score.py +++ b/modules/mol/alg/pymod/contact_score.py @@ -516,7 +516,7 @@ class ContactScorer: return self.ICSFromFlatMapping(flat_mapping) - def ScoreInterface(self, trg_ch1, trg_ch2, mdl_ch1, mdl_ch2): + def ScoreICSInterface(self, trg_ch1, trg_ch2, mdl_ch1, mdl_ch2): """ Computes ICS scores only considering one interface This only works for interfaces that are computed in :func:`Score`, i.e. diff --git a/modules/mol/alg/pymod/scoring.py b/modules/mol/alg/pymod/scoring.py index e73f9d7e9b91e02aa36be054411ef812f457c9e4..50a1e7faabd2a1c6c323e9a32c38b3a6352e2281 100644 --- a/modules/mol/alg/pymod/scoring.py +++ b/modules/mol/alg/pymod/scoring.py @@ -251,6 +251,9 @@ class Scorer: self._ics_precision = None self._ics_recall = None self._ics = None + self._per_interface_ics_precision = None + self._per_interface_ics_recall = None + self._per_interface_ics = None self._ips_precision = None self._ips_recall = None self._ips = None @@ -749,6 +752,48 @@ class Scorer: self._compute_ics_scores() return self._ics + @property + def per_interface_ics_precision(self): + """ Per-interface ICS precision + + :attr:`~ics_precision` for each interface in + :attr:`~contact_target_interfaces` + + :type: :class:`list` of :class:`float` + """ + if self._per_interface_ics_precision is None: + self._compute_ics_scores() + return self._per_interface_ics_precision + + + @property + def per_interface_ics_recall(self): + """ Per-interface ICS recall + + :attr:`~ics_recall` for each interface in + :attr:`~contact_target_interfaces` + + :type: :class:`list` of :class:`float` + """ + if self._per_interface_ics_recall is None: + self._compute_ics_scores() + return self._per_interface_ics_recall + + @property + def per_interface_ics(self): + """ Per-interface ICS (Interface Contact Similarity) score + + :attr:`~ics` for each interface in + :attr:`~contact_target_interfaces` + + :type: :class:`float` + """ + + if self._per_interface_ics is None: + self._compute_ics_scores() + return self._per_interface_ics + + @property def ips_precision(self): """ Fraction of model interface residues that are also interface @@ -1342,6 +1387,25 @@ class Scorer: self._ics_precision = contact_scorer_res.precision self._ics_recall = contact_scorer_res.recall self._ics = contact_scorer_res.ics + self._per_interface_ics_precision = list() + self._per_interface_ics_recall = list() + self._per_interface_ics = list() + flat_mapping = self.mapping.GetFlatMapping() + for trg_int in self.contact_target_interfaces: + trg_ch1 = trg_int[0] + trg_ch2 = trg_int[1] + if trg_ch1 in flat_mapping and trg_ch2 in flat_mapping: + mdl_ch1 = flat_mapping[trg_ch1] + mdl_ch2 = flat_mapping[trg_ch2] + res = self.contact_scorer.ScoreICSInterface(trg_ch1, trg_ch2, + mdl_ch1, mdl_ch2) + self._per_interface_ics_precision.append(res.precision) + self._per_interface_ics_recall.append(res.recall) + self._per_interface_ics.append(res.ics) + else: + self._per_interface_ics_precision.append(None) + self._per_interface_ics_recall.append(None) + self._per_interface_ics.append(None) def _compute_ips_scores(self): contact_scorer_res = self.contact_scorer.ScoreIPS(self.mapping.mapping)