Introduction to QuickBLAST

R is widely used for data analysis, but running NCBI’s standard BLAST tools within R has traditionally been slow. Because the NCBI C++ toolkit is massive and difficult to compile for R, existing packages are forced to run BLAST as an external subprocess, creating major read/write bottlenecks.

QuickBLAST solves this by building a direct bridge between R and the NCBI C++ toolkit via Rcpp. By bypassing traditional text-based formatting and transporting data directly into memory using Apache Arrow, QuickBLAST performs sequence comparisons exceptionally fast.

Setup and Instance Management

QuickBLAST operates using “instances”. An instance is an active C++ object pointer (QuickBLAST_XPtr) that holds your BLAST configuration open in memory.

library(QuickBLAST)
#>  QuickBLAST Loaded!
#>  Version: 1.99.5
#>  Github: https://github.com/vizkidd/QuickBLAST

QuickBLAST::isQuickBLASTLoaded()
#>  [1] TRUE

# Create instances for different sequence types and programs.  Note: seq_type =
# 1 (Protein), seq_type = 0 (Nucleotide)
blastp_inst <- QuickBLAST::CreateQuickBLASTInstance(seq_type = 1, strand = 0, program = "blastp",
    save_sequences = TRUE, options = "-evalue 100000")
#>  Option: evalue set to : 100000

blastn_inst <- QuickBLAST::CreateQuickBLASTInstance(seq_type = 0, strand = 0, program = "blastn",
    options = "-evalue 100000")
#>  Option: evalue set to : 100000

tblastx_inst <- QuickBLAST::CreateQuickBLASTInstance(seq_type = 0, strand = 0, program = "tblastx")
#>  Using tblastx Defaults...

# Inspecting an instance reveals its underlying C++ pointer and attributes
blastn_inst
#>  <pointer: 0x55bd65a32ff0>
#>  attr(,"seq_type")
#>  [1] 0
#>  attr(,"strand")
#>  [1] 0
#>  attr(,"program")
#>  [1] "blastn"
#>  attr(,"options")
#>  [1] "-evalue 100000"
#>  attr(,"save_sequences")
#>  [1] FALSE
#>  attr(,"save_hsp_sequences")
#>  [1] FALSE
#>  attr(,"class")
#>  [1] "QuickBLAST_XPtr"

Managing Memory

Because QuickBLAST creates objects in C++, we provide convenience functions to track, verify, and clean up instances to prevent memory leaks.

QuickBLAST::GetInstanceCount()
#>  [1] 3

QuickBLAST::GetInstanceID(blastp_inst)
#>  [1] 0

# Clean up memory by deleting specific instances
QuickBLAST::DeleteQuickBLASTInstance(QuickBLAST::GetQuickBLASTInstance(1))
#>  [1] TRUE

QuickBLAST::DeleteQuickBLASTInstance(2)
#>  [1] TRUE

QuickBLAST::GetInstanceCount()
#>  [1] 1

# Attempting to fetch a deleted instance securely throws a C++ map error
# try(identical(try(QuickBLAST::GetQuickBLASTInstance(1), silent = TRUE,
# outFile = stdout()), blastn_inst), silent = T, outFile = stdout())

# Recreate the blastn instance for later steps
blastn_inst <- QuickBLAST::CreateQuickBLASTInstance(seq_type = 0, strand = 0, program = "blastn")
#>  Using blastn Defaults...

Raw Sequence Comparison

You can pass raw character strings directly to QuickBLAST. The engine evaluates the sequences in memory instantly. Notice that QuickBLAST includes automatic recovery: if a pointer is dead, it will attempt to reload it.

# (~)Successful TBLASTX execution : Garbage low information nucleotide sequences
try(QuickBLAST::BLAST2Seqs(tblastx_inst, "AAAAAAAAAAAATTTTTTTTTTTTGGGGGGGGGGGCCCCCCCCC", "TTTTTTTTTTTGGGGGGGGGGGG"), silent = T, outFile = stdout())
#>  Dead pointer detected. Attempting to reload...
#>  Using tblastx Defaults...
#>  Clock : 0.00225137 seconds
#>  1
#>  RecordBatchVector size: 1
#>  Total rows across all batches: 1
#>  [[1]]
#>    seq_info_num_alignments seq_info_seqids_qseqid seq_info_seqids_sseqid
#>  1                       1                      1                      2
#>    seq_info_seqs_qseq seq_info_seqs_sseq seq_info_strands seq_info_lengths_qlen
#>  1                                                    +/+                    44
#>    seq_info_lengths_slen hsps_qhsp hsps_shsp hsps_pident hsps_pident_gap
#>  1                    23                               0               0
#>    hsps_frames hsps_evalue hsps_length hsps_length01 hsps_qstart hsps_qend
#>  1         3/2           0         207      1.045455          14        35
#>    hsps_sstart hsps_send hsps_bitscore hsps_score hsps_qcovhsp hsps_blast_score
#>  1           1        23             0          0            0                0
#>    hsps_gaps hsps_nident hsps_mismatch hsps_positive hsps_n_splices hsps_hsp_num
#>  1         0           0           207             0              0            1
#>    hsps_sum_evalue hsps_product_coverage hsps_overall_identity
#>  1               0                     0                     0
#>    hsps_negative_count hsps_matches hsps_high_quality_percent_coverage
#>  1                   0            0                                  0
#>    hsps_exon_identity hsps_consensus_splices hsps_comp_adj_method
#>  1                  0                      0                    0

# Expected empty results due to mismatched pointer type (Nucleotide vs Nucleotide with a blastp pointer) 
try(QuickBLAST::BLAST2Seqs(blastp_inst, "AAAAAAAAAAAATTTTTTTTTTTTGGGGGGGGGGGCCCCCCCCC", "TTTTTTTTTTTGGGGGGGGGGGG"), silent = T, outFile = stdout())
#>  Clock : 0.00115844 seconds
#>  1
#>  RecordBatchVector size: 1
#>  list()

# (~)Successful Protein-Protein search : Same protein
QuickBLAST::BLAST2Seqs(blastp_inst, "MQILLVEDDNTLFQELKKELEQWDFNVAGIEDFGKVMDTFESFNPEIVILDVQLPKYDGFYWCRKMREVSNVPILFLSSRDNPMDQVMSMELGADDYMQKPFYTNVLIAKLQAIYRRVYEFTAEEKRTLTWQDAVVDLSKDSIQKGDDTIFLSKTEMIILEILITKKNQIVSRDTIITALWDDEAFVSDNTLTVNVNRLRKKLSEISMDSAIETKVGKGYMAHE", "MQILLVEDDNTLFQELKKELEQWDFNVAGIEDFGKVMDTFESFNPEIVILDVQLPKYDGFYWCRKMREVSNVPILFLSSRDNPMDQVMSMELGADDYMQKPFYTNVLIAKLQAIYRRVYEFTAEEKRTLTWQDAVVDLSKDSIQKGDDTIFLSKTEMIILEILITKKNQIVSRDTIITALWDDEAFVSDNTLTVNVNRLRKKLSEISMDSAIETKVGKGYMAHE")
#>  Clock : 0.00184889 seconds
#>  1
#>  RecordBatchVector size: 1
#>  Total rows across all batches: 1
#>  [[1]]
#>    seq_info_num_alignments seq_info_seqids_qseqid seq_info_seqids_sseqid
#>  1                       1                      3                      4
#>                                                                                                                                                                                                                  seq_info_seqs_qseq
#>  1 MQILLVEDDNTLFQELKKELEQWDFNVAGIEDFGKVMDTFESFNPEIVILDVQLPKYDGFYWCRKMREVSNVPILFLSSRDNPMDQVMSMELGADDYMQKPFYTNVLIAKLQAIYRRVYEFTAEEKRTLTWQDAVVDLSKDSIQKGDDTIFLSKTEMIILEILITKKNQIVSRDTIITALWDDEAFVSDNTLTVNVNRLRKKLSEISMDSAIETKVGKGYMAHE
#>                                                                                                                                                                                                                  seq_info_seqs_sseq
#>  1 MQILLVEDDNTLFQELKKELEQWDFNVAGIEDFGKVMDTFESFNPEIVILDVQLPKYDGFYWCRKMREVSNVPILFLSSRDNPMDQVMSMELGADDYMQKPFYTNVLIAKLQAIYRRVYEFTAEEKRTLTWQDAVVDLSKDSIQKGDDTIFLSKTEMIILEILITKKNQIVSRDTIITALWDDEAFVSDNTLTVNVNRLRKKLSEISMDSAIETKVGKGYMAHE
#>    seq_info_strands seq_info_lengths_qlen seq_info_lengths_slen hsps_qhsp
#>  1              */*                   224                   224          
#>    hsps_shsp hsps_pident hsps_pident_gap hsps_frames   hsps_evalue hsps_length
#>  1                   100             100         0/0 9.626626e-172         224
#>    hsps_length01 hsps_qstart hsps_qend hsps_sstart hsps_send hsps_bitscore
#>  1             1           1       224           1       224      458.7585
#>    hsps_score hsps_qcovhsp hsps_blast_score hsps_gaps hsps_nident hsps_mismatch
#>  1       1179            1             1179         0         224             0
#>    hsps_positive hsps_n_splices hsps_hsp_num hsps_sum_evalue
#>  1           224              0            1               0
#>    hsps_product_coverage hsps_overall_identity hsps_negative_count hsps_matches
#>  1                     0                     0                   0            0
#>    hsps_high_quality_percent_coverage hsps_exon_identity hsps_consensus_splices
#>  1                                  0                  0                      0
#>    hsps_comp_adj_method
#>  1                    2

# Successful NT-NT search
QuickBLAST::BLAST2Seqs(blastn_inst, "ATGGAGGAGCCGCAGTCAGATCCTAGCGTCGAGCCCCCTCTGAGTCAGGAAACATTTTCA", "ATGGAGGAGCCGCAGTCAGATCCTAGCCTCGAGCCCCCTCTGAGTCAGGAAACATTTTCA")
#>  Clock : 0.000831738 seconds
#>  1
#>  RecordBatchVector size: 1
#>  Total rows across all batches: 1
#>  [[1]]
#>    seq_info_num_alignments seq_info_seqids_qseqid seq_info_seqids_sseqid
#>  1                       1                      1                      2
#>    seq_info_seqs_qseq seq_info_seqs_sseq seq_info_strands seq_info_lengths_qlen
#>  1                                                    +/+                    60
#>    seq_info_lengths_slen hsps_qhsp hsps_shsp hsps_pident hsps_pident_gap
#>  1                    60                        98.33333        98.33333
#>    hsps_frames  hsps_evalue hsps_length hsps_length01 hsps_qstart hsps_qend
#>  1         2/2 2.763249e-29          60             1           1        60
#>    hsps_sstart hsps_send hsps_bitscore hsps_score hsps_qcovhsp hsps_blast_score
#>  1           1        60      106.3793         57            0               57
#>    hsps_gaps hsps_nident hsps_mismatch hsps_positive hsps_n_splices hsps_hsp_num
#>  1         0          59             1             0              0            1
#>    hsps_sum_evalue hsps_product_coverage hsps_overall_identity
#>  1               0                     0                     0
#>    hsps_negative_count hsps_matches hsps_high_quality_percent_coverage
#>  1                   0            0                                  0
#>    hsps_exon_identity hsps_consensus_splices hsps_comp_adj_method
#>  1                  0                      0                    0

Remote NCBI Searching

If you do not want to compile databases locally, QuickBLAST can interface directly with NCBI’s remote servers.

# Safely check if the machine has internet access before running
if (requireNamespace("curl", quietly = TRUE) && curl::has_internet()) {
# Example of a remote timeout handling
try(QuickBLAST::RemoteBLAST(blastn_inst, query_input="AAAAAAAAAAAATTTTTTTTTTTTGGGGGGGGGGGCCCCCCCCC", database= "nr", input_type=1, return_values=TRUE), silent = T, outFile = stdout())

# Successful Remote Protein Query against the PDB database
QuickBLAST::RemoteBLAST(blastp_inst, query_input="MQILLVEDDNTLFQELKKELEQWDFNVAGIEDFGKVMDTFESFNPEIVILDVQLPKYDGFYWCRKMREVSNVPILFLSSRDNPMDQVMSMELGADDYMQKPFYTNVLIAKLQAIYRRVYEFTAEEKRTLTWQDAVVDLSKDSIQKGDDTIFLSKTEMIILEILITKKNQIVSRDTIITALWDDEAFVSDNTLTVNVNRLRKKLSEISMDSAIETKVGKGYMAHE", database= "pdb", input_type=1, return_values=TRUE)
  
}else {
  message("No internet connection available. Skipping remote BLAST examples.")
}

File-to-File Comparisons

For large-scale genomics, sequences are often stored in FASTA files. QuickBLAST can compare two files directly via multi-threaded reading, without loading everything into the R global environment first.


safe_temp_dir <- normalizePath(tempdir(check = T), mustWork = FALSE)

# Create temporary FASTA files
query_fasta <- normalizePath(tempfile(fileext = ".fasta", tmpdir = safe_temp_dir), mustWork = F)
subject_fasta <- normalizePath(tempfile(fileext = ".fasta", tmpdir = safe_temp_dir), mustWork = F)

# Human TP53 gene fragment (Query)
writeLines(c(
  ">Human_p53_fragment\nATGGAGGAGCCGCAGTCAGATCCTAGCGTCGAGCCCCCTCTGAGTCAGGAAACATTTTCA"
), query_fasta)

# Slightly mutated version (Subject - e.g., an ortholog or variant)
writeLines(c(
  ">Variant_p53_fragment\nATGGAGGAGCCGCAGTCAGATCCTAGCCTCGAGCCCCCTCTGAGTCAGGAAACATTTTCA"
), subject_fasta)

# Efficiently compare the files using the BLASTN instance
file_results <- QuickBLAST::BLAST2Files(
  blastn_inst, 
  query = query_fasta, 
  subject = subject_fasta,
  return_values = TRUE,
  num_threads = 1
)
#>  Num Threads: 1
#>  Total Records (Q + S): 2 (1 + 1)
#>  Batch Size: 2
#>  Computing: [========================================] 100% (done)                         
#>  Total Records Processed: 1
#>  Clock : 0.0026459 seconds
#>  RecordBatchVector size: 1
#>  Total rows across all batches: 1

print(file_results)
#>  [[1]]
#>    seq_info_num_alignments seq_info_seqids_qseqid seq_info_seqids_sseqid
#>  1                       1     Human_p53_fragment   Variant_p53_fragment
#>    seq_info_seqs_qseq seq_info_seqs_sseq seq_info_strands seq_info_lengths_qlen
#>  1                                                    +/+                    60
#>    seq_info_lengths_slen hsps_qhsp hsps_shsp hsps_pident hsps_pident_gap
#>  1                    60                        98.33333        98.33333
#>    hsps_frames  hsps_evalue hsps_length hsps_length01 hsps_qstart hsps_qend
#>  1         2/2 2.763249e-29          60             1           1        60
#>    hsps_sstart hsps_send hsps_bitscore hsps_score hsps_qcovhsp hsps_blast_score
#>  1           1        60      106.3793         57            0               57
#>    hsps_gaps hsps_nident hsps_mismatch hsps_positive hsps_n_splices hsps_hsp_num
#>  1         0          59             1             0              0            1
#>    hsps_sum_evalue hsps_product_coverage hsps_overall_identity
#>  1               0                     0                     0
#>    hsps_negative_count hsps_matches hsps_high_quality_percent_coverage
#>  1                   0            0                                  0
#>    hsps_exon_identity hsps_consensus_splices hsps_comp_adj_method
#>  1                  0                      0                    0

Local Database Creation and Searching

For high-throughput homology searches against a fixed reference, compiling the sequences into an indexed BLAST database is much faster than raw file-to-file comparisons. QuickBLAST wraps the NCBI makeblastdb functionality directly into R.


safe_temp_dir <- normalizePath(tempdir(check = T), mustWork = FALSE)
query_fasta <- normalizePath(tempfile(fileext = ".fasta", tmpdir = safe_temp_dir), mustWork = FALSE)
subject_fasta <- normalizePath(tempfile(fileext = ".fasta", tmpdir = safe_temp_dir), mustWork = FALSE)

# Human TP53 gene fragment (Query)
writeLines(c(
  ">Human_p53_fragment\nATGGAGGAGCCGCAGTCAGATCCTAGCGTCGAGCCCCCTCTGAGTCAGGAAACATTTTCA"
), query_fasta)
# Mutant TP53 gene fragment (Subject)
writeLines(c(
  ">Variant_p53_fragment\nATGGAGGAGCCGCAGTCAGATCCTAGCCTCGAGCCCCCTCTGAGTCAGGAAACATTTTCA"
), subject_fasta)

db_path <- file.path(safe_temp_dir, "My_Test_DB")
# db_path_sub <- file.path(safe_temp_dir, "My_Test_DB_sub")

# 1. Compile the local BLAST database
# db_type can be "nucl" or "prot"
# QuickBLAST::MakeBLASTDB(
#   ptr = blastn_inst,
#   input_file = query_fasta,
#   database_name = db_path,
#   parse_seqids = FALSE,
#   stdout_opt = "",
#   stderr_opt = FALSE
# )

try(QuickBLAST::MakeBLASTDB(
  ptr = blastn_inst,
  input_file = subject_fasta,
  database_name = db_path,
  parse_seqids = FALSE
), silent = TRUE, outFile = stdout())
#>  [MakeBLASTDB] Executing command: /home/runner/work/_temp/Library/QuickBLAST/bin//makeblastdb -in /tmp/Rtmp3qYeLB/file816f10f21a5e.fasta -dbtype nucl -out /tmp/Rtmp3qYeLB/My_Test_DB 
#>  makeblastdb finished successfully
#>  [1] "/tmp/Rtmp3qYeLB/My_Test_DB"

# 2. Search a query file or query database against the custom local database
db_results <- try(QuickBLAST::BLAST2DBs(
 blastn_inst,
 query = query_fasta,
 subject = db_path,
 return_values = TRUE,
 num_threads = 2
), silent = TRUE, outFile = stdout())
#>  [MakeBLASTDB] Executing command: /home/runner/work/_temp/Library/QuickBLAST/bin//makeblastdb -in /tmp/Rtmp3qYeLB/file816fd36c9ff.fasta -dbtype nucl -out /tmp/Rtmp3qYeLB/file816fd36c9ff.fasta.db 
#>  makeblastdb finished successfully
#>  Q :/tmp/Rtmp3qYeLB/file816fd36c9ff.fasta.db
#>  S :/tmp/Rtmp3qYeLB/My_Test_DB
#>  Num Threads: 2
#>  Total Records (Q + S): 2 (1 + 1)
#>  Batch Size: 3
#>  Batch Hits: 1
#>  Processed Batches:1
#>  Total Records Processed: 2
#>  Clock : 0.0764669 seconds
#>  RecordBatchVector size: 1
#>  Total rows across all batches: 1

print(db_results)
#>  [[1]]
#>    seq_info_num_alignments seq_info_seqids_qseqid seq_info_seqids_sseqid
#>  1                       1     Human_p53_fragment     Human_p53_fragment
#>    seq_info_seqs_qseq seq_info_seqs_sseq seq_info_strands seq_info_lengths_qlen
#>  1                                                    +/+                    60
#>    seq_info_lengths_slen hsps_qhsp hsps_shsp hsps_pident hsps_pident_gap
#>  1                    60                        98.33333        98.33333
#>    hsps_frames  hsps_evalue hsps_length hsps_length01 hsps_qstart hsps_qend
#>  1         1/1 2.763249e-29          60             1           0        59
#>    hsps_sstart hsps_send hsps_bitscore hsps_score hsps_qcovhsp hsps_blast_score
#>  1           0        59      106.3793         57            0               57
#>    hsps_gaps hsps_nident hsps_mismatch hsps_positive hsps_n_splices hsps_hsp_num
#>  1         0          59             1             0              0            1
#>    hsps_sum_evalue hsps_product_coverage hsps_overall_identity
#>  1               0                     0                     0
#>    hsps_negative_count hsps_matches hsps_high_quality_percent_coverage
#>  1                   0            0                                  0
#>    hsps_exon_identity hsps_consensus_splices hsps_comp_adj_method
#>  1                  0                      0                    0

High-Performance Output with Apache Arrow

By default, the return_values = TRUE flag seen above brings data directly into R as an Rcpp::List. However, when processing millions of alignments, memory can max out quickly.

To solve this, QuickBLAST writes results asynchronously using Apache Arrow formats (arrow::parquet, arrow::ipc and arrow::csv). This creates a highly compressed, blazingly fast columnar storage file, an inter-process-communicable file or a tabular CSV.


safe_temp_dir <- normalizePath(tempdir(check = T), mustWork = FALSE)
query_fasta <- normalizePath(tempfile(fileext = ".fasta", tmpdir = safe_temp_dir), mustWork = FALSE)
subject_fasta <- normalizePath(tempfile(fileext = ".fasta", tmpdir = safe_temp_dir), mustWork = FALSE)

# Human TP53 gene fragment (Query)
writeLines(c(
  ">Human_p53_fragment\nATGGAGGAGCCGCAGTCAGATCCTAGCGTCGAGCCCCCTCTGAGTCAGGAAACATTTTCA"
), query_fasta)
# Mutant TP53 gene fragment (Subject)
writeLines(c(
  ">Variant_p53_fragment\nATGGAGGAGCCGCAGTCAGATCCTAGCCTCGAGCCCCCTCTGAGTCAGGAAACATTTTCA"
), subject_fasta)

# Specify an output path
out_parquet <- normalizePath(tempfile(fileext = ".parquet"), mustWork = FALSE)
out_ipc <- normalizePath(tempfile(fileext = ".ipc"), mustWork = FALSE)
out_csv <- normalizePath(tempfile(fileext = ".csv"), mustWork = FALSE)

# Perform the search and write straight to an Arrow Parquet/IPC/CSV file
# We don't need it in RAM, we are writing to disk
QuickBLAST::BLAST2Files(
  blastn_inst, 
  query = query_fasta, 
  subject = subject_fasta,
  out_file = out_parquet,
  out_format = "parquet",
  return_values = FALSE,
  verbose = T
)
#>  Writing to : /tmp/Rtmp3qYeLB/file816f4c6cb054.parquet
#>  Output Format : parquet
#>  Num Threads: 2
#>  Total Records (Q + S): 2 (1 + 1)
#>  Batch Size: 3
#>  Computing: [========================================] 100% (done)                         
#>  Done writing to file.
#>  Total Records Processed: 5
#>  Clock : 0.0502599 seconds
#>  [1] TRUE

QuickBLAST::BLAST2Files(
  blastn_inst, 
  query = query_fasta, 
  subject = subject_fasta,
  out_file = out_ipc,
  out_format = "ipc",
  return_values = FALSE
)
#>  Writing to : /tmp/Rtmp3qYeLB/file816f6954b66c.ipc
#>  Output Format : ipc
#>  Num Threads: 2
#>  Total Records (Q + S): 2 (1 + 1)
#>  Batch Size: 3
#>  Computing: [========================================] 100% (done)                         
#>  Done writing to file.
#>  Total Records Processed: 5
#>  Clock : 0.050232 seconds
#>  [1] TRUE

QuickBLAST::BLAST2Files(
  blastn_inst, 
  query = query_fasta, 
  subject = subject_fasta,
  out_file = out_csv,
  out_format = "csv",
  return_values = FALSE
)
#>  Writing to : /tmp/Rtmp3qYeLB/file816f18886e6b.csv
#>  Output Format : csv
#>  Num Threads: 2
#>  Total Records (Q + S): 2 (1 + 1)
#>  Batch Size: 3
#>  Computing: [========================================] 100% (done)                         
#>  Done writing to file.
#>  Total Records Processed: 5
#>  Clock : 0.0502573 seconds
#>  [1] TRUE

# Load the hits back into an R data.frame natively via QuickBLAST
hits_df <- QuickBLAST::LoadBLASTHits(out_parquet, format = "parquet")
head(hits_df)
#>  # A tibble: 6 × 2
#>    seq_info$num_alignments $seqids$qseqid  $$sseqid $seqs$qseq $strands hsps$qhsp
#>                      <int> <chr>           <chr>    <chr>      <chr>    <chr>    
#>  1                       1 Human_p53_frag… Variant… ""         +/+      ""       
#>  2                       1 Human_p53_frag… Variant… ""         +/+      ""       
#>  3                       1 Human_p53_frag… Variant… ""         +/+      ""       
#>  4                       1 Human_p53_frag… Variant… ""         +/+      ""       
#>  5                       1 Human_p53_frag… Variant… ""         +/+      ""       
#>  6                       1 Human_p53_frag… Variant… ""         +/+      ""       
#>  # ℹ 32 more variables: seq_info$seqs$sseq <chr>, seq_info$lengths <tibble[,2]>,
#>  #   hsps$shsp <chr>, $pident <dbl>, $pident_gap <dbl>, $frames <chr>,
#>  #   $evalue <dbl>, $length <int>, $length01 <dbl>, $qstart <int>, $qend <int>,
#>  #   $sstart <int>, $send <int>, $bitscore <dbl>, $score <dbl>, $qcovhsp <dbl>,
#>  #   $blast_score <dbl>, $gaps <int>, $nident <int>, $mismatch <int>,
#>  #   $positive <int>, $n_splices <int>, $hsp_num <int>, $sum_evalue <dbl>,
#>  #   $product_coverage <dbl>, $overall_identity <dbl>, $negative_count <int>, …

hits_df <- QuickBLAST::LoadBLASTHits(out_ipc, format = "ipc")
head(hits_df)
#>  # A tibble: 6 × 39
#>    num_alignments seqids_qseqid      seqids_sseqid    seqs_qseq seqs_sseq strands
#>             <int> <chr>              <chr>            <chr>     <chr>     <chr>  
#>  1              1 Human_p53_fragment Variant_p53_fra… ""        ""        +/+    
#>  2              1 Human_p53_fragment Variant_p53_fra… ""        ""        +/+    
#>  3              1 Human_p53_fragment Variant_p53_fra… ""        ""        +/+    
#>  4              1 Human_p53_fragment Variant_p53_fra… ""        ""        +/+    
#>  5              1 Human_p53_fragment Variant_p53_fra… ""        ""        +/+    
#>  6              1 Human_p53_fragment Variant_p53_fra… ""        ""        +/+    
#>  # ℹ 33 more variables: lengths_qlen <int>, lengths_slen <int>, qhsp <chr>,
#>  #   shsp <chr>, pident <dbl>, pident_gap <dbl>, frames <chr>, evalue <dbl>,
#>  #   length <int>, length01 <dbl>, qstart <int>, qend <int>, sstart <int>,
#>  #   send <int>, bitscore <dbl>, score <dbl>, qcovhsp <dbl>, blast_score <dbl>,
#>  #   gaps <int>, nident <int>, mismatch <int>, positive <int>, n_splices <int>,
#>  #   hsp_num <int>, sum_evalue <dbl>, product_coverage <dbl>,
#>  #   overall_identity <dbl>, negative_count <int>, matches <dbl>, …

hits_df <- QuickBLAST::LoadBLASTHits(out_csv, format = "csv", sep = "\t", header = T)
head(hits_df)
#>  # A tibble: 6 × 39
#>    seq_info.num_alignments seq_info.seqids.qseqid seq_info.seqids.sseqid
#>                      <int> <chr>                  <chr>                 
#>  1                       1 Human_p53_fragment     Variant_p53_fragment  
#>  2                       1 Human_p53_fragment     Variant_p53_fragment  
#>  3                       1 Human_p53_fragment     Variant_p53_fragment  
#>  4                       1 Human_p53_fragment     Variant_p53_fragment  
#>  5                       1 Human_p53_fragment     Variant_p53_fragment  
#>  6                       1 Human_p53_fragment     Variant_p53_fragment  
#>  # ℹ 36 more variables: seq_info.seqs.qseq <???>, seq_info.seqs.sseq <???>,
#>  #   seq_info.strands <chr>, seq_info.lengths.qlen <int>,
#>  #   seq_info.lengths.slen <int>, hsps.qhsp <???>, hsps.shsp <???>,
#>  #   hsps.pident <dbl>, hsps.pident_gap <dbl>, hsps.frames <chr>,
#>  #   hsps.evalue <dbl>, hsps.length <int>, hsps.length01 <int>,
#>  #   hsps.qstart <int>, hsps.qend <int>, hsps.sstart <int>, hsps.send <int>,
#>  #   hsps.bitscore <dbl>, hsps.score <int>, hsps.qcovhsp <int>, …

Conclusion

QuickBLAST modernizes sequence alignment in R. By managing your instances appropriately, keeping data in native C++ structures, and offloading massive search results to Apache Arrow formats, you can run high-throughput bioinformatic pipelines natively without relying on slow Sys.Call() bash wrappers.