Answers.Body: <p>The way I solve that is with a metaData table where I define what is what. This is not exacly what you ask for but, I find it more flexible since the pipeline does not crash if a file is named differently.</p>
  <p><strong>metaData.csv</strong></p>
  <pre><code>path;sample_sp;pairedStatus
  input/A.fq.gz;A;SE
  input/B-fw.fq.gz;B;PE
  input/B-rv.fq.gz;B;PE
  </code></pre>
  <p><strong>snakefile</strong></p>
  <pre><code>import pandas as pd
  samples = pd.read_table(&quot;metaData.csv&quot;,sep=';').set_index(&quot;path&quot;, drop=False)
  
  
  rule all:
      input:
          expand(&quot;out/{sample_sp}.sam&quot;,sample_sp=list(set([f&quot;{a}_{b}&quot; for a, b in zip(samples.sample_sp.iloc, samples.pairedStatus)])) )
  
  
  rule test_PE:
      input:
          lambda wc: samples[(samples.sample_sp == wc.id) &amp; (samples.pairedStatus == &quot;PE&quot;)].path
      output:
          temp(&quot;out/{id}_PE.sam&quot;)
      shell:
          &quot;test&quot;
  
  rule test_SE:
      input:
          lambda wc: samples[(samples.sample_sp == wc.id) &amp; (samples.pairedStatus == &quot;SE&quot;)].path
      output:
          temp(&quot;out/{id}_SE.sam&quot;)
      shell:
          &quot;test&quot;
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2024-06-20T09:25:09.587Z
Answers.Text: Interesting solution! I'll dig into using a DF to store sample information if my current approach doesn't pan out :)
Answers.UserDisplayName: Whitehot
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2024-06-18T12:46:48.273Z
Answers.LastActivityDate: 2024-06-18T12:46:48.273Z
Answers.UserDisplayName: kEks
Answers.Body: <p>Not sure about best practice, but this is basically also how i would do it. Having two independent rules handling PE and SE separately. So my guess would be that the error stems from a later rule which combines both PE and SE outputs again, or associating the right input to the correct rule.</p>
  <p>Could you provide a minimal example, this might help to spot the error.</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2024-06-18T11:36:34.570Z
Answers.Text: I never really got an error, I tried implementing the wildcard constraint and snakemake just didn't use that wildcard and never attempted to run the rules that used it. Currently I have rules for SE and PE separately when it matters, I'm just trying to implement a method where the filename alone is enough to get snakemake to understand which rules to run.
Answers.UserDisplayName: Whitehot
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2024-06-18T09:41:39.447Z
Answers.LastActivityDate: 2024-06-18T09:41:39.447Z
Answers.UserDisplayName: kEks
Answers.Body: <p>The negative lookbehind does seem to work for me. With just the below in a test Snakefile:</p>
  <pre><code>wildcard_constraints:
      sample_sp = r&quot;.+(?&lt;!_pe)&quot;
  
  rule bowtie2_map_se:
      input:
          &quot;DATA/FASTQ/{sample_sp}.filtered.fq.gz&quot;
      output:
          temp(&quot;DATA/BAM/UNSORTED/{sample_sp}.sam&quot;)
  
  rule bowtie2_map_pe:
      input:
          fq1=&quot;DATA/FASTQ/{sample_sp}_fw.filtered.fq.gz&quot;,
          fq2=&quot;DATA/FASTQ/{sample_sp}_rv.filtered.fq.gz&quot;
      output:
          temp(&quot;DATA/BAM/UNSORTED/{sample_sp}_pe.sam&quot;)
  </code></pre>
  <p>Then...</p>
  <pre><code>$ snakemake -n DATA/BAM/UNSORTED/foo_pe.sam
  Building DAG of jobs...
  MissingInputException in rule bowtie2_map_pe in file /home/tbooth2/workspace/snakemake_scratch/stack_overflow/Snakefile, line 10:
  Missing input files for rule bowtie2_map_pe:
      output: DATA/BAM/UNSORTED/foo_pe.sam
      wildcards: sample_sp=foo
      affected files:
          DATA/FASTQ/foo_fw.filtered.fq.gz
          DATA/FASTQ/foo_rv.filtered.fq.gz
  </code></pre>
  <p>If you comment out the wildcard constraints, the error from Snakemake will indicates that it considers both rules to be candidates to generate this output. I could <em>touch</em> the input files to get rid of this error, or use <code>--debug-dag</code>, but there is no point as the error message tells me everything I need.</p>
  <p>Remember that <code>wildcard_constraints</code> have no effect on rule inputs - they are only used by Snakemake when trying to determine if a given rule can create some requested output.</p>
  <p>While this can work, I'd not call it &quot;best practice&quot;. My inclination would be to have the data path reflect the type of BAM being created, eg:</p>
  <pre><code>rule bowtie2_map_se:
     input: ...
     output:
          temp(&quot;DATA/BAM/UNSORTED_SE/{sample_sp}.sam&quot;)
  
  rule bowtie2_map_pe:
      input: ...
      output:
          temp(&quot;DATA/BAM/UNSORTED_PE/{sample_sp}_pe.sam&quot;)
  </code></pre>
  <p>Now whatever rule deals with the unsorted BAM files (presumably using samtools or picard to sort the BAM) is going to have to do an if/else to decide if it wants to get input from <code>UNSORTED_PE</code> or <code>UNSORTED_SE</code>, so you'll need an input function there, but this can implement whatever logic you like, based on the sample name, or on a metadata table as suggested by @kEks, or just a global config item if you are not mixing se/pe in one run.</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2024-06-20T09:33:30.060Z
Answers.Text: I agree with your reasoning that using separate directories to store SE and PE data, but I am developing this pipeline for a solidly not-bioinformatics team and already have what they would consider a weighty folder architecture, so I think filename using wildcard constraints is my preferred option. Thanks for the answer!
Answers.UserDisplayName: Whitehot
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2024-06-19T12:02:43.463Z
Answers.LastActivityDate: 2024-06-19T12:02:43.463Z
Answers.UserDisplayName: Tim Booth
Body: <p>I am fairly new to Snakemake and am building an NGS data processing pipeline to align ChIP-seq data and call peaks on it, plus some other analyses. The data can be single-end or paired-end, depending on the experiment.</p>
  <p>Originally, I was intending to handle single-end and paired-end data using a <code>sample_sp</code> wildcard for the tools that require different paramters, and impose a regex constraint on it. That way, all my paired-end data files could bear a <code>_pe</code> suffix, and would only be processed by the paired-end rules in the pipeline like so:</p>
  <pre><code>rule bowtie2_map_se:
      input:
          &quot;DATA/FASTQ/{sample_sp}.filtered.fq.gz&quot;
      output:
          temp(&quot;DATA/BAM/UNSORTED/{sample_sp}.sam&quot;)
      ...
  
  
  rule bowtie2_map_pe:
      input:
          fq1=&quot;DATA/FASTQ/{sample_sp}_fw.filtered.fq.gz&quot;,
          fq2=&quot;DATA/FASTQ/{sample_sp}_rv.filtered.fq.gz&quot;
      output:
          temp(&quot;DATA/BAM/UNSORTED/{sample_sp}_pe.sam&quot;)
      ...
  
  </code></pre>
  <p>By constraining <code>sample_sp</code> to not end with <code>{&quot;_fw&quot;, &quot;_rv&quot;, &quot;_pe&quot;}</code>, then snakemake will not attempt to use those data files for single-end specific rules.</p>
  <p>However, I encountered <a href="https://stackoverflow.com/questions/78631989/snakemake-wildcard-constraint-using-negative-lookbehind-or-lookahead">issues</a> when trying to implement these regex constraints.</p>
  <p>What are the best practices for handling single-end and paired-end data in your snakemake pipelines? Are there ways to implement these regex constraints, or am I approaching the issue from the wrong point of view?</p>
  
Tags: <ngs><snakemake>
Title: Best practices for handling single-end and paired-end data in a Snakemake pipeline
ThreadText: # Best practices for handling single-end and paired-end data in a Snakemake pipeline
  
  (Asked by: username_0 on 2024-06-18)
  
  I am fairly new to Snakemake and am building an NGS data processing pipeline to align ChIP-seq data and call peaks on it, plus some other analyses. The data can be single-end or paired-end, depending on the experiment.
  
  Originally, I was intending to handle single-end and paired-end data using a `sample_sp` wildcard for the tools that require different paramters, and impose a regex constraint on it. That way, all my paired-end data files could bear a `_pe` suffix, and would only be processed by the paired-end rules in the pipeline like so:
  
  ```
  rule bowtie2_map_se:
      input:
          "DATA/FASTQ/{sample_sp}.filtered.fq.gz"
      output:
          temp("DATA/BAM/UNSORTED/{sample_sp}.sam")
      ...
  
  rule bowtie2_map_pe:
      input:
          fq1="DATA/FASTQ/{sample_sp}_fw.filtered.fq.gz",
          fq2="DATA/FASTQ/{sample_sp}_rv.filtered.fq.gz"
      output:
          temp("DATA/BAM/UNSORTED/{sample_sp}_pe.sam")
      ...
  
  ```
  
  By constraining `sample_sp` to not end with `{"_fw", "_rv", "_pe"}`, then snakemake will not attempt to use those data files for single-end specific rules.
  
  However, I encountered [issues](https://stackoverflow.com/questions/78631989/snakemake-wildcard-constraint-using-negative-lookbehind-or-lookahead) when trying to implement these regex constraints.
  
  What are the best practices for handling single-end and paired-end data in your snakemake pipelines? Are there ways to implement these regex constraints, or am I approaching the issue from the wrong point of view?
  
  **Tags:** ngs, snakemake
  
  
  ---
  
  ## 3 answers
  
  ### Answer by username_1 on 2024-06-18. Score: 2
  
  The way I solve that is with a metaData table where I define what is what. This is not exacly what you ask for but, I find it more flexible since the pipeline does not crash if a file is named differently.
  
  **metaData.csv**
  
  ```
  path;sample_sp;pairedStatus
  input/A.fq.gz;A;SE
  input/B-fw.fq.gz;B;PE
  input/B-rv.fq.gz;B;PE
  
  ```
  
  **snakefile**
  
  ```
  import pandas as pd
  samples = pd.read_table("metaData.csv",sep=';').set_index("path", drop=False)
  
  rule all:
      input:
          expand("out/{sample_sp}.sam",sample_sp=list(set([f"{a}_{b}" for a, b in zip(samples.sample_sp.iloc, samples.pairedStatus)])) )
  
  rule test_PE:
      input:
          lambda wc: samples[(samples.sample_sp == wc.id) & (samples.pairedStatus == "PE")].path
      output:
          temp("out/{id}_PE.sam")
      shell:
          "test"
  
  rule test_SE:
      input:
          lambda wc: samples[(samples.sample_sp == wc.id) & (samples.pairedStatus == "SE")].path
      output:
          temp("out/{id}_SE.sam")
      shell:
          "test"
  
  ```
  
  > **Comments:**
  > **username_0**: Interesting solution! I'll dig into using a DF to store sample information if my current approach doesn't pan out :)  
  
  ---
  
  ### Answer by username_1 on 2024-06-18. Score: 1
  
  Not sure about best practice, but this is basically also how i would do it. Having two independent rules handling PE and SE separately. So my guess would be that the error stems from a later rule which combines both PE and SE outputs again, or associating the right input to the correct rule.
  
  Could you provide a minimal example, this might help to spot the error.
  
  > **Comments:**
  > **username_0**: I never really got an error, I tried implementing the wildcard constraint and snakemake just didn't use that wildcard and never attempted to run the rules that used it. Currently I have rules for SE and PE separately when it matters, I'm just trying to implement a method where the filename alone is enough to get snakemake to understand which rules to run.  
  
  ---
  
  ### Answer by username_2 on 2024-06-19. Score: 1
  
  The negative lookbehind does seem to work for me. With just the below in a test Snakefile:
  
  ```
  wildcard_constraints:
      sample_sp = r".+(?<!_pe)"
  
  rule bowtie2_map_se:
      input:
          "DATA/FASTQ/{sample_sp}.filtered.fq.gz"
      output:
          temp("DATA/BAM/UNSORTED/{sample_sp}.sam")
  
  rule bowtie2_map_pe:
      input:
          fq1="DATA/FASTQ/{sample_sp}_fw.filtered.fq.gz",
          fq2="DATA/FASTQ/{sample_sp}_rv.filtered.fq.gz"
      output:
          temp("DATA/BAM/UNSORTED/{sample_sp}_pe.sam")
  
  ```
  
  Then...
  
  ```
  $ snakemake -n DATA/BAM/UNSORTED/foo_pe.sam
  Building DAG of jobs...
  MissingInputException in rule bowtie2_map_pe in file /home/tbooth2/workspace/snakemake_scratch/stack_overflow/Snakefile, line 10:
  Missing input files for rule bowtie2_map_pe:
      output: DATA/BAM/UNSORTED/foo_pe.sam
      wildcards: sample_sp=foo
      affected files:
          DATA/FASTQ/foo_fw.filtered.fq.gz
          DATA/FASTQ/foo_rv.filtered.fq.gz
  
  ```
  
  If you comment out the wildcard constraints, the error from Snakemake will indicates that it considers both rules to be candidates to generate this output. I could *touch* the input files to get rid of this error, or use `--debug-dag`, but there is no point as the error message tells me everything I need.
  
  Remember that `wildcard_constraints` have no effect on rule inputs - they are only used by Snakemake when trying to determine if a given rule can create some requested output.
  
  While this can work, I'd not call it "best practice". My inclination would be to have the data path reflect the type of BAM being created, eg:
  
  ```
  rule bowtie2_map_se:
     input: ...
     output:
          temp("DATA/BAM/UNSORTED_SE/{sample_sp}.sam")
  
  rule bowtie2_map_pe:
      input: ...
      output:
          temp("DATA/BAM/UNSORTED_PE/{sample_sp}_pe.sam")
  
  ```
  
  Now whatever rule deals with the unsorted BAM files (presumably using samtools or picard to sort the BAM) is going to have to do an if/else to decide if it wants to get input from `UNSORTED_PE` or `UNSORTED_SE`, so you'll need an input function there, but this can implement whatever logic you like, based on the sample name, or on a metadata table as suggested by @username_1, or just a global config item if you are not mixing se/pe in one run.
  
  > **Comments:**
  > **username_0**: I agree with your reasoning that using separate directories to store SE and PE data, but I am developing this pipeline for a solidly not-bioinformatics team and already have what they would consider a weighty folder architecture, so I think filename using wildcard constraints is my preferred option. Thanks for the answer!  
  
  ---
  
  