#!/usr/bin/env python# -*- coding: utf-8 -*-## This file is part of the `pypath` python module## Copyright 2014-2023# EMBL, EMBL-EBI, Uniklinik RWTH Aachen, Heidelberg University## Authors: see the file `README.rst`# Contact: Dénes Türei (turei.denes@gmail.com)## Distributed under the GPLv3 License.# See accompanying file LICENSE.txt or copy at# https://www.gnu.org/licenses/gpl-3.0.html## Website: https://pypath.omnipathdb.org/#"""Provides classes for representing and processing evidences supportingrelationships. The evidences hold information about the databases andliterature references, they can be organized into collections. A numberof operations are available on evidences and their collections, forexample they can be combined or filtered."""from__future__importannotationsfromfuture.utilsimportiteritemsimportimportlibasimpimportcopyimportpypath.internals.refsasrefsimportpypath.share.commonascommonimportpypath.share.sessionassession_modimportpypath.core.entityasentityimportpypath.core.attrsasattrs_modimportpypath.resources.networkasnetres_logger=session_mod.Logger(name='evidence')_log=_logger._log
[docs]classEvidence(attrs_mod.AttributeHandler):""" Represents an evidence supporting a relationship such as molecular interaction, molecular complex, enzyme-PTM interaction, annotation, etc. The evidence consists of two main parts: the database and the literature references. If a relationship is supported by multiple databases, for each one `Evidence` object should be created and :arg pypath.resource.ResourceAttributes resource: An object derived from :py:class:`pypath.resource.ResourceAttributes`. :arg str,list,set,NoneType references: Optional, one or more literature references (preferably PubMed IDs). """__slots__=['resource','references','dataset',]
[docs]defreload(self):""" Reloads the object from the module level. """modname=self.__class__.__module__mod=__import__(modname,fromlist=[modname.split('.')[0]])imp.reload(mod)new=getattr(mod,self.__class__.__name__)setattr(self,'__class__',new)
@staticmethoddef_process_references(references):references=common.to_set(references)return(set((refs.Reference(ref)ifnotisinstance(ref,refs.Reference)elseref)forrefinreferences))def__hash__(self):returnself.resource.__hash__()def__eq__(self,other):return(self.resource==otheror(hasattr(other,'resource')andself.resource==other.resourceand(self.resource.interaction_type==self.resource.interaction_type)))def__iadd__(self,other):""" This will ignore if the other evidence is from different resource: still better than attributing wrong references to a resource. """ifself==other:self.dataset=netres.choose_dataset(self.dataset,other.dataset)self.references.update(other.references)attrs_mod.AttributeHandler.__iadd__(other)else:_log('Warning: attempt to merge evidences from different ''resources. Ignoring the second evidence.')returnselfdef__add__(self,other):dataset=netres.choose_dataset(self.dataset,other.dataset)new=self.__class__(resource=self.resource,references=self.references|other.references,)new.dataset=datasetnew.update_attrs(self.attrs.copy())new.update_attrs(other.attrs.copy())returnnew@propertydefkey(self):returnself.resource.key
[docs]defmerge(self,other):""" Merges two evidences. Returns set of either one or two evidences depending on whether the two evidences are from the same resource. """ifself==other:self+=otherreturn{self}else:return{self,other}
def__repr__(self):return'<Evidence %s (%s%u references)>'%(self.resource.name,'via %s, '%self.resource.viaifself.resource.viaelse'',len(self.references),)def__str__(self):returnself.resource.namedef__copy__(self):returnself.__class__(resource=self.resource,references=copy.copy(self.references),attrs=self.attrs.copy(),)def__contains__(self,other):""" :arg str,tuple,Reference other: Either a reference or a database name, or a tuple of a database name and an interaction type or a tuple of a database, interaction type and a primary database (or None if the query should be limited only to primary databases). """return(self._contains(self,other)orattrs_mod.AttributeHandler.__contains__(self,other))defcontains_database(self,database):returnself.resource.name==databasedefcontains_reference(self,reference):returnreferenceinself.referencesdefhas_database_via(self,database,via):return(self.resource.name==databaseandself.resource.via==via)
[docs]defhas_interaction_type(self,interaction_type,database=None,via=False,):""" If ``via`` is ``False`` then it will be ignored, otherwise if ``None`` only primary resources are considered. """return(self.resource.interaction_type==interaction_typeand(notdatabaseorself.resource.name==database)and(via==Falseorself.resource.via==via))
@staticmethoddef_contains(obj,other):ifisinstance(other,int):other='%u'%otherifisinstance(other,str)andother.isdigit():other=refs.Reference(other)ifisinstance(other,refs.Reference):returnobj.contains_reference(other)# this makes possible to accept a NetworkResource or a# NetworkResourceKey:if(hasattr(other,'name')andhasattr(other,'interaction_type')andhasattr(other,'via')):other=(other.name,other.interaction_type,other.via)other=otherifisinstance(other,tuple)else(other,)return(obj.contains_database(other[0])and(len(other)==1orobj.has_interaction_type(other[1],other[0]))and(len(other)<=2orobj.has_database_via(other[0],other[2])))defhas_data_model(self,data_model):returnself.resource.data_model==data_modeldefmatch(self,resource=None,data_model=None,interaction_type=None,via=False,references=None,datasets=None,):def_match(attr,value):return(getattr(self.resource,attr)invalueifisinstance(value,_const.LIST_LIKE)elsegetattr(self.resource,attr)==value)resource=(resource.resourceifisinstance(resource,Evidence)elseresource)interaction_type=(resource.interaction_typeif(interaction_typeisNoneandhasattr(resource,'interaction_type'))elseinteraction_type)via=(resource.viaif(viaisNoneandhasattr(resource,'via'))elsevia)data_model=(resource.data_modelifhasattr(resource,'data_model')elsedata_model)references=common.to_set(references)return((resourceisNoneor(self.resource.nameinresourceifisinstance(resource,set)elseself.resource==resource))and(interaction_typeisNoneor_match('interaction_type',interaction_type))and(viaisNoneor(via==Falseandnotself.resource.via)or(via==Trueandself.resource.via)or_match('via',via))and(notreferencesorself.references&references)and(notdata_modelor_match('data_model',data_model))and(datasetsisNoneor_match('dataset',datasets)))def__str__(self):returnself.resource.name@propertydefpubmeds(self)->list[str]:""" PubMed IDs of the references supporting this evidence. """return[r.pmidforrinself.references]
[docs]defasdict(self)->dict:""" Dictionary representation of the evidence. """return{'resource':self.resource.name,'references':self.pubmeds,'dataset':self.dataset,'via':self.resource.via,'attrs':self.attrs,}
[docs]classEvidences(object):""" A collection of evidences. All evidences supporting a relationship such as molecular interaction, molecular complex, enzyme-PTM interaction, annotation, etc should be collected in one `Evidences` object. This way the set of evidences can be queried a comprehensive way. :arg tuple,list,set,Evidences evidences: An iterable providing :py:class:`Evidence` instances. It is possible to create an empty evidence collection and populate it later or to show this way that certain relationship has no supporting evidences. """__slots__=['evidences',]
[docs]defreload(self):""" Reloads the object from the module level. """modname=self.__class__.__module__mod=__import__(modname,fromlist=[modname.split('.')[0]])imp.reload(mod)new=getattr(mod,self.__class__.__name__)setattr(self,'__class__',new)new_ev_class=getattr(mod,'Evidence')forevinself:ev.__class__=new_ev_class
def__iadd__(self,other):ifisinstance(other,str):other=Evidence(other)other=(otherif(hasattr(other,'__iter__')andnotisinstance(other,Evidence))else(other,)ifisinstance(other,self.__class__)else())forevinother:ifev.keyinself.evidences:self.evidences[ev.key]=self.evidences[ev.key]+evelse:self.evidences[ev.key]=ev.__copy__()returnselfdef__add__(self,other):ifnotisinstance(other,self.__class__):returnself.__copy__()returnEvidences((self.evidences[key].__copy__()ifkeynotinother.evidenceselseother.evidences[key].__copy__()ifkeynotinself.evidenceselseself.evidences[key]+other.evidences[key])forkeyinset(self.evidences.keys())|set(other.evidences.keys()))def__radd__(self,other):returnself.__add__(other)def__sub__(self,other):returnEvidences(evforevinselfifevnotinother)defintersection(self,other):returnEvidences(self.evidences[key]+other.evidences[key]forkeyinset(self.evidences.keys())&set(other.evidences.keys()))def__iter__(self):forevinself.evidences.values():yieldevdef__repr__(self):return'<Evidences: %s (%u references)>'%((', '.join(sorted(set(ev.resource.nameforevinself)))ifselfelse'None'),(len(set.union(*(ev.referencesforevinself)))ifselfelse0),)def__copy__(self):returnEvidences((ev.__copy__()forevinself))def__bool__(self):returnbool(len(self.evidences))def__contains__(self,other):""" :arg str,tuple,Reference other: Either a reference or a database name, or a tuple of a database name and an interaction type or a tuple of a database, interaction type and a primary database (or None if the query should be limited only to primary databases). """returnEvidence._contains(self,other)def__and__(self,other):other=self._foreign_resources_set(other)this=self._resident_resources_set(other)returnthis&otherdef__or__(self,other):other=self._foreign_resources_set(other)this=self._resident_resources_set(other)returnthis|other@staticmethoddef_foreign_resources_set(resources):other=common.to_set(resources)return{(res.resourceifhasattr(res,'resource')elseres)forresinresources}def_resident_resources_set(self,other=None):return({ev.resource.nameforevinself}if(hasattr(other,'__iter__')andall(isinstance(res,str)forresinother))else{ev.resourceforevinself})def__eq__(self,other):return{ev.resourceforevinself}=={ev.resourceforevinother}def__len__(self):returnself.count_resources()defcount_resources(self,**kwargs):returnlen(list(self.filter(**kwargs)))defget_resources(self,**kwargs):return{ev.resourceforevinself.filter(**kwargs)}defget_resources_via(self,**kwargs):return{(ev.resource,ev.resource.via)forevinself.filter(**kwargs)}defget_resource_names_via(self,**kwargs):return{(ev.resource.name,ev.resource.via)forevinself.filter(**kwargs)}defcount_references(self,**kwargs):returnlen(self.get_references(**kwargs))defget_references(self,**kwargs):evidences=self.filter(**kwargs)return{refforevinevidencesforrefinev.references}defcount_curation_effort(self,**kwargs):returnlen(self.get_curation_effort(**kwargs))defget_curation_effort(self,**kwargs):evidences=self.filter(**kwargs)return{(ev.resource,ref)forevinevidencesforrefinev.references}defcontains_database(self,database,**kwargs):returnany(ev.resource.name==databaseforevinself.filter(**kwargs))defcontains_reference(self,reference,**kwargs):returnany(referenceinev.referencesforevinself.filter(**kwargs))defhas_database_via(self,database,via,**kwargs):returnany(ev.has_database_via(database,via)forevinself.filter(**kwargs))
[docs]defhas_interaction_type(self,interaction_type,database=None,via=False,):""" If ``via`` is ``False`` then it will be ignored, otherwise if ``None`` only primary resources are considered. """returnany(ev.has_interaction_type(interaction_type,database,via)forevinself)
[docs]defhas_dataset(self,dataset:str,**kwargs)->bool:""" Contains evidence(s) from a given dataset meeting the criteria. Args: dataset: Name of the dataset. kwargs: Filtering criteria for evidences. """returndatasetinself.get_datasets(**kwargs)
def__isub__(self,other):ifisinstance(other,self.__class__):self.evidences=dict((key,ev)forkey,eviniteritems(self.evidences)ifkeynotinother.evidencesorother.evidences[key]!=ev)else:self.remove(other)returnselfdefremove(self,resource=None,interaction_type=None,via=False):self.evidences=dict((key,ev)forkey,eviniteritems(self.evidences)ifnotev.match(resource=resource,interaction_type=interaction_type,via=via,))deffilter(self,resource=None,data_model=None,interaction_type=None,via=False,references=None,datasets=None,):return(evforevinselfifev.match(resource=resource,data_model=data_model,interaction_type=interaction_type,via=via,references=references,datasets=datasets,))defmatch(self,resource=None,data_model=None,interaction_type=None,via=False,references=None,datasets=None,):returnbool(tuple(self.filter(resource=resource,data_model=data_model,interaction_type=interaction_type,via=via,references=references,datasets=datasets,)))def__getitem__(self,key):""" Key is a :py:class:`pypath.internals.resource.NetworkResourceKey` or an equivalent tuple. """returnself.evidences.get(key,None)orself.simple_dict[key]
[docs]defkeys(self):""" Returns (dict_keys): The keys of this dictionary are :py:class:`pypath.internals.resource.NetworkResourceKey` objects. """returnself.evidences.keys()
[docs]defitems(self):""" Returns (dict_items): The evidences as a mapping, with :py:class:`pypath.internals.resource.NetworkResourceKey` objects as keys and :py:class:`pypath.core.evidence.Evidence` objects as values. """returnself.evidences.items()
@propertydefsimple_dict(self)->dict[str,evidence.Evidence]:""" Returns Keys are resource labels, values are ``Evidence`` objects. """returndict((res.last,ev)forres,eviniteritems(self))
[docs]defserialize_attrs(self,top_key_prefix:bool=True)->str:""" Serialize the extra attributes of the evidences as a JSON string. Returns A JSON serialized string with the evidences from each resource. """returnattrs_mod.AttributeHandler._serialize(self.simple_dict,top_key_prefix=top_key_prefix,default=lambdaobj:obj.serialize(),)
@propertydefdatasets(self)->set:""" Datasets in this evidence set. """return{ev.datasetforevinself}@propertydefattrs(self)->dict:""" Combines the custom attributes from all evidences within this set. """returncommon.combine_attrs([ev.attrsforevinself])
[docs]defasdict(self)->list[dict]:""" Evidence set as a list of dictionaries. """return[ev.asdict()forevinself]