Load an numpy structured array with data from the fixed fields of a VCF
    file (including INFO).

    Parameters
    ----------

    vcf_fn: string or list
        Name of the VCF file or list of file names.
    region: string, optional
        Region to extract, e.g., 'chr1' or 'chr1:0-100000'.
    fields: list or array-like, optional
        List of fields to extract from the VCF.
    exclude_fields: list or array-like, optional
        Fields to exclude from extraction.
    dtypes: dict or dict-like, optional
        Dictionary cotaining dtypes to use instead of the default inferred
        ones.
    arities: dict or dict-like, optional
        Dictionary containing field:integer mappings used to override the
        number of values to expect.
    fills: dict or dict-like, optional
        Dictionary containing field:fillvalue mappings used to override the
        defaults used for missing values.
    transformers: dict or dict-like, optional
        Dictionary containing field:function mappings used to preprocess
        any values prior to loading into array.
    vcf_types: dict or dict-like, optional
        Dictionary containing field:string mappings used to override any
        bogus type declarations in the VCF header (e.g., MQ0Fraction declared
        as Integer).
    count: int, optional
        Attempt to extract a specific number of records.
    progress: int, optional
        If greater than 0, log progress.
    logstream: file or file-like object, optional
        Stream to use for logging progress.
    condition: array, optional
        Boolean array defining which rows to load.
    slice_args: tuple or list, optional
        Slice of the underlying iterator, e.g., (0, 1000, 10) takes every
        10th row from the first 1000.
    flatten_filter: bool, optional
        Return FILTER as multiple boolean fields, e.g., FILTER_PASS,
        FILTER_LowQuality, etc.
    verbose: bool, optional
        Log more messages.
    cache: bool, optional
        If True, save the resulting numpy array to disk, and load from the
        cache if present rather than rebuilding from the VCF.
    cachedir: string, optional
        Manually specify the directory to use to store cache files.
    skip_cached: bool, optional
        If True and cache file is fresh, do not load and return None.
    compress_cache: bool, optional
        If True, compress the cache file.
    truncate: bool, optional
        If True (default) only include variants whose start position is within
        the given region. If False, use default tabix behaviour.

    Examples
    --------

    >>> from vcfnp import variants
    >>> v = variants('fixture/sample.vcf')
    >>> v
    array([ (b'19', 111, b'.', b'A', b'C', 9.600000381469727, (False, False, False), 2, True, 0, b'', 0, 0.0, 0, False, 0, False, 0),
           (b'19', 112, b'.', b'A', b'G', 10.0, (False, False, False), 2, True, 0, b'', 0, 0.0, 0, False, 0, False, 0),
           (b'20', 14370, b'rs6054257', b'G', b'A', 29.0, (False, False, True), 2, True, 0, b'', 0, 0.5, 0, True, 14, True, 3),
           (b'20', 17330, b'.', b'T', b'A', 3.0, (True, False, False), 2, True, 0, b'', 0, 0.016998291015625, 0, False, 11, False, 3),
           (b'20', 1110696, b'rs6040355', b'A', b'G', 67.0, (False, False, True), 3, True, 0, b'T', 0, 0.3330078125, 0, True, 10, False, 2),
           (b'20', 1230237, b'.', b'T', b'.', 47.0, (False, False, True), 2, False, 0, b'T', 0, 0.0, 0, False, 13, False, 3),
           (b'20', 1234567, b'microsat1', b'G', b'GA', 50.0, (False, False, True), 3, False, 1, b'G', 3, 0.0, 6, False, 9, False, 3),
           (b'20', 1235237, b'.', b'T', b'.', 0.0, (False, False, False), 2, False, 0, b'', 0, 0.0, 0, False, 0, False, 0),
           (b'X', 10, b'rsTest', b'AC', b'A', 10.0, (False, False, True), 3, False, -1, b'', 0, 0.0, 0, False, 0, False, 0)],
          dtype=[('CHROM', 'S12'), ('POS', '<i4'), ('ID', 'S12'), ('REF', 'S12'), ('ALT', 'S12'), ('QUAL', '<f4'), ('FILTER', [('q10', '?'), ('s50', '?'), ('PASS', '?')]), ('num_alleles', 'u1'), ('is_snp', '?'), ('svlen', '<i4'), ('AA', 'S12'), ('AC', '<u2'), ('AF', '<f2'), ('AN', '<u2'), ('DB', '?'), ('DP', '<i4'), ('H2', '?'), ('NS', '<i4')])
    >>> v['QUAL']
    array([  9.60000038,  10.        ,  29.        ,   3.        ,
            67.        ,  47.        ,  50.        ,   0.        ,  10.        ], dtype=float32)
    >>> v['FILTER']['PASS']
    array([False, False,  True, False,  True,  True,  True, False,  True], dtype=bool)
    >>> v['AF']
    array([ 0.        ,  0.        ,  0.5       ,  0.01699829,  0.33300781,
            0.        ,  0.        ,  0.        ,  0.        ], dtype=float16)

def variants(vcf_fn, region=None, fields=None, exclude_fields=None,
             dtypes=None, arities=None, fills=None, transformers=None,
             vcf_types=None, count=None, progress=0, logstream=None,
             condition=None, slice_args=None, flatten_filter=False,
             verbose=True, cache=False, cachedir=None, skip_cached=False,
             compress_cache=False, truncate=True):
    """
    Load an numpy structured array with data from the fixed fields of a VCF
    file (including INFO).

    Parameters
    ----------

    vcf_fn: string or list
        Name of the VCF file or list of file names.
    region: string, optional
        Region to extract, e.g., 'chr1' or 'chr1:0-100000'.
    fields: list or array-like, optional
        List of fields to extract from the VCF.
    exclude_fields: list or array-like, optional
        Fields to exclude from extraction.
    dtypes: dict or dict-like, optional
        Dictionary cotaining dtypes to use instead of the default inferred
        ones.
    arities: dict or dict-like, optional
        Dictionary containing field:integer mappings used to override the
        number of values to expect.
    fills: dict or dict-like, optional
        Dictionary containing field:fillvalue mappings used to override the
        defaults used for missing values.
    transformers: dict or dict-like, optional
        Dictionary containing field:function mappings used to preprocess
        any values prior to loading into array.
    vcf_types: dict or dict-like, optional
        Dictionary containing field:string mappings used to override any
        bogus type declarations in the VCF header (e.g., MQ0Fraction declared
        as Integer).
    count: int, optional
        Attempt to extract a specific number of records.
    progress: int, optional
        If greater than 0, log progress.
    logstream: file or file-like object, optional
        Stream to use for logging progress.
    condition: array, optional
        Boolean array defining which rows to load.
    slice_args: tuple or list, optional
        Slice of the underlying iterator, e.g., (0, 1000, 10) takes every
        10th row from the first 1000.
    flatten_filter: bool, optional
        Return FILTER as multiple boolean fields, e.g., FILTER_PASS,
        FILTER_LowQuality, etc.
    verbose: bool, optional
        Log more messages.
    cache: bool, optional
        If True, save the resulting numpy array to disk, and load from the
        cache if present rather than rebuilding from the VCF.
    cachedir: string, optional
        Manually specify the directory to use to store cache files.
    skip_cached: bool, optional
        If True and cache file is fresh, do not load and return None.
    compress_cache: bool, optional
        If True, compress the cache file.
    truncate: bool, optional
        If True (default) only include variants whose start position is within
        the given region. If False, use default tabix behaviour.

    Examples
    --------

    >>> from vcfnp import variants
    >>> v = variants('fixture/sample.vcf')
    >>> v
    array([ (b'19', 111, b'.', b'A', b'C', 9.600000381469727, (False, False, False), 2, True, 0, b'', 0, 0.0, 0, False, 0, False, 0),
           (b'19', 112, b'.', b'A', b'G', 10.0, (False, False, False), 2, True, 0, b'', 0, 0.0, 0, False, 0, False, 0),
           (b'20', 14370, b'rs6054257', b'G', b'A', 29.0, (False, False, True), 2, True, 0, b'', 0, 0.5, 0, True, 14, True, 3),
           (b'20', 17330, b'.', b'T', b'A', 3.0, (True, False, False), 2, True, 0, b'', 0, 0.016998291015625, 0, False, 11, False, 3),
           (b'20', 1110696, b'rs6040355', b'A', b'G', 67.0, (False, False, True), 3, True, 0, b'T', 0, 0.3330078125, 0, True, 10, False, 2),
           (b'20', 1230237, b'.', b'T', b'.', 47.0, (False, False, True), 2, False, 0, b'T', 0, 0.0, 0, False, 13, False, 3),
           (b'20', 1234567, b'microsat1', b'G', b'GA', 50.0, (False, False, True), 3, False, 1, b'G', 3, 0.0, 6, False, 9, False, 3),
           (b'20', 1235237, b'.', b'T', b'.', 0.0, (False, False, False), 2, False, 0, b'', 0, 0.0, 0, False, 0, False, 0),
           (b'X', 10, b'rsTest', b'AC', b'A', 10.0, (False, False, True), 3, False, -1, b'', 0, 0.0, 0, False, 0, False, 0)],
          dtype=[('CHROM', 'S12'), ('POS', '<i4'), ('ID', 'S12'), ('REF', 'S12'), ('ALT', 'S12'), ('QUAL', '<f4'), ('FILTER', [('q10', '?'), ('s50', '?'), ('PASS', '?')]), ('num_alleles', 'u1'), ('is_snp', '?'), ('svlen', '<i4'), ('AA', 'S12'), ('AC', '<u2'), ('AF', '<f2'), ('AN', '<u2'), ('DB', '?'), ('DP', '<i4'), ('H2', '?'), ('NS', '<i4')])
    >>> v['QUAL']
    array([  9.60000038,  10.        ,  29.        ,   3.        ,
            67.        ,  47.        ,  50.        ,   0.        ,  10.        ], dtype=float32)
    >>> v['FILTER']['PASS']
    array([False, False,  True, False,  True,  True,  True, False,  True], dtype=bool)
    >>> v['AF']
    array([ 0.        ,  0.        ,  0.5       ,  0.01699829,  0.33300781,
            0.        ,  0.        ,  0.        ,  0.        ], dtype=float16)

    """  # flake8: noqa

    loader = _VariantsLoader(vcf_fn, region=region, fields=fields,
                             exclude_fields=exclude_fields, dtypes=dtypes,
                             arities=arities, fills=fills,
                             transformers=transformers, vcf_types=vcf_types,
                             count=count, progress=progress,
                             logstream=logstream, condition=condition,
                             slice_args=slice_args,
                             flatten_filter=flatten_filter, verbose=verbose,
                             cache=cache, cachedir=cachedir,
                             skip_cached=skip_cached,
                             compress_cache=compress_cache, truncate=truncate)
    return loader.load()