-
Notifications
You must be signed in to change notification settings - Fork 596
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added some generic read tag/expression fliters for use on numeric tags (
- Loading branch information
1 parent
bd640ea
commit a3141f7
Showing
7 changed files
with
387 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
95 changes: 95 additions & 0 deletions
95
...n/java/org/broadinstitute/hellbender/engine/filters/JexlExpressionReadTagValueFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
package org.broadinstitute.hellbender.engine.filters; | ||
|
||
import htsjdk.samtools.util.Lazy; | ||
import htsjdk.variant.variantcontext.VariantContextUtils; | ||
import org.apache.commons.jexl2.Expression; | ||
import org.apache.commons.jexl2.JexlContext; | ||
import org.apache.logging.log4j.LogManager; | ||
import org.apache.logging.log4j.Logger; | ||
import org.broadinstitute.barclay.argparser.Argument; | ||
import org.broadinstitute.barclay.help.DocumentedFeature; | ||
import org.broadinstitute.hellbender.cmdline.ReadFilterArgumentDefinitions; | ||
import org.broadinstitute.hellbender.tools.walkers.haplotypecaller.HaplotypeCallerEngine; | ||
import org.broadinstitute.hellbender.utils.help.HelpConstants; | ||
import org.broadinstitute.hellbender.utils.read.GATKRead; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.LinkedList; | ||
import java.util.List; | ||
import java.util.function.BiFunction; | ||
|
||
/** | ||
* Keep only reads that the attributes of meet a given set of jexl expressions | ||
*/ | ||
@DocumentedFeature(groupName = HelpConstants.DOC_CAT_READFILTERS, groupSummary = HelpConstants.DOC_CAT_READFILTERS_SUMMARY, | ||
summary = "Keep only reads that meet all given jexl expressions (on their attributes)") | ||
public final class JexlExpressionReadTagValueFilter extends ReadFilter { | ||
private static final long serialVersionUID = 1L; | ||
private static final Logger logger = LogManager.getLogger(JexlExpressionReadTagValueFilter.class); | ||
|
||
@Argument(fullName=ReadFilterArgumentDefinitions.READ_FILTER_EXPRESSION_LONG_NAME, doc="One or more JEXL expressions used to filter", optional=false) | ||
public List<String> filterExpressions = new ArrayList<>(); | ||
|
||
private Lazy<List<Expression>> jexlExprs = new Lazy<>(() -> { | ||
List<Expression> l = new LinkedList<>(); | ||
for ( String expr : filterExpressions ) { | ||
final Expression jexl = VariantContextUtils.engine.get().createExpression(expr); | ||
logger.info("created jexl: " + jexl); | ||
l.add(jexl); | ||
} | ||
return l; | ||
}); | ||
|
||
private static class GATKReadJexlContext implements JexlContext { | ||
|
||
final private GATKRead read; | ||
|
||
GATKReadJexlContext(final GATKRead read) { | ||
this.read = read; | ||
} | ||
|
||
@Override | ||
public Object get(final String name) { | ||
return read.getAttributeAsString(name); | ||
} | ||
|
||
@Override | ||
public void set(final String name, final Object value) { | ||
throw new IllegalArgumentException("setting attributes is not allowed"); | ||
} | ||
|
||
@Override | ||
public boolean has(final String name) { | ||
return read.hasAttribute(name); | ||
} | ||
} | ||
|
||
public JexlExpressionReadTagValueFilter() { | ||
} | ||
|
||
// convenience constructor for using a single jexl expression | ||
public JexlExpressionReadTagValueFilter(final String jexlExpr) { | ||
this.filterExpressions = Collections.singletonList(jexlExpr); | ||
} | ||
|
||
// convenience constructor for using a multiple jexl expressions | ||
public JexlExpressionReadTagValueFilter(final List<String> jexlExprs) { | ||
this.filterExpressions = jexlExprs; | ||
} | ||
|
||
@Override | ||
public boolean test(final GATKRead read) { | ||
|
||
// loop over expressions. At this point expressions are ANDed | ||
for ( Expression expr : jexlExprs.get() ) { | ||
Object v = expr.evaluate(new GATKReadJexlContext(read)); | ||
if (!v.equals(Boolean.TRUE)) { | ||
return false; | ||
} | ||
} | ||
|
||
// if here, all expressions matched | ||
return true; | ||
} | ||
} |
78 changes: 78 additions & 0 deletions
78
src/main/java/org/broadinstitute/hellbender/engine/filters/ReadTagValueFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package org.broadinstitute.hellbender.engine.filters; | ||
|
||
import htsjdk.samtools.util.Lazy; | ||
import htsjdk.variant.variantcontext.VariantContextUtils; | ||
import org.apache.commons.jexl2.JexlContext; | ||
import org.apache.commons.jexl2.JexlEngine; | ||
import org.broadinstitute.barclay.argparser.Argument; | ||
import org.broadinstitute.barclay.help.DocumentedFeature; | ||
import org.broadinstitute.hellbender.cmdline.ReadFilterArgumentDefinitions; | ||
import org.broadinstitute.hellbender.utils.help.HelpConstants; | ||
import org.broadinstitute.hellbender.utils.read.GATKRead; | ||
|
||
import org.apache.commons.jexl2.Expression; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.LinkedList; | ||
import java.util.List; | ||
import java.util.function.BiFunction; | ||
|
||
/** | ||
* Keep only reads that contain a tag with a value that agrees with parameters as specified. | ||
* | ||
*/ | ||
@DocumentedFeature(groupName = HelpConstants.DOC_CAT_READFILTERS, groupSummary = HelpConstants.DOC_CAT_READFILTERS_SUMMARY, | ||
summary = "Keep only reads that contains a tag with value that agrees with parameters") | ||
public final class ReadTagValueFilter extends ReadFilter { | ||
private static final long serialVersionUID = 1L; | ||
|
||
@Argument(fullName = ReadFilterArgumentDefinitions.READ_FILTER_TAG, | ||
doc = "Look for this tag in read", optional=false) | ||
public String readFilterTagName = null; | ||
|
||
@Argument(fullName = ReadFilterArgumentDefinitions.READ_FILTER_TAG_COMP, | ||
doc = "Compare value in tag to this value", optional=true) | ||
public Float readFilterTagComp = 0F; | ||
|
||
public enum Operator { | ||
LESS((Float x, Float y) -> x < y), | ||
LESS_OR_EQUAL((Float x, Float y) -> x <= y), | ||
GREATER((Float x, Float y) -> x > y), | ||
GREATER_OR_EQUAL((Float x, Float y) -> x >= y), | ||
EQUAL(Float::equals), | ||
NOT_EQUAL((Float x, Float y) -> !x.equals(y)); | ||
|
||
final BiFunction<Float, Float, Boolean> comp; | ||
|
||
Operator(BiFunction<Float, Float, Boolean> comp) { | ||
this.comp = comp; | ||
} | ||
} | ||
|
||
@Argument(fullName = ReadFilterArgumentDefinitions.READ_FILTER_TAG_OP, | ||
doc = "Compare value in tag to value with this operator. " + | ||
"If T is the value in the tag, OP is the operation provided, " + | ||
"and V is the value in read-filter-tag, then the " + | ||
"read will pass the filter iff T OP V is true.", optional = true) | ||
public Operator readFilterTagOp = Operator.EQUAL; | ||
|
||
public ReadTagValueFilter() { | ||
} | ||
|
||
// convenience constructor for using <tag operator value> form | ||
public ReadTagValueFilter(final String tagName, final float tagValue, final Operator operator) { | ||
this.readFilterTagName = tagName; | ||
this.readFilterTagComp = tagValue; | ||
this.readFilterTagOp = operator; | ||
} | ||
|
||
@Override | ||
public boolean test(final GATKRead read) { | ||
|
||
return read.hasAttribute(this.readFilterTagName) && | ||
this.readFilterTagComp != null && | ||
this.readFilterTagOp.comp.apply(read.getAttributeAsFloat(this.readFilterTagName), | ||
this.readFilterTagComp); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
89 changes: 89 additions & 0 deletions
89
...rg/broadinstitute/hellbender/engine/filters/JexlExpressionReadTagValueFilterUnitTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package org.broadinstitute.hellbender.engine.filters; | ||
|
||
import org.broadinstitute.hellbender.GATKBaseTest; | ||
import org.broadinstitute.hellbender.utils.read.GATKRead; | ||
import org.testng.Assert; | ||
import org.testng.annotations.DataProvider; | ||
import org.testng.annotations.Test; | ||
|
||
import java.util.Arrays; | ||
import java.util.Iterator; | ||
import java.util.LinkedList; | ||
import java.util.List; | ||
|
||
public class JexlExpressionReadTagValueFilterUnitTest extends GATKBaseTest { | ||
|
||
@Test(dataProvider= "JexlExpressionReadTagValueFilterDataProvider") | ||
public void testJexlExpressionReadTagValueFilter(final String cigarString, | ||
final Object[] attrsNameAndValue, | ||
final String[] jexlExpr, | ||
final boolean expectedResult, | ||
final Class<? extends Throwable> expectedException) { | ||
|
||
final JexlExpressionReadTagValueFilter filter; | ||
|
||
// test different constructors here as well | ||
if ( jexlExpr.length == 1 ) { | ||
filter = new JexlExpressionReadTagValueFilter(jexlExpr[0]); | ||
} else { | ||
filter = new JexlExpressionReadTagValueFilter(Arrays.asList(jexlExpr)); | ||
} | ||
|
||
final GATKRead read = ReadTagValueFilterUnitTest.buildSAMRead(cigarString, attrsNameAndValue); | ||
try { | ||
Assert.assertEquals(filter.test(read), expectedResult, cigarString); | ||
Assert.assertNull(expectedException); | ||
} catch (Throwable e) { | ||
if ( expectedException == null || !expectedException.isInstance(e) ) | ||
throw e; | ||
} | ||
} | ||
|
||
@DataProvider(name = "JexlExpressionReadTagValueFilterDataProvider") | ||
public Iterator<Object[]> jexlExpressionReadTagValueFilterDataProvider() { | ||
final List<Object[]> result = new LinkedList<>(); | ||
|
||
result.add(new Object[] { | ||
"100M", // cigar | ||
new Object[] {"TM", 1.0f}, // attributes | ||
new String[] {"TM == 1.0"}, // jexl expressions | ||
Boolean.TRUE, // expected | ||
null // expected exception | ||
}); | ||
|
||
result.add(new Object[] { | ||
"100M", // cigar | ||
new Object[] {"TM", 1.0f}, // attributes | ||
new String[] {"TM < 1.0"}, // jexl expressions | ||
Boolean.FALSE, // expected | ||
null // expected exception | ||
}); | ||
|
||
result.add(new Object[] { | ||
"100M", // cigar | ||
new Object[] {"TM", 1.0f}, // attributes | ||
new String[] {"NO_SUCH < 1.0"}, // jexl expressions | ||
Boolean.FALSE, // expected | ||
IllegalArgumentException.class // expected exception | ||
}); | ||
|
||
result.add(new Object[] { | ||
"100M", // cigar | ||
new Object[] {"TM", 1.0f, "TA", 2.0f}, // attributes | ||
new String[] {"TM >= 1.0", "TA <= 2.0"}, // jexl expressions | ||
Boolean.TRUE, // expected | ||
null // expected exception | ||
}); | ||
|
||
result.add(new Object[] { | ||
"100M", // cigar | ||
new Object[] {"TM", 1.0f, "TA", 2.0f}, // attributes | ||
new String[] {"TM >= 1.0", "TA < 2.0"}, // jexl expressions | ||
Boolean.FALSE, // expected | ||
null // expected exception | ||
}); | ||
return result.iterator(); | ||
} | ||
|
||
|
||
} |
Oops, something went wrong.