<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Bioinformatics | Giuliano Netto Flores Cruz</title>
    <link>/tag/bioinformatics/</link>
      <atom:link href="/tag/bioinformatics/index.xml" rel="self" type="application/rss+xml" />
    <description>Bioinformatics</description>
    <generator>Source Themes Academic (https://sourcethemes.com/academic/)</generator><language>en-us</language><lastBuildDate>Wed, 22 Jul 2026 00:00:00 +0000</lastBuildDate>
    <image>
      <url>/images/icon_hu6b1018a71552519c02d42a2d80b6cfcb_432_512x512_fill_lanczos_center_2.png</url>
      <title>Bioinformatics</title>
      <link>/tag/bioinformatics/</link>
    </image>
    
    <item>
      <title>Integrating interactive data analysis in R with Nextflow pipelines</title>
      <link>/post/here-argparse-nextflow/</link>
      <pubDate>Wed, 22 Jul 2026 00:00:00 +0000</pubDate>
      <guid>/post/here-argparse-nextflow/</guid>
      <description>&lt;p&gt;Most data analysis in R is done interactively. At some point, you may want this analysis to run as a reproducible 
&lt;a href=&#34;https://www.nextflow.io/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Nextflow&lt;/a&gt; pipeline. You carefully craft a structured script (e.g., with no hard-coded paths). By then, your code is reproducible, but often not so interactive anymore (e.g., lots of command-line options floating around). Iterating between Nextflow runs and analysis development becomes a bit of a headache. Here is a quick recipe to make your data analysis in R ready for Nextflow, while still supporting your regular interactive workflow.&lt;/p&gt;
&lt;h2 id=&#34;the-analysis-script&#34;&gt;The analysis script&lt;/h2&gt;
&lt;p&gt;We write the script the way we always would, and give it a small command-line interface with 
&lt;a href=&#34;https://cran.r-project.org/package=argparse&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;argparse&lt;/code&gt;&lt;/a&gt;. The one detail worth adding is to set each argument&amp;rsquo;s default with 
&lt;a href=&#34;https://here.r-lib.org/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;here::here()&lt;/code&gt;&lt;/a&gt;, so the default paths always resolve from the project root.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;#!/usr/bin/env Rscript
library(tidyverse)
library(argparse)
library(here)

parser &amp;lt;- ArgumentParser()
parser$add_argument(&amp;quot;--input_data&amp;quot;, default = here::here(&amp;quot;data/data.tsv&amp;quot;))
parser$add_argument(&amp;quot;--output_dir&amp;quot;, default = here::here(&amp;quot;tmp&amp;quot;))
args &amp;lt;- parser$parse_args()

dir.create(args$output_dir, showWarnings = FALSE, recursive = TRUE)

data &amp;lt;- read_tsv(args$input_data)

data %&amp;gt;%
  dplyr::count(group) %&amp;gt;%
  write_tsv(file.path(args$output_dir, &amp;quot;counts.tsv&amp;quot;))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run this in the R console and &lt;code&gt;parse_args()&lt;/code&gt; sees no command-line arguments, so every option falls back to its default.&lt;/p&gt;
&lt;p&gt;Because the defaults use &lt;code&gt;here::here()&lt;/code&gt;, they resolve from the project root regardless of our current working directory, so &lt;code&gt;read_tsv(args$input_data)&lt;/code&gt; just finds &lt;code&gt;&amp;lt;project-root&amp;gt;/data/data.tsv&lt;/code&gt;. We also point the outputs to a temporary directory (&lt;code&gt;&amp;lt;project-root&amp;gt;/tmp/&lt;/code&gt;) by default, as the final outputs will be created reproducibly in Nextflow later.&lt;/p&gt;
&lt;p&gt;Without &lt;code&gt;here::here()&lt;/code&gt;, the default would evaluate to &lt;code&gt;&amp;lt;working-directory&amp;gt;/data/data.tsv&lt;/code&gt; at runtime, which would only work if our working directory in R happened to match the project root. Anchoring to the project root explicitly keeps each path stable.&lt;/p&gt;
&lt;p&gt;Our interactive data analysis workflow is then untouched: we can continue analyzing data using the above script as usual.&lt;/p&gt;
&lt;h2 id=&#34;the-nextflow-pipeline&#34;&gt;The Nextflow pipeline&lt;/h2&gt;
&lt;p&gt;A minimal Nextflow pipeline that runs this one step is a single &lt;code&gt;main.nf&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-groovy&#34;&gt;// main.nf
params.input_data = &amp;quot;data/data.tsv&amp;quot;

process ANALYZE {
    input:
    path input_data

    output:
    path &amp;quot;counts.tsv&amp;quot;

    script:
    &amp;quot;&amp;quot;&amp;quot;
    analyze.R --input_data ${input_data} --output_dir &amp;quot;.&amp;quot;
    &amp;quot;&amp;quot;&amp;quot;
}

workflow {
    ANALYZE(Channel.fromPath(params.input_data))
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The process calls &lt;code&gt;analyze.R&lt;/code&gt; and passes the input and output paths explicitly on the command line.&lt;/p&gt;
&lt;h2 id=&#34;putting-the-script-in-the-pipeline&#34;&gt;Putting the script in the pipeline&lt;/h2&gt;
&lt;p&gt;For Nextflow to find the script, we add the shebang line (&lt;code&gt;#!/usr/bin/env Rscript&lt;/code&gt;, already at the top of the R script above), make it executable, and drop it in the pipeline&amp;rsquo;s &lt;code&gt;bin/&lt;/code&gt; folder:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;chmod +x bin/analyze.R # make it executable
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nextflow automatically puts &lt;code&gt;bin/&lt;/code&gt; on the &lt;code&gt;PATH&lt;/code&gt;, so the process can call &lt;code&gt;analyze.R&lt;/code&gt; by name. The whole project now looks like the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.
├── main.nf
├── bin/
│   └── analyze.R
└── data/
    └── data.tsv
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We run it with &lt;code&gt;nextflow run main.nf&lt;/code&gt;. Now the same script runs inside the pipeline, except Nextflow passes the input and output paths explicitly (&lt;code&gt;--input_data&lt;/code&gt; and &lt;code&gt;--output_dir&lt;/code&gt;), overriding the &lt;code&gt;here::here()&lt;/code&gt; defaults. Nothing in the script changed: the defaults serve our interactive session, and Nextflow supplies the real paths at runtime.&lt;/p&gt;
&lt;p&gt;That is the whole integration. We keep writing data analysis code exactly as before while leveraging the power of Nextflow.&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
