Introduction to DNABarcodeCompatibility

This document gives an overview of the DNABarcodeCompatibility R package with a brief description of the set of tools that it contains. The package includes six main functions that are briefly described below with examples. These functions allow one to load a list of DNA barcodes (such as the Illumina TruSeq small RNA kits), to filter these barcodes according to distance and nucleotide content criteria, to generate sets of compatible barcode combinations out of the filtered barcode list, and finally to generate an optimized selection of barcode combinations for multiplex sequencing experiments. In particular, the package provides an optimizer function to favour the selection of compatible barcode combinations with least heterogeneity in the frequencies of DNA barcodes, and allows one to keep barcodes that are robust against substitution and insertion/deletion errors, thereby facilitating the demultiplexing step.

The DNABarcodeCompatibility package also contains:

  • one workflow called experiment_design() allowing one to perform all steps in one go.
  • two data sets called IlluminaIndexesRaw and IlluminaIndexes for running and testing examples.
  • a series of API to build your own workflow.

The package deals with the three existing sequencing-by-synthesis chemistries from Illumina:

  • Four-Channel SBS Chemistry: MiSeq, HiSeq systems
  • Two-Channel SBS Chemistry: MiniSeq, NextSeq, NovaSeq systems
  • One-Channel SBS Chemistry: iSeq system

Load the package

library("DNABarcodeCompatibility")

Define a helper function to save the raw dataset as a temporary text file

# This function is created for the purpose of the documentation 
export_dataset_to_file = 
    function(dataset = DNABarcodeCompatibility::IlluminaIndexesRaw) {
        if ("data.frame" %in% is(dataset)) {
            write.table(dataset,
                        textfile <- tempfile(),
                        row.names = FALSE, col.names = FALSE, quote=FALSE)
            return(textfile)
        } else print(paste("The input dataset isn't a data.frame:",
                            "NOT exported into file"))
    }

Design an experiment

The function experiment_design() uses a Shannon-entropy maximization approach to identify a set of compatible barcode combinations in which the frequencies of occurrences of the various DNA barcodes are as uniform as possible. The optimization can be performed in the contexts of single and dual barcoding. It performs either an exhaustive or a random search of compatible DNA-barcode combinations, depending on the size of the DNA-barcode set used, and on the number of samples to be multiplexed.

Examples for single indexing

  • 12 libraries sequenced in multiplex of 3 on a HiSeq (4 channels) platform
txtfile <- export_dataset_to_file (
    dataset = DNABarcodeCompatibility::IlluminaIndexesRaw
)
experiment_design(file1=txtfile,
                    sample_number=12,
                    mplex_level=3,
                    platform=4)
## [1] "Theoretical max entropy: 2.48491"
## [1] "Entropy of the optimized set: 2.48491"
##    sample Lane    Id sequence
## 1       1    1 RPI04   TGACCA
## 2       2    1 RPI05   ACAGTG
## 3       3    1 RPI37   CGGAAT
## 4       4    2 RPI27   ATTCCT
## 5       5    2 RPI33   CAGGCG
## 6       6    2 RPI43   TACAGC
## 7       7    3 RPI03   TTAGGC
## 8       8    3 RPI09   GATCAG
## 9       9    3 RPI13   AGTCAA
## 10     10    4 RPI06   GCCAAT
## 11     11    4 RPI08   ACTTGA
## 12     12    4 RPI12   CTTGTA
  • 12 libraries sequenced in multiplex of 3 on a NextSeq (2 channels) platform
txtfile <- export_dataset_to_file (
    dataset = DNABarcodeCompatibility::IlluminaIndexesRaw
)
experiment_design(file1=txtfile,
                    sample_number=12,
                    mplex_level=3,
                    platform=2)
## [1] "Theoretical max entropy: 2.48491"
## [1] "Entropy of the optimized set: 2.48491"
##    sample Lane    Id sequence
## 1       1    1 RPI05   ACAGTG
## 2       2    1 RPI41   GACGAC
## 3       3    1 RPI44   TATAAT
## 4       4    2 RPI21   GTTTCG
## 5       5    2 RPI35   CATTTT
## 6       6    2 RPI43   TACAGC
## 7       7    3 RPI07   CAGATC
## 8       8    3 RPI13   AGTCAA
## 9       9    3 RPI28   CAAAAG
## 10     10    4 RPI22   CGTACG
## 11     11    4 RPI27   ATTCCT
## 12     12    4 RPI45   TCATTC
  • 12 libraries sequenced in multiplex of 3 on a iSeq (1 channels) platform
txtfile <- export_dataset_to_file (
    dataset = DNABarcodeCompatibility::IlluminaIndexesRaw
)
experiment_design(file1=txtfile,
                    sample_number=12,
                    mplex_level=3,
                    platform=1)
## [1] "Theoretical max entropy: 2.48491"
## [1] "Entropy of the optimized set: 2.48491"
##    sample Lane    Id sequence
## 1       1    1 RPI15   ATGTCA
## 2       2    1 RPI34   CATGGC
## 3       3    1 RPI44   TATAAT
## 4       4    2 RPI04   TGACCA
## 5       5    2 RPI21   GTTTCG
## 6       6    2 RPI37   CGGAAT
## 7       7    3 RPI11   GGCTAC
## 8       8    3 RPI26   ATGAGC
## 9       9    3 RPI29   CAACTA
## 10     10    4 RPI13   AGTCAA
## 11     11    4 RPI35   CATTTT
## 12     12    4 RPI38   CTAGCT
  • 12 libraries sequenced in multiplex of 3 on a HiSeq platform using barcodes robust against 1 substitution error
txtfile <- export_dataset_to_file (
    dataset = DNABarcodeCompatibility::IlluminaIndexesRaw
)
experiment_design(file1=txtfile,
                sample_number=12,
                mplex_level=3,
                platform=4,
                metric = "hamming",
                d = 3)
## [1] "Theoretical max entropy: 2.48491"
## [1] "Entropy of the optimized set: 2.48491"
##    sample Lane    Id sequence
## 1       1    1 RPI37   CGGAAT
## 2       2    1 RPI45   TCATTC
## 3       3    1 RPI46   TCCCGA
## 4       4    2 RPI08   ACTTGA
## 5       5    2 RPI24   GGTAGC
## 6       6    2 RPI42   TAATCG
## 7       7    3 RPI10   TAGCTT
## 8       8    3 RPI21   GTTTCG
## 9       9    3 RPI29   CAACTA
## 10     10    4 RPI09   GATCAG
## 11     11    4 RPI31   CACGAT
## 12     12    4 RPI40   CTCAGA

Examples for dual indexing

  • 12 libraries sequenced in multiplex of 3 on a HiSeq platform
# Select the first half of barcodes from the dataset
txtfile1 <- export_dataset_to_file (
    DNABarcodeCompatibility::IlluminaIndexesRaw[1:24,]
)

# Select the second half of barcodes from the dataset
txtfile2 <- export_dataset_to_file (
    DNABarcodeCompatibility::IlluminaIndexesRaw[25:48,]
)

# Get compatibles combinations of least redundant barcodes
experiment_design(file1=txtfile1,
                sample_number=12,
                mplex_level=3,
                platform=4,
                file2=txtfile2)
## [1] "Theoretical max entropy: 2.48491"
## [1] "Entropy of the optimized set: 2.48491"
##       Id Lane
## 1  RPI02    1
## 2  RPI16    1
## 3  RPI19    1
## 4  RPI01    2
## 5  RPI07    2
## 6  RPI17    2
## 7  RPI04    3
## 8  RPI06    3
## 9  RPI12    3
## 10 RPI05    4
## 11 RPI11    4
## 12 RPI24    4
## [1] "Theoretical max entropy: 2.48491"
## [1] "Entropy of the optimized set: 2.48491"
##       Id Lane
## 1  RPI26    1
## 2  RPI38    1
## 3  RPI44    1
## 4  RPI31    2
## 5  RPI37    2
## 6  RPI43    2
## 7  RPI27    3
## 8  RPI34    3
## 9  RPI41    3
## 10 RPI40    4
## 11 RPI45    4
## 12 RPI47    4
##       Id Lane sequence
## 1  RPI02    1   CGATGT
## 2  RPI16    1   CCGTCC
## 3  RPI19    1   GTGAAA
## 4  RPI01    2   ATCACG
## 5  RPI07    2   CAGATC
## 6  RPI17    2   GTAGAG
## 7  RPI04    3   TGACCA
## 8  RPI06    3   GCCAAT
## 9  RPI12    3   CTTGTA
## 10 RPI05    4   ACAGTG
## 11 RPI11    4   GGCTAC
## 12 RPI24    4   GGTAGC
##       Id Lane sequence
## 1  RPI26    1   ATGAGC
## 2  RPI38    1   CTAGCT
## 3  RPI44    1   TATAAT
## 4  RPI31    2   CACGAT
## 5  RPI37    2   CGGAAT
## 6  RPI43    2   TACAGC
## 7  RPI27    3   ATTCCT
## 8  RPI34    3   CATGGC
## 9  RPI41    3   GACGAC
## 10 RPI40    4   CTCAGA
## 11 RPI45    4   TCATTC
## 12 RPI47    4   TCGAAG
##    sample Lane   Id1 sequence1   Id2 sequence2
## 1       1    1 RPI02    CGATGT RPI26    ATGAGC
## 2       2    1 RPI16    CCGTCC RPI38    CTAGCT
## 3       3    1 RPI19    GTGAAA RPI44    TATAAT
## 4       4    2 RPI01    ATCACG RPI31    CACGAT
## 5       5    2 RPI07    CAGATC RPI37    CGGAAT
## 6       6    2 RPI17    GTAGAG RPI43    TACAGC
## 7       7    3 RPI04    TGACCA RPI27    ATTCCT
## 8       8    3 RPI06    GCCAAT RPI34    CATGGC
## 9       9    3 RPI12    CTTGTA RPI41    GACGAC
## 10     10    4 RPI05    ACAGTG RPI40    CTCAGA
## 11     11    4 RPI11    GGCTAC RPI45    TCATTC
## 12     12    4 RPI24    GGTAGC RPI47    TCGAAG
  • 12 libraries sequenced in multiplex of 3 on a HiSeq platform using barcodes robust against 1 substitution error
# Select the first half of barcodes from the dataset
txtfile1 <- export_dataset_to_file (
    DNABarcodeCompatibility::IlluminaIndexesRaw[1:24,]
)

# Select the second half of barcodes from the dataset
txtfile2 <- export_dataset_to_file (
    DNABarcodeCompatibility::IlluminaIndexesRaw[25:48,]
)

# Get compatibles combinations of least redundant barcodes
experiment_design(file1=txtfile1, sample_number=12, mplex_level=3, platform=4,
                    file2=txtfile2, metric="hamming", d=3)
## [1] "Theoretical max entropy: 2.48491"
## [1] "Entropy of the optimized set: 2.48491"
##       Id Lane
## 1  RPI07    1
## 2  RPI17    1
## 3  RPI23    1
## 4  RPI03    2
## 5  RPI06    2
## 6  RPI16    2
## 7  RPI02    3
## 8  RPI09    3
## 9  RPI12    3
## 10 RPI01    4
## 11 RPI08    4
## 12 RPI10    4
## [1] "Theoretical max entropy: 2.48491"
## [1] "Entropy of the optimized set: 2.48491"
##       Id Lane
## 1  RPI39    1
## 2  RPI45    1
## 3  RPI47    1
## 4  RPI26    2
## 5  RPI31    2
## 6  RPI43    2
## 7  RPI27    3
## 8  RPI38    3
## 9  RPI46    3
## 10 RPI37    4
## 11 RPI40    4
## 12 RPI41    4
##       Id Lane sequence
## 1  RPI07    1   CAGATC
## 2  RPI17    1   GTAGAG
## 3  RPI23    1   GAGTGG
## 4  RPI03    2   TTAGGC
## 5  RPI06    2   GCCAAT
## 6  RPI16    2   CCGTCC
## 7  RPI02    3   CGATGT
## 8  RPI09    3   GATCAG
## 9  RPI12    3   CTTGTA
## 10 RPI01    4   ATCACG
## 11 RPI08    4   ACTTGA
## 12 RPI10    4   TAGCTT
##       Id Lane sequence
## 1  RPI39    1   CTATAC
## 2  RPI45    1   TCATTC
## 3  RPI47    1   TCGAAG
## 4  RPI26    2   ATGAGC
## 5  RPI31    2   CACGAT
## 6  RPI43    2   TACAGC
## 7  RPI27    3   ATTCCT
## 8  RPI38    3   CTAGCT
## 9  RPI46    3   TCCCGA
## 10 RPI37    4   CGGAAT
## 11 RPI40    4   CTCAGA
## 12 RPI41    4   GACGAC
##    sample Lane   Id1 sequence1   Id2 sequence2
## 1       1    1 RPI07    CAGATC RPI39    CTATAC
## 2       2    1 RPI17    GTAGAG RPI45    TCATTC
## 3       3    1 RPI23    GAGTGG RPI47    TCGAAG
## 4       4    2 RPI03    TTAGGC RPI26    ATGAGC
## 5       5    2 RPI06    GCCAAT RPI31    CACGAT
## 6       6    2 RPI16    CCGTCC RPI43    TACAGC
## 7       7    3 RPI02    CGATGT RPI27    ATTCCT
## 8       8    3 RPI09    GATCAG RPI38    CTAGCT
## 9       9    3 RPI12    CTTGTA RPI46    TCCCGA
## 10     10    4 RPI01    ATCACG RPI37    CGGAAT
## 11     11    4 RPI08    ACTTGA RPI40    CTCAGA
## 12     12    4 RPI10    TAGCTT RPI41    GACGAC

Build your own workflow

This section guides you through the detailed API of the package with the aim to help you build your own workflow. The package is designed to be flexible and should be easily adaptable to most experimental contexts, using the experiment_design() function as a template, or building your own workflow from scratch.

Load and check a dataset of barcodes

The file_loading_and_checking() function loads the file containing the DNA barcodes set and analyzes its content. In particular, it checks that each barcode in the set is unique and uniquely identified (removing any repetition that occurs). It also checks the homogeneity of size of the barcodes, calculates their GC content and detects the presence of homopolymers of length >= 3.

file_loading_and_checking(
    file = export_dataset_to_file(
        dataset = DNABarcodeCompatibility::IlluminaIndexesRaw
    )
)
##       Id sequence GC_content homopolymer
## 1  RPI01   ATCACG      50.00       FALSE
## 2  RPI02   CGATGT      50.00       FALSE
## 3  RPI03   TTAGGC      50.00       FALSE
## 4  RPI04   TGACCA      50.00       FALSE
## 5  RPI05   ACAGTG      50.00       FALSE
## 6  RPI06   GCCAAT      50.00       FALSE
## 7  RPI07   CAGATC      50.00       FALSE
## 8  RPI08   ACTTGA      33.33       FALSE
## 9  RPI09   GATCAG      50.00       FALSE
## 10 RPI10   TAGCTT      33.33       FALSE
## 11 RPI11   GGCTAC      66.67       FALSE
## 12 RPI12   CTTGTA      33.33       FALSE
## 13 RPI13   AGTCAA      33.33       FALSE
## 14 RPI14   AGTTCC      50.00       FALSE
## 15 RPI15   ATGTCA      33.33       FALSE
## 16 RPI16   CCGTCC      83.33       FALSE
## 17 RPI17   GTAGAG      50.00       FALSE
## 18 RPI18   GTCCGC      83.33       FALSE
## 19 RPI19   GTGAAA      33.33        TRUE
## 20 RPI20   GTGGCC      83.33       FALSE
## 21 RPI21   GTTTCG      50.00        TRUE
## 22 RPI22   CGTACG      66.67       FALSE
## 23 RPI23   GAGTGG      66.67       FALSE
## 24 RPI24   GGTAGC      66.67       FALSE
## 25 RPI25   ACTGAT      33.33       FALSE
## 26 RPI26   ATGAGC      50.00       FALSE
## 27 RPI27   ATTCCT      33.33       FALSE
## 28 RPI28   CAAAAG      33.33        TRUE
## 29 RPI29   CAACTA      33.33       FALSE
## 30 RPI30   CACCGG      83.33       FALSE
## 31 RPI31   CACGAT      50.00       FALSE
## 32 RPI32   CACTCA      50.00       FALSE
## 33 RPI33   CAGGCG      83.33       FALSE
## 34 RPI34   CATGGC      66.67       FALSE
## 35 RPI35   CATTTT      16.67        TRUE
## 36 RPI36   CCAACA      50.00       FALSE
## 37 RPI37   CGGAAT      50.00       FALSE
## 38 RPI38   CTAGCT      50.00       FALSE
## 39 RPI39   CTATAC      33.33       FALSE
## 40 RPI40   CTCAGA      50.00       FALSE
## 41 RPI41   GACGAC      66.67       FALSE
## 42 RPI42   TAATCG      33.33       FALSE
## 43 RPI43   TACAGC      50.00       FALSE
## 44 RPI44   TATAAT       0.00       FALSE
## 45 RPI45   TCATTC      33.33       FALSE
## 46 RPI46   TCCCGA      66.67        TRUE
## 47 RPI47   TCGAAG      50.00       FALSE
## 48 RPI48   TCGGCA      66.67       FALSE

Examples of an exhaustive search of compatible barcode combinations

The total number of combinations depends on the number of available barcodes and of the multiplex level. For 48 barcodes and a multiplex level of 3, the total number of combinations (compatible or not) can be calculated using choose(48,3), which gives 17296 combinations. In many cases the total number of combinations can become much larger (even gigantic), and one cannot perform an exhaustive search (see get_random_combinations() below).

  • 48 barcodes, multiplex level of 2, HiSeq platform
# Total number of combinations
choose(48,2)
## [1] 1128

# Load barcodes
barcodes <- DNABarcodeCompatibility::IlluminaIndexes

# Time for an exhaustive search
system.time(m <- get_all_combinations(index_df = barcodes,
                                    mplex_level = 2,
                                    platform = 4))
##    user  system elapsed 
##   0.225   0.000   0.224

# Each line represents a compatible combination of barcodes
head(m)
##      [,1]    [,2]   
## [1,] "RPI04" "RPI35"
## [2,] "RPI05" "RPI19"
## [3,] "RPI06" "RPI12"
## [4,] "RPI07" "RPI17"
## [5,] "RPI10" "RPI39"
## [6,] "RPI18" "RPI25"
  • 48 barcodes, multiplex level of 3, HiSeq platform
# Total number of combinations
choose(48,3)
## [1] 17296

# Load barcodes
barcodes <- DNABarcodeCompatibility::IlluminaIndexes

# Time for an exhaustive search
system.time(m <- get_all_combinations(index_df = barcodes,
                                    mplex_level = 3,
                                    platform = 4))
##    user  system elapsed 
##   4.610   0.000   4.611

# Each line represents a compatible combination of barcodes
head(m)
##      [,1]    [,2]    [,3]   
## [1,] "RPI01" "RPI02" "RPI48"
## [2,] "RPI01" "RPI03" "RPI07"
## [3,] "RPI01" "RPI03" "RPI08"
## [4,] "RPI01" "RPI03" "RPI09"
## [5,] "RPI01" "RPI03" "RPI10"
## [6,] "RPI01" "RPI03" "RPI16"

Examples of a random search of compatible barcode combinations

When the total number of combinations is too high, it is recommended to pick combinations at random and then select those that are compatible.

  • 48 barcodes, multiplex level of 3, HiSeq platform
# Total number of combinations
choose(48,3)
## [1] 17296

# Load barcodes
barcodes <- DNABarcodeCompatibility::IlluminaIndexes

# Time for a random search
system.time(m <- get_random_combinations(index_df = barcodes,
                                        mplex_level = 2,
                                        platform = 4))
##    user  system elapsed 
##   0.157   0.000   0.157

# Each line represents a compatible combination of barcodes
head(m)
##      [,1]    [,2]   
## [1,] "RPI04" "RPI35"
## [2,] "RPI07" "RPI17"
## [3,] "RPI10" "RPI39"
## [4,] "RPI20" "RPI30"
## [5,] "RPI24" "RPI31"
## [6,] "RPI27" "RPI45"
  • 48 barcodes, multiplex level of 4, HiSeq platform
# Total number of combinations
choose(48,4)
## [1] 194580

# Load barcodes
barcodes <- DNABarcodeCompatibility::IlluminaIndexes

# Time for a random search
system.time(m <- get_random_combinations(index_df = barcodes,
                                        mplex_level = 4,
                                        platform = 4))
##    user  system elapsed 
##    0.74    0.00    0.74

# Each line represents a compatible combination of barcodes
head(m)
##      [,1]    [,2]    [,3]    [,4]   
## [1,] "RPI01" "RPI32" "RPI34" "RPI43"
## [2,] "RPI01" "RPI02" "RPI29" "RPI48"
## [3,] "RPI01" "RPI14" "RPI18" "RPI35"
## [4,] "RPI01" "RPI22" "RPI45" "RPI46"
## [5,] "RPI01" "RPI04" "RPI23" "RPI33"
## [6,] "RPI01" "RPI12" "RPI35" "RPI41"
  • 48 barcodes, multiplex level of 6, HiSeq platform
# Total number of combinations
choose(48,6)
## [1] 12271512

# Load barcodes
barcodes <- DNABarcodeCompatibility::IlluminaIndexes

# Time for a random search
system.time(m <- get_random_combinations(index_df = barcodes,
                                        mplex_level = 6,
                                        platform = 4))
##    user  system elapsed 
##   1.208   0.000   1.207

# Each line represents a compatible combination of barcodes
head(m)
##      [,1]    [,2]    [,3]    [,4]    [,5]    [,6]   
## [1,] "RPI01" "RPI04" "RPI12" "RPI22" "RPI40" "RPI43"
## [2,] "RPI01" "RPI05" "RPI27" "RPI32" "RPI36" "RPI46"
## [3,] "RPI01" "RPI06" "RPI18" "RPI27" "RPI33" "RPI39"
## [4,] "RPI01" "RPI16" "RPI24" "RPI32" "RPI40" "RPI46"
## [5,] "RPI01" "RPI11" "RPI12" "RPI14" "RPI23" "RPI41"
## [6,] "RPI01" "RPI09" "RPI11" "RPI23" "RPI29" "RPI30"

Constrain barcodes to be robust against one substitution error

# Load barcodes
barcodes <- DNABarcodeCompatibility::IlluminaIndexes

# Perform a random search of compatible combinations
m <- get_random_combinations(index_df = barcodes,
                            mplex_level = 3,
                            platform = 4)

# Keep barcodes that are robust against one substitution error
filtered_m <- distance_filter(index_df = barcodes,
                            combinations_m = m,
                            metric = "hamming",
                            d = 3)

# Each line represents a compatible combination of barcodes
head(filtered_m)
##      V1      V2      V3     
## [1,] "RPI01" "RPI14" "RPI46"
## [2,] "RPI01" "RPI06" "RPI08"
## [3,] "RPI01" "RPI07" "RPI48"
## [4,] "RPI01" "RPI08" "RPI42"
## [5,] "RPI01" "RPI24" "RPI42"
## [6,] "RPI01" "RPI23" "RPI48"

Optimize the set of compatible combinations to reduce barcode redundancy

# Keep set of compatible barcodes that are robust against one substitution
# error
filtered_m <- distance_filter(
    index_df = DNABarcodeCompatibility::IlluminaIndexes,
    combinations_m = get_random_combinations(index_df = barcodes,
                                            mplex_level = 3,
                                            platform = 4),
    metric = "hamming", d = 3)

# Use a Shannon-entropy maximization approach to reduce barcode redundancy
df <- optimize_combinations(combination_m = filtered_m,
                            nb_lane = 12,
                            index_number = 48)
## [1] "Theoretical max entropy: 3.58352"
## [1] "Entropy of the optimized set: 3.58352"

# Each line represents a compatible combination of barcodes and each row a lane
# of the flow cell
df
##       V1      V2      V3     
##  [1,] "RPI08" "RPI37" "RPI41"
##  [2,] "RPI05" "RPI19" "RPI46"
##  [3,] "RPI27" "RPI30" "RPI48"
##  [4,] "RPI17" "RPI24" "RPI36"
##  [5,] "RPI03" "RPI22" "RPI29"
##  [6,] "RPI20" "RPI26" "RPI28"
##  [7,] "RPI14" "RPI23" "RPI43"
##  [8,] "RPI10" "RPI39" "RPI44"
##  [9,] "RPI04" "RPI06" "RPI35"
## [10,] "RPI07" "RPI18" "RPI21"
## [11,] "RPI02" "RPI11" "RPI47"
## [12,] "RPI09" "RPI38" "RPI45"

The optimized result isn’t an optimum when filtering out too many barcodes

  • Increased distance between barcode sequences: redundancy may become inevitable
# Keep set of compatible barcodes that are robust against multiple substitution
# and insertion/deletion errors
filtered_m <- distance_filter(
    index_df = DNABarcodeCompatibility::IlluminaIndexes,
    combinations_m = get_random_combinations(index_df = barcodes,
                                            mplex_level = 3,
                                            platform = 4),
    metric = "seqlev", d = 4)

# Use a Shannon-entropy maximization approach to reduce barcode redundancy
df <- optimize_combinations(combination_m = filtered_m,
                            nb_lane = 12,
                            index_number = 48)
## [1] "Theoretical max entropy: 3.58352"
## [1] "Entropy of the optimized set: 2.72932"

# Each line represents a compatible combination of barcodes and each row a
# lane of the flow cell
df
##       V1      V2      V3     
##  [1,] "RPI06" "RPI08" "RPI24"
##  [2,] "RPI17" "RPI27" "RPI29"
##  [3,] "RPI11" "RPI12" "RPI28"
##  [4,] "RPI22" "RPI35" "RPI46"
##  [5,] "RPI11" "RPI35" "RPI46"
##  [6,] "RPI11" "RPI12" "RPI28"
##  [7,] "RPI06" "RPI21" "RPI26"
##  [8,] "RPI24" "RPI35" "RPI36"
##  [9,] "RPI17" "RPI27" "RPI29"
## [10,] "RPI19" "RPI35" "RPI46"
## [11,] "RPI11" "RPI28" "RPI35"
## [12,] "RPI03" "RPI23" "RPI28"