Holy cattle, it worked ?!? I don't know of a specific xcas reader offhand ... have you tried running with the xmi reader? Some of the reads laying around will handle both.
-----Original Message----- From: Tomasz Oliwa [mailto:ol...@uchicago.edu] Sent: Thursday, November 19, 2015 6:48 PM To: dev@ctakes.apache.org Subject: RE: TermConsumers Sean, I tested this, the Annotator itself works, great. The only change I had to do when writing the Annotator class with the code below is to provide generics in: static private final Collection<Class<? extends IdentifiedAnnotation>> EVENT_CLASSES = Arrays.<Class<? extends IdentifiedAnnotation>>asList( MedicationMention.class, DiseaseDisorderMention.class, SignSymptomMention.class, LabMention.class, ProcedureMention.class ); At least on a small example XMI CAS I see the behavior is as expected for the IdentifiedAnnotations. However, for my usecase, I have XCAS files, not XMI CAS files. I can use XCasWriterCasConsumer to write the CAS files, but I cannot find any XCAS Collection Reader to initially read them in. Is such a reader available? Regards, Tomasz ________________________________________ From: Finan, Sean [sean.fi...@childrens.harvard.edu] Sent: Thursday, November 19, 2015 4:03 PM To: dev@ctakes.apache.org Subject: RE: TermConsumers Hi Tomasz, I don't know that anybody has done this. However, you could try running a pipeline with items in ctakes-core: XmiCollectionReaderCtakes to read your existing cas xmi files in directory -- custom refiner AE below -- to remove unwanted umls annotations XmiWriterCasConsumerCtakes to write the new cas xmi files The refiner AE would basically do what the PrecisionTermConsumer of the fast lookup does, but over a pre-populated cas. This is mostly cut and paste from other code with a little bit of lookompiling - I haven't tested it at all! If you do give it a run-through and it works then let me know and I'll clean it up and check into sandbox. static private final Collection<Class<? extends IdentifiedAnnotation>> EVENT_CLASSES = Arrays.asList( MedicationMention.class, DiseaseDisorderMention.class, SignSymptomMention.class, LabMention.class, ProcedureMention.class ); // Don't forget AnatomicalSiteMention.class and generic EntityMention.class! static private final Function<Annotation,TextSpan> createTextSpan = annotation -> new DefaultTextSpan( annotation.getBegin(), annotation.getEnd() ); static private final Function<IdentifiedAnnotation,IdentifiedAnnotation> returnSelf = annotation -> annotation; @Override public void process( final JCas jcas ) throws AnalysisEngineProcessException { LOGGER.info( "Starting processing" ); for ( Class<? extends IdentifiedAnnotation> eventClass : EVENT_CLASSES ) { refineForClass( jcas, eventClass ); } final Collection<AnatomicalSiteMention> anatomicals = JCasUtil.select( jcas, AnatomicalSiteMention.class ); final Collection<EntityMention> entityMentions = new ArrayList<>( JCasUtil.select( jcas, EntityMention.class ) ); entityMentions.removeAll( anatomicals ); refineForAnnotations( jcas, anatomicals ); refineForAnnotations( jcas, entityMentions ); LOGGER.info( "Finished processing" ); } static private <T extends IdentifiedAnnotation> void refineForClass( final JCas jcas, final Class<T> eventClass ) { refineForAnnotations( jcas, JCasUtil.select( jcas, eventClass ) ); } static private <T extends IdentifiedAnnotation> void refineForAnnotations( final JCas jcas, final Collection<T> annotations ) { final Map<TextSpan,IdentifiedAnnotation> annotationTextSpans = annotations.stream().collect( Collectors.toMap( createTextSpan, returnSelf ) ); final Collection<TextSpan> unwantedSpans = getUnwantedSpans( annotationTextSpans.keySet() ); unwantedSpans.stream().map( annotationTextSpans::get ).forEach( t -> t.removeFromIndexes( jcas ) ); } static private Collection<TextSpan> getUnwantedSpans( final Collection<TextSpan> originalTextSpans ) { final List<TextSpan> textSpans = new ArrayList<>( originalTextSpans ); final Collection<TextSpan> discardSpans = new HashSet<>(); final int count = textSpans.size(); for ( int i = 0; i < count; i++ ) { final TextSpan spanKeyI = textSpans.get( i ); for ( int j = i + 1; j < count; j++ ) { final TextSpan spanKeyJ = textSpans.get( j ); if ( (spanKeyJ.getBegin() <= spanKeyI.getBegin() && spanKeyJ.getEnd() > spanKeyI.getEnd()) || (spanKeyJ.getBegin() < spanKeyI.getBegin() && spanKeyJ.getEnd() >= spanKeyI.getEnd()) ) { // J contains I, discard less precise concepts for span I and move on to next span I discardSpans.add( spanKeyI ); break; } if ( ((spanKeyI.getBegin() <= spanKeyJ.getBegin() && spanKeyI.getEnd() > spanKeyJ.getEnd()) || (spanKeyI.getBegin() < spanKeyJ.getBegin() && spanKeyI.getEnd() >= spanKeyJ.getEnd())) ) { // I contains J, discard less precise concepts for span J and move on to next span J discardSpans.add( spanKeyJ ); } } } return discardSpans; } Good luck, Sean -----Original Message----- From: Tomasz Oliwa [mailto:ol...@uchicago.edu] Sent: Thursday, November 19, 2015 12:08 PM To: dev@ctakes.apache.org Subject: TermConsumers Hi, How can I run a different TermConsumer on already generated CAS files? I have CAS files created by the AggregatePlaintextFastUMLSProcessor with the DefaultTermConsumer set in cTakesHsql.xml. Now I would like to apply the PrecisionTermConsumer on these CAS files without having to do the whole annotation process again. The IdentifiedAnnotations are all there, it is only a matter of removing them according to the TermConsumers logic. Is there a way to create a passthrough Processor that simply reads the CAS, applies a different TermConsumer and writes it to disk? Or is there a different way to go on about this? Thanks for any help, Tomasz