Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
Already on GitHub? Sign in to your account
matching between ontology classes (not instances) #41
Merged
Jump to file or symbol
Failed to load files and symbols.
| @@ -0,0 +1,93 @@ | ||
| +package org.monarchinitiative.owlsim.compute.classmatch; | ||
| + | ||
| +import java.util.ArrayList; | ||
| +import java.util.List; | ||
| +import java.util.Set; | ||
| +import java.util.stream.Collectors; | ||
| + | ||
| +import javax.inject.Inject; | ||
| + | ||
| +import org.monarchinitiative.owlsim.kb.BMKnowledgeBase; | ||
| +import org.monarchinitiative.owlsim.kb.LabelMapper; | ||
| + | ||
| +import com.googlecode.javaewah.EWAHCompressedBitmap; | ||
| + | ||
| +/** | ||
| + * Finds matches between classes in the KB | ||
| + * | ||
| + * @author cjm | ||
| + * | ||
| + */ | ||
| +public class ClassMatcher { | ||
| + | ||
| + BMKnowledgeBase kb; | ||
| + | ||
| + | ||
| + @Inject | ||
| + public ClassMatcher(BMKnowledgeBase kb) { | ||
| + super(); | ||
| + this.kb = kb; | ||
| + } | ||
| + | ||
| + /** | ||
| + * Find best match for every class in ont1, where the best | ||
| + * match is in ont2 | ||
| + * | ||
| + * @param qOnt | ||
| + * @param tOnt | ||
| + * @return | ||
| + */ | ||
| + public List<SimpleClassMatch> matchOntologies(String qOnt, String tOnt) { | ||
| + Set<String> qids = getClassIdsByOntology(qOnt); | ||
| + Set<String> tids = getClassIdsByOntology(tOnt); | ||
| + return matchClassSets(qids, tids); | ||
| + } | ||
| + | ||
| + public List<SimpleClassMatch> matchClassSets(Set<String> qids, | ||
| + Set<String> tids) { | ||
| + ArrayList<SimpleClassMatch> matches = new ArrayList<>(); | ||
| + for (String q : qids) { | ||
| + matches.add(getBestMatch(q, tids)); | ||
| + } | ||
| + return matches; | ||
| + } | ||
| + | ||
| + private SimpleClassMatch getBestMatch(String q, Set<String> tids) { | ||
| + EWAHCompressedBitmap qbm = kb.getSuperClassesBM(q); | ||
| + double bestEqScore = 0.0; | ||
| + String best = null; | ||
| + for (String t : tids) { | ||
| + EWAHCompressedBitmap tbm = kb.getSuperClassesBM(t); | ||
| + int numInQueryAndInTarget = qbm.andCardinality(tbm); | ||
| + int numInQueryOrInTarget = qbm.orCardinality(tbm); | ||
| + double eqScore = numInQueryAndInTarget / (double) numInQueryOrInTarget; | ||
| + if (eqScore > bestEqScore) { | ||
| + bestEqScore = eqScore; | ||
| + best = t; | ||
| + } | ||
| + } | ||
| + | ||
| + EWAHCompressedBitmap tbm = kb.getSuperClassesBM(best); | ||
| + int numInQueryAndInTarget = qbm.andCardinality(tbm); | ||
| + double subClassScore = numInQueryAndInTarget / (double) qbm.cardinality(); | ||
| + double superClassScore = numInQueryAndInTarget / (double) tbm.cardinality(); | ||
| + | ||
| + LabelMapper lm = kb.getLabelMapper(); | ||
| + return new SimpleClassMatch(q, best, | ||
| + lm.getArbitraryLabel(q), | ||
| + lm.getArbitraryLabel(best), | ||
| + bestEqScore, | ||
| + subClassScore, | ||
| + superClassScore); | ||
| + } | ||
| + | ||
| + public Set<String> getClassIdsByOntology(String ont) { | ||
| + return kb.getClassIdsInSignature().stream().filter(x -> isIn(x, ont)).collect(Collectors.toSet()); | ||
| + } | ||
| + | ||
| + public boolean isIn(String id, String ont) { | ||
| + // TODO - use curie util | ||
| + return id.startsWith(ont+":") || id.contains("/"+ont+"_"); | ||
| + } | ||
| + | ||
| +} |
| @@ -0,0 +1,124 @@ | ||
| +package org.monarchinitiative.owlsim.compute.classmatch; | ||
| + | ||
| +public class SimpleClassMatch { | ||
| + | ||
| + private String queryClassId; | ||
| + private String matchClassId; | ||
| + private String queryClassLabel; | ||
| + private String matchClassLabel; | ||
| + | ||
| + private double eqScore; | ||
| + private double subClassScore; | ||
| + private double superClassScore; | ||
| + public SimpleClassMatch(String queryClassId, String matchClassId, | ||
| + String queryClassLabel, String matchClassLabel, double eqScore, | ||
| + double subClassScore, double superClassScore) { | ||
| + super(); | ||
| + this.queryClassId = queryClassId; | ||
| + this.matchClassId = matchClassId; | ||
| + this.queryClassLabel = queryClassLabel; | ||
| + this.matchClassLabel = matchClassLabel; | ||
| + this.eqScore = eqScore; | ||
| + this.subClassScore = subClassScore; | ||
| + this.superClassScore = superClassScore; | ||
| + } | ||
| + /** | ||
| + * @return the queryClassId | ||
| + */ | ||
| + public String getQueryClassId() { | ||
| + return queryClassId; | ||
| + } | ||
| + /** | ||
| + * @param queryClassId the queryClassId to set | ||
| + */ | ||
| + public void setQueryClassId(String queryClassId) { | ||
| + this.queryClassId = queryClassId; | ||
| + } | ||
| + /** | ||
| + * @return the matchClassId | ||
| + */ | ||
| + public String getMatchClassId() { | ||
| + return matchClassId; | ||
| + } | ||
| + /** | ||
| + * @param matchClassId the matchClassId to set | ||
| + */ | ||
| + public void setMatchClassId(String matchClassId) { | ||
| + this.matchClassId = matchClassId; | ||
| + } | ||
| + /** | ||
| + * @return the queryClassLabel | ||
| + */ | ||
| + public String getQueryClassLabel() { | ||
| + return queryClassLabel; | ||
| + } | ||
| + /** | ||
| + * @param queryClassLabel the queryClassLabel to set | ||
| + */ | ||
| + public void setQueryClassLabel(String queryClassLabel) { | ||
| + this.queryClassLabel = queryClassLabel; | ||
| + } | ||
| + /** | ||
| + * @return the matchClassLabel | ||
| + */ | ||
| + public String getMatchClassLabel() { | ||
| + return matchClassLabel; | ||
| + } | ||
| + /** | ||
| + * @param matchClassLabel the matchClassLabel to set | ||
| + */ | ||
| + public void setMatchClassLabel(String matchClassLabel) { | ||
| + this.matchClassLabel = matchClassLabel; | ||
| + } | ||
| + /** | ||
| + * @return the eqScore | ||
| + */ | ||
| + public double getEqScore() { | ||
| + return eqScore; | ||
| + } | ||
| + /** | ||
| + * @param eqScore the eqScore to set | ||
| + */ | ||
| + public void setEqScore(double eqScore) { | ||
| + this.eqScore = eqScore; | ||
| + } | ||
| + /** | ||
| + * @return the subClassScore | ||
| + */ | ||
| + public double getSubClassScore() { | ||
| + return subClassScore; | ||
| + } | ||
| + /** | ||
| + * @param subClassScore the subClassScore to set | ||
| + */ | ||
| + public void setSubClassScore(double subClassScore) { | ||
| + this.subClassScore = subClassScore; | ||
| + } | ||
| + /** | ||
| + * @return the superClassScore | ||
| + */ | ||
| + public double getSuperClassScore() { | ||
| + return superClassScore; | ||
| + } | ||
| + /** | ||
| + * @param superClassScore the superClassScore to set | ||
| + */ | ||
| + public void setSuperClassScore(double superClassScore) { | ||
| + this.superClassScore = superClassScore; | ||
| + } | ||
| + /* (non-Javadoc) | ||
| + * @see java.lang.Object#toString() | ||
| + */ | ||
| + @Override | ||
| + public String toString() { | ||
| + return "SimpleClassMatch [queryClassId=" + queryClassId | ||
| + + ", matchClassId=" + matchClassId + ", queryClassLabel=" | ||
| + + queryClassLabel + ", matchClassLabel=" + matchClassLabel | ||
| + + ", eqScore=" + eqScore + ", subClassScore=" + subClassScore | ||
| + + ", superClassScore=" + superClassScore + "]"; | ||
| + } | ||
| + | ||
| + | ||
| + | ||
| + | ||
| +} |
| @@ -0,0 +1,8 @@ | ||
| +/** | ||
| + * | ||
| + */ | ||
| +/** | ||
| + * @author cjm | ||
| + * | ||
| + */ | ||
| +package org.monarchinitiative.owlsim.compute.classmatch; |
| @@ -0,0 +1,59 @@ | ||
| +package org.monarchinitiative.owlsim.compute.classmatch; | ||
| + | ||
| +import static org.junit.Assert.*; | ||
| + | ||
| +import java.net.URISyntaxException; | ||
| +import java.net.URL; | ||
| +import java.util.List; | ||
| + | ||
| +import org.apache.log4j.Logger; | ||
| +import org.junit.Test; | ||
| +import org.monarchinitiative.owlsim.compute.mica.AbstractMICAStoreTest; | ||
| +import org.monarchinitiative.owlsim.compute.mica.MICAStore; | ||
| +import org.monarchinitiative.owlsim.compute.mica.impl.MICAStoreImpl; | ||
| +import org.monarchinitiative.owlsim.compute.mica.impl.NoRootException; | ||
| +import org.monarchinitiative.owlsim.compute.stats.KBStatsCalculator; | ||
| +import org.monarchinitiative.owlsim.io.OWLLoader; | ||
| +import org.monarchinitiative.owlsim.kb.BMKnowledgeBase; | ||
| +import org.monarchinitiative.owlsim.kb.LabelMapper; | ||
| +import org.semanticweb.owlapi.model.IRI; | ||
| +import org.semanticweb.owlapi.model.OWLOntologyCreationException; | ||
| + | ||
| +import com.google.monitoring.runtime.instrumentation.common.com.google.common.io.Resources; | ||
| + | ||
| +public class ClassMatcherTest { | ||
| + | ||
| + protected BMKnowledgeBase kb; | ||
| + protected ClassMatcher classMatcher; | ||
| + private Logger LOG = Logger.getLogger(ClassMatcherTest.class); | ||
| + | ||
| + protected void load(String fn, String... ontfns) throws OWLOntologyCreationException, URISyntaxException, NoRootException { | ||
| + OWLLoader loader = new OWLLoader(); | ||
| + LOG.info("Loading: "+fn); | ||
| + loader.load(IRI.create(Resources.getResource(fn))); | ||
| + for (String ontfn : ontfns) { | ||
| + URL res = getClass().getResource(ontfn); | ||
| + LOG.info("RES="+res); | ||
| + loader.loadOntologies(res.getFile()); | ||
| + } | ||
| + kb = loader.createKnowledgeBaseInterface(); | ||
| + classMatcher = new ClassMatcher(kb); | ||
| + } | ||
| + | ||
| + @Test | ||
| + public void selfTest() throws OWLOntologyCreationException, URISyntaxException, NoRootException { | ||
| + load("mp-subset.ttl"); | ||
| + LabelMapper lm = kb.getLabelMapper(); | ||
| + | ||
| + List<SimpleClassMatch> matches = classMatcher.matchOntologies("MP", "MP"); | ||
| + | ||
| + int numNonSelfMatches = 0; | ||
| + for (SimpleClassMatch m : matches) { | ||
| + if (!m.getQueryClassId().equals(m.getMatchClassId())) { | ||
| + numNonSelfMatches++; | ||
| + } | ||
| + } | ||
| + assertEquals(0, numNonSelfMatches); | ||
| + } | ||
| + | ||
| +} |
| @@ -0,0 +1,66 @@ | ||
| +package org.monarchinitiative.owlsim.services.resources; | ||
| + | ||
| +import java.util.Collection; | ||
| +import java.util.HashMap; | ||
| +import java.util.List; | ||
| +import java.util.Map; | ||
| +import java.util.Set; | ||
| +import java.util.concurrent.TimeUnit; | ||
| + | ||
| +import javax.inject.Inject; | ||
| +import javax.ws.rs.GET; | ||
| +import javax.ws.rs.Path; | ||
| +import javax.ws.rs.PathParam; | ||
| +import javax.ws.rs.Produces; | ||
| +import javax.ws.rs.QueryParam; | ||
| +import javax.ws.rs.core.MediaType; | ||
| + | ||
| +import org.monarchinitiative.owlsim.compute.classmatch.ClassMatcher; | ||
| +import org.monarchinitiative.owlsim.compute.classmatch.SimpleClassMatch; | ||
| +import org.monarchinitiative.owlsim.compute.cpt.IncoherentStateException; | ||
| +import org.monarchinitiative.owlsim.compute.matcher.NegationAwareProfileMatcher; | ||
| +import org.monarchinitiative.owlsim.compute.matcher.ProfileMatcher; | ||
| +import org.monarchinitiative.owlsim.kb.filter.AnonIndividualFilter; | ||
| +import org.monarchinitiative.owlsim.kb.filter.TypeFilter; | ||
| +import org.monarchinitiative.owlsim.kb.filter.UnknownFilterException; | ||
| +import org.monarchinitiative.owlsim.model.match.MatchSet; | ||
| +import org.monarchinitiative.owlsim.model.match.ProfileQuery; | ||
| +import org.monarchinitiative.owlsim.model.match.ProfileQueryFactory; | ||
| +import org.monarchinitiative.owlsim.services.exceptions.NonNegatedMatcherException; | ||
| +import org.monarchinitiative.owlsim.services.exceptions.UnknownMatcherException; | ||
| + | ||
| +import com.codahale.metrics.annotation.Timed; | ||
| +import com.wordnik.swagger.annotations.Api; | ||
| +import com.wordnik.swagger.annotations.ApiOperation; | ||
| +import com.wordnik.swagger.annotations.ApiParam; | ||
| + | ||
| +import io.dropwizard.jersey.caching.CacheControl; | ||
| + | ||
| +@Path("/ontomatch") | ||
| +@Api(value = "/ontomatch", description = "ontology match services") | ||
| +@Produces({MediaType.APPLICATION_JSON}) | ||
| +public class OntologyMatchResource { | ||
| + | ||
| + @Inject | ||
| + ClassMatcher classMatcher; | ||
| + | ||
| + @GET | ||
| + @Path("/{queryOntology}/{targetOntology}") | ||
| + @Timed | ||
| + @CacheControl(maxAge = 2, maxAgeUnit = TimeUnit.HOURS) | ||
| + @ApiOperation(value = "Match", response = MatchSet.class, | ||
| + notes = "Additional notes on the match resource.") | ||
| + public List<SimpleClassMatch> getMatches( | ||
| + @ApiParam(value = "base ontology, e.g. MP", | ||
| + required = true) @PathParam("queryOntology") String queryOntology, | ||
| + @ApiParam(value = "ontology to be matched, e.g. HP", | ||
| + required = true) @PathParam("targetOntology") String targetOntology) | ||
| + throws UnknownFilterException, IncoherentStateException { | ||
| + List<SimpleClassMatch> matches = | ||
| + classMatcher.matchOntologies(queryOntology, targetOntology); | ||
| + return matches; | ||
| + } | ||
| + | ||
| + // TODO - API for comparing two entities | ||
| + | ||
| +} |