org.apache.ctakes.ytex.uima.annotators.MetaMapToCTakesAnnotator.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.ctakes.ytex.uima.annotators.MetaMapToCTakesAnnotator.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.ctakes.ytex.uima.annotators;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ctakes.typesystem.type.constants.CONST;
import org.apache.ctakes.typesystem.type.refsem.OntologyConcept;
import org.apache.ctakes.typesystem.type.textsem.EntityMention;
import org.apache.ctakes.typesystem.type.textsem.IdentifiedAnnotation;
import org.apache.ctakes.typesystem.type.textsem.MedicationEventMention;
import org.apache.uima.UimaContext;
import org.apache.uima.analysis_component.JCasAnnotator_ImplBase;
import org.apache.uima.analysis_engine.AnalysisEngineProcessException;
import org.apache.uima.cas.FSIterator;
import org.apache.uima.cas.Feature;
import org.apache.uima.cas.Type;
import org.apache.uima.jcas.JCas;
import org.apache.uima.jcas.cas.FSArray;
import org.apache.uima.jcas.cas.StringArray;
import org.apache.uima.jcas.tcas.Annotation;
import org.apache.uima.resource.ResourceInitializationException;

/**
 * Convert metamap concepts to ctakes named entities.
 * <p/>
 * Create MedicationEventMention/EntityMention annotations for each set of
 * CandidateConcept annotations that span the same text.
 * <p/>
 * if checkMedications is set to true, see if a named entity has a medication
 * semantic type. if so, create a MedicationEventMention. else create
 * EntityMention.
 * 
 * @author vijay
 * 
 */
public class MetaMapToCTakesAnnotator extends JCasAnnotator_ImplBase {
    private static final Log log = LogFactory.getLog(MetaMapToCTakesAnnotator.class);
    private boolean checkMedications = false;

    private static final String[] medicationAbrs = { "aapp", "antb", "bacs", "bodm", "carb", "chem", "chvf", "chvs",
            "clnd", "eico", "elii", "enzy", "hops", "horm", "imft", "inch", "irda", "lipd", "nnon", "nsba", "opco",
            "orch", "phsu", "rcpt", "strd", "vita" };

    private static Set<String> setMedicationAbrs;
    static {
        setMedicationAbrs = new HashSet<String>(Arrays.asList(medicationAbrs));
    }

    public static class NegSpan {
        int begin;

        public int getBegin() {
            return begin;
        }

        public void setBegin(int begin) {
            this.begin = begin;
        }

        public int getEnd() {
            return end;
        }

        public void setEnd(int end) {
            this.end = end;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + begin;
            result = prime * result + end;
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            NegSpan other = (NegSpan) obj;
            if (begin != other.begin)
                return false;
            if (end != other.end)
                return false;
            return true;
        }

        public NegSpan(int begin, int end) {
            super();
            this.begin = begin;
            this.end = end;
        }

        public NegSpan(Annotation anno) {
            super();
            this.begin = anno.getBegin();
            this.end = anno.getEnd();
        }

        int end;
    }

    /**
     * get all negated spans
     * 
     * @param jcas
     * @return
     */
    private Set<NegSpan> getNegatedSpans(JCas jcas) {
        Set<NegSpan> negSet = new HashSet<NegSpan>();
        // get the Metamap type
        Type negType = jcas.getTypeSystem().getType("org.metamap.uima.ts.Negation");
        // abort if the type is not found
        if (negType == null) {
            log.debug("no negated concepts");
        } else {
            Feature spanFeature = negType.getFeatureByBaseName("ncSpans");
            if (spanFeature == null) {
                log.warn("no ncSpans feature!");
            } else {
                FSIterator<Annotation> negIter = jcas.getAnnotationIndex(negType).iterator();
                while (negIter.hasNext()) {
                    Annotation negAnno = negIter.next();
                    FSArray spanArr = (FSArray) negAnno.getFeatureValue(spanFeature);
                    if (spanArr != null) {
                        for (int i = 0; i < spanArr.size(); i++) {
                            negSet.add(new NegSpan((Annotation) spanArr.get(i)));
                        }
                    }
                }
            }
        }
        return negSet;
    }

    @Override
    public void process(JCas jcas) throws AnalysisEngineProcessException {
        // get the negated spans
        Set<NegSpan> negSet = getNegatedSpans(jcas);
        // get the Metamap type
        Type candidateType = jcas.getTypeSystem().getType("org.metamap.uima.ts.Candidate");
        // abort if the type is not found
        if (candidateType == null) {
            log.debug("no candidate concepts");
            return;
        }
        // get the cui feature
        Feature cuiFeature = candidateType.getFeatureByBaseName("cui");
        if (cuiFeature == null) {
            log.warn("no cui feature!");
            return;
        }
        Feature tuiFeature = candidateType.getFeatureByBaseName("semanticTypes");
        if (tuiFeature == null) {
            log.warn("no semanticTypes feature!");
            return;
        }

        // iterate through candidates
        FSIterator<Annotation> candidateIter = jcas.getAnnotationIndex(candidateType).iterator();
        // span we are working on in loop
        int begin = -1;
        int end = -1;
        // concepts for a given span
        Map<String, OntologyConcept> concepts = new HashMap<String, OntologyConcept>();
        // is one of the concepts for the span a medication?
        boolean bMedication = false;
        while (candidateIter.hasNext()) {
            Annotation annoCandidate = candidateIter.next();
            if (begin >= 0 && begin == annoCandidate.getBegin() && end == annoCandidate.getEnd()) {
                // this candidate spans the same text as the last named entity
                // add it as one of the concepts
                bMedication = addConcept(jcas, concepts, annoCandidate, cuiFeature, tuiFeature, bMedication);

            } else {
                // moving on to a new named entity, finalize the old one
                addNamedEntity(jcas, begin, end, concepts, bMedication, negSet);
                // reset span
                begin = annoCandidate.getBegin();
                end = annoCandidate.getEnd();
                bMedication = addConcept(jcas, concepts, annoCandidate, cuiFeature, tuiFeature, bMedication);
            }
        }
        addNamedEntity(jcas, begin, end, concepts, bMedication, negSet);
    }

    private void addNamedEntity(JCas jcas, int begin, int end, Map<String, OntologyConcept> concepts,
            boolean bMedication, Set<NegSpan> negSet) {
        if (concepts.isEmpty())
            return;
        IdentifiedAnnotation neLast = bMedication ? new MedicationEventMention(jcas) : new EntityMention(jcas);
        neLast.setPolarity(negSet.contains(new NegSpan(begin, end)) ? CONST.NE_POLARITY_NEGATION_PRESENT
                : CONST.NE_POLARITY_NEGATION_ABSENT);
        neLast.setBegin(begin);
        neLast.setEnd(end);
        FSArray ocArr = new FSArray(jcas, concepts.size());
        int ocArrIdx = 0;
        for (OntologyConcept oc : concepts.values()) {
            // set the cui field if this is in fact a cui
            ocArr.set(ocArrIdx, oc);
            ocArrIdx++;
        }
        neLast.setOntologyConceptArr(ocArr);
        concepts.clear();
        neLast.addToIndexes();
    }

    /**
     * add a concept to the map of concepts for the current named entity.
     * 
     * @param jcas
     * @param concepts
     * @param annoCandidate
     * @param cuiFeature
     * @param tuiFeature
     * @param bMedication
     * @return is this concept a medication concept? only check if
     *         checkMedications is true
     */
    private boolean addConcept(JCas jcas, Map<String, OntologyConcept> concepts, Annotation annoCandidate,
            Feature cuiFeature, Feature tuiFeature, boolean bMedication) {
        String cui = annoCandidate.getStringValue(cuiFeature);
        if (concepts.containsKey(cui))
            return bMedication;
        OntologyConcept oc = new OntologyConcept(jcas);
        oc.setCode(cui);
        oc.setCodingScheme("METAMAP");
        StringArray tuiArr = (StringArray) annoCandidate.getFeatureValue(tuiFeature);
        List<String> tuis = null;
        if (tuiArr != null)
            tuis = Arrays.asList(tuiArr.toStringArray());
        concepts.put(cui, oc);
        return checkMedications && tuis != null ? !Collections.disjoint(setMedicationAbrs, tuis) : false;
    }

    @Override
    public void initialize(UimaContext aContext) throws ResourceInitializationException {
        super.initialize(aContext);
        checkMedications = (Boolean) aContext.getConfigParameterValue("checkMedications");
    }

}