Skip to content

Compare samples

Once you have two or more .snipesig signatures, you can combine and compare them with set operations: keep what they share, pool them together, or subtract one from another. This guide walks the four operations — intersect, union, sum, and difference — from both the snipe CLI and the import snipe Python API.

You will learn:

  • What each set operation means for edgemers, not just plain k-mers.
  • The two CLI shapes: intersect (sample vs. reference) and the repeatable -i/--input operations.
  • The matching Python operators (&, |, +, -) and methods (sig.intersect / union / sum / difference).
  • How to measure similarity with jaccard and containment.

Each operation has a CLI subcommand, a Python operator, and a Python method. They fall into two families:

  • Additive ops (union, sum) combine signatures and require both operands to share the same backing — both edgemer, or both K1-only.
  • Filter ops (intersect, difference) subset the left signature; the result follows the left operand’s backing.
Operation CLI Python operator Python method What it keeps
Intersect snipe intersect a & b a.intersect(b) Edgemers present in both
Union snipe union a | b a.union(b) Every edgemer, max abundance (max-merge)
Sum snipe sum a + b a.sum(b) Every edgemer, abundances added
Difference snipe difference a - b a.difference(b) Left minus right

The union vs. sum distinction matters for abundance: union is a max-merge (each edgemer keeps the larger of the two abundances), while sum adds the abundances — pooling depth across signatures.

  1. snipe intersect compares a sample against a reference and keeps only the edgemers present in both. Unlike the other set operations, it takes exactly two named inputs — --sample and --reference — not a repeatable list:

    Terminal window
    snipe intersect --sample sampleA.snipesig --reference sampleB.snipesig -o shared.snipesig

    The output shared.snipesig is the shared content. Because intersect is a filter op, the result follows the left (sample) signature’s backing: intersecting an edgemer sample keeps edgemer structure.

    In Python, use the & operator or the intersect method:

    import snipe
    a = snipe.load("sampleA.snipesig")
    b = snipe.load("sampleB.snipesig")
    shared = a & b # operator form
    shared = a.intersect(b) # method form — identical result
    shared.save("shared.snipesig")

    Pass inplace=True to a.intersect(b, inplace=True) to mutate a and return None instead of allocating a new signature.

  2. union, sum, and difference all use the repeatable -i/--input flag and need at least two inputs. Repeat the flag once per signature:

    Terminal window
    snipe union -i run1.snipesig -i run2.snipesig -i run3.snipesig -o merged.snipesig

    union is a max-merge: the merged signature carries every edgemer seen in any input, each at its maximum observed abundance. To add abundances instead — pooling coverage across technical replicates of the same sample — use sum:

    Terminal window
    snipe sum -i rep1.snipesig -i rep2.snipesig -o pooled.snipesig

    Both are additive ops, so every input must share the same backing. In Python, the operators fold naturally, and there are module-level helpers for a list:

    import snipe
    sigs = snipe.load_many("runs/*.snipesig")
    merged = snipe.union(sigs) # left-fold | over the list
    pooled = snipe.sum(sigs) # left-fold + over the list
    # equivalently, two at a time:
    merged = sigs[0] | sigs[1]
    pooled = sigs[0] + sigs[1]

    snipe.union and snipe.sum left-fold | / + across the list — the same semantics as the CLI, where the first input is the base.

  3. Difference: subtract one signature from another

    Section titled “Difference: subtract one signature from another”

    snipe difference computes the first input minus the rest — the edgemers unique to the first signature after removing everything in the others:

    Terminal window
    snipe difference -i sampleA.snipesig -i contaminant.snipesig -o cleaned.snipesig

    By default, difference removes an edgemer entirely when it appears in a later input. The --weighted flag changes this to subtract abundances instead of removing edgemers completely — useful when you want to deplete depth rather than drop content outright:

    Terminal window
    snipe difference --weighted -i sampleA.snipesig -i background.snipesig -o depleted.snipesig

    Difference is a filter op, so the result follows the first (left) signature’s backing. In Python:

    import snipe
    a = snipe.load("sampleA.snipesig")
    b = snipe.load("contaminant.snipesig")
    cleaned = a - b # operator form
    cleaned = a.difference(b) # method form
    # fold over a whole list (first minus the rest):
    cleaned = snipe.difference([a, b, c])
  4. Set operations produce new signatures. When you only want a number describing how alike two samples are, the Python API exposes two scalar comparisons directly on a signature:

    import snipe
    a = snipe.load("sampleA.snipesig")
    b = snipe.load("sampleB.snipesig")
    a.jaccard(b) # |A ∩ B| / |A ∪ B| — symmetric overlap
    a.containment(b) # |A ∩ B| / |A| — how much of A is in B

    Both operate over the K1-hash sets. jaccard is symmetric; containment is directional — how much of a is contained in b. Two empty signatures give 0.0.

  • Intersect (&, snipe intersect --sample/--reference) keeps shared edgemers — a filter op that follows the left backing.
  • Union (|, snipe union -i …) max-merges; sum (+, snipe sum -i …) adds abundances. Both are additive and need matching backings and at least two inputs.
  • Difference (-, snipe difference -i …) is first-minus-the-rest; --weighted subtracts abundances instead of dropping edgemers.
  • For a similarity score rather than a new signature, use sig.jaccard or sig.containment.