<p>Probably it is better to use dedicated tools, however, here is a quick R workaround. It is relatively straightforward to remove the extra base (first letter of column 4) in the second type of output:</p>

<pre><code># just a workaround to encolose the second file in a data frame
# you can just read your file as a table using fread(), read.csv(), ...
test &lt;- data.frame(
  col1 = "chr1",
  col2 = 100,
  col3 = ".",
  col4 = "GAAA",
  col5 = "G",
  stringsAsFactors = FALSE
)

&gt; test
  col1 col2 col3 col4 col5
1 chr1  100    . GAAA    G

# remove the first letter in column 4
test<span class="math-container">$col4 &lt;- substring(test$</span>col4, 2)
# converting column 4 to "-"
test$col5 &lt;- "-"

&gt; test
  col1 col2 col3 col4 col5
1 chr1  100    .  AAA    -
</code></pre>

<p>EDIT:</p>

<p>Here is how it can be done with <code>awk</code> for speed and memory concerns: The logic is the same as above, remove the first letter in column 4 and then change column 5 to "-".</p>

<pre><code>awk '{<span class="math-container">$4 = substr($</span>4,2); print}' infile &gt; outfile
awk '{$5 = "-"; print}' outfile &gt; outfile2
</code></pre>

<p>infile</p>

<pre><code>chr1 100 . GAAA G
</code></pre>

<p>outfile</p>

<pre><code>chr1 100 . AAA G
</code></pre>

<p>outfile2</p>

<pre><code>chr1 100 . AAA -
</code></pre>

CC BY-SA 4.0
2019-08-19T11:59:08.840Z
Thanks but my real VCF is several GB in size so I don't think this will work.
Nils
CC BY-SA 4.0
2019-08-19T13:22:33.483Z
I have edited my answer so as to include a solution making use of awk.
haci
CC BY-SA 4.0
2019-08-19T10:12:42.853Z
2019-08-19T13:20:03.430Z
2019-08-19T13:20:03.430Z
haci
<p>Edited version of <a href="https://bioinformatics.stackexchange.com/users/7230/namshin-kim">Namshin's</a> code this can handle if REF or ALT is '-' ( these records in vcf file are observed when there is insertion/deletion).
Added documentation in the script to explain code block I have modified.
Hope this useful.
Tested example with GRCh37</p>
<pre><code>python fix_vcf.py -f genome.fa -o out_fixed.vcf input.vcf
</code></pre>
<p>example input.vcf:</p>
<pre><code>##fileformat=VCFv4.2
#CHROM  POS ID  REF ALT QUAL  FILTER  INFO
7 6027157 . - T 1 PASS  NS=1  GQ  10
9 98218616  . - C 1 PASS  NS=1  GQ  10
17  41245200  . AT  - 1 PASS  NS=1  GQ  10
13  32929213  . C - 1 PASS  NS=1  GQ  10

</code></pre>
<p>out_fixed.vcf:</p>
<pre><code>##fileformat=VCFv4.2
##FILTER=&lt;ID=PASS,Description=&quot;All filters passed&quot;&gt;
#CHROM  POS ID  REF ALT QUAL  FILTER  INFO
7 6027157 . T TT  1 PASS  NS=1
9 98218616  . A AC  1 PASS  NS=1
17  41245199  . GAT G 1 PASS  NS=1
13  32929212  . CC  C 1 PASS  NS=1
</code></pre>
<pre><code># -*- coding: utf-8 -*-
import argparse
import sys

import pysam


class Application:
    def __init__(self):
        self.args = Application.get_args()
        self.vcf = pysam.VariantFile(sys.stdin)
        self.fasta = pysam.FastaFile(self.args.fasta)

    @staticmethod
    def get_args() -&gt; argparse.Namespace:
        &quot;&quot;&quot;
        Read command line arguments and set input and output sources.

        :return: parsed command line arguments
        &quot;&quot;&quot;

        parser = argparse.ArgumentParser(prog=&quot;fixvcf.py&quot;, description=&quot;Fixes deletion represented by -&quot;)
        parser.add_argument('--version', action='version', version='%(prog)s 0.1')

        parser.add_argument(&quot;input&quot;, help=&quot;input file, use - to read from stdin&quot;)
        parser.add_argument(&quot;-f&quot;, &quot;--fasta&quot;, help=&quot;fasta reference file&quot;, required=True)
        parser.add_argument(&quot;-o&quot;, &quot;--output&quot;, help=&quot;output file&quot;)

        if len(sys.argv) == 1:
            parser.print_help(sys.stderr)
            sys.exit(1)

        args = parser.parse_args()

        if args.output:
            sys.stdout = open(args.output, &quot;w&quot;)

        if args.input != &quot;-&quot;:
            sys.stdin = open(args.input, &quot;r&quot;)

        return args

    def fix_record(self, record: pysam.libcbcf.VariantRecord) -&gt;     pysam.libcbcf.VariantRecord:
        &quot;&quot;&quot;
        If the ALT column of the records contains a `-`, the variant position is shift one position to the left.
        For reaching this the reference base for the position is determined and prepended to the current REF bases.
        The ALT value `-` is replaced with the determined ref base on position before. In case there are multiple ALT
        values the determined ref base is prepended to them.

        If the REF column of the records contains a `-`, the variant position will change.
        For reaching this the reference base for the position is determined and prepended to the current ALT bases.
        The REF value `-` is replaced with the determined ref base on position. In case there are multiple ALT
        values the determined ref base is prepended to them.

        :param record: vcf file record
        :return: fixed vcf record
        &quot;&quot;&quot;

        if &quot;-&quot; in record.alts:
            ref = self.fasta.fetch(reference=record.chrom, start=record.pos - 2, end=record.pos -1)

            record.pos = record.pos - 1
            record.ref = f&quot;{ref}{record.ref}&quot;
            record.alts = tuple(map(lambda alt: ref if alt == &quot;-&quot; else f&quot;{ref}{alt}&quot;, record.alts))

        if &quot;-&quot; in record.ref:
            ref = self.fasta.fetch(reference=record.chrom, start=record.pos - 1, end=record.pos)

            record.pos = record.pos
            record.ref = f&quot;{ref}&quot;
            record.alts = tuple(map(lambda alt: f&quot;{ref}{alt}&quot;, record.alts))

        return record

    def start(self):
        &quot;&quot;&quot;
        Print the header of the vcf file. Afterwards the fix_record method is applied to each record in the vcf file.

        For more information about python's map() see: 
https://docs.python.org/3.6/library/functions.html#map

        For more information about the unpacking mechanism using `*`see:
        https://docs.python.org/3/tutorial/controlflow.html#tut-unpacking-arguments
        &quot;&quot;&quot;

        print(self.vcf.header, end=&quot;&quot;)
        #print(*map(self.fix_record, self.vcf), sep=&quot;&quot;, end=&quot;&quot;)
        for line in self.vcf:
            print(self.fix_record(line), sep=&quot;&quot;, end=&quot;&quot;)

if __name__ == &quot;__main__&quot;:
    app = Application()
    app.start()
</code></pre>

CC BY-SA 4.0
2021-03-10T18:10:51.370Z
2021-03-11T12:42:28.637Z
2021-03-11T12:42:28.637Z
Shriram Bhosle
<p>One more thing. If empty ALT allele is not "-", you can change it such as "*", as follows.</p>

<pre><code>    if "*" in record.alts:
        ref = self.fasta.fetch(reference=record.chrom, start=record.pos - 1, end=record.pos)
        record.pos = record.pos - 1
        record.ref = f"{ref}{record.ref}"
        record.alts = tuple(map(lambda alt: ref if alt == "*" else f"{ref}{alt}", record.alts))
</code></pre>

CC BY-SA 4.0
2020-02-20T11:54:29.217Z
Hi Namshin, thanks for contributing, but this is not the way we usually answer questions on this website. You should not assume that a reader reads the whole thread. If it's an extension for a different answer, meaning which one and also explain what is the benefit of your extension. Note you can [[edit]] your answer.
Kamil S Jaron
CC BY-SA 4.0
2020-02-16T02:07:04.343Z
2020-02-16T02:07:04.343Z
Namshin Kim
<p>This is my first post and I will add some explanations. When I tried to use this script, <code>print(*map(self.fix_record, self.vcf), sep=&quot;&quot;, end=&quot;&quot;)</code>, it will increase memory usage because it will load everything into memory and then start to apply fix_record function. So, I just used two lines <code>for line in self.vcf: print(self.fix_record(line), sep=&quot;&quot;, end=&quot;&quot;)</code>. Because <code>pysam</code> uses generator by default, if we process one by one by pysam's <code>VariantFile</code> function, it will consume very little memory. In my case, the VCF file was more than 200GB, so I had to fix this script for my VCF file.</p>
<p>Second my VCF file used &quot;*&quot; for empty ALT, so I fixed two lines (1) <code>if &quot;*&quot; in record.alts:</code> (2) <code>record.alts = tuple(map(lambda alt: ref if alt == &quot;*&quot; else f&quot;{ref}{alt}&quot;, record.alts))</code></p>
<pre><code>#!/usr/bin/env python
# -*- coding: utf-8 -*-
import argparse
import sys

import pysam


class Application:
    def __init__(self):
        self.args = Application.get_args()
        self.vcf = pysam.VariantFile(sys.stdin)
        self.fasta = pysam.FastaFile(self.args.fasta)

    @staticmethod
    def get_args() -&gt; argparse.Namespace:
        &quot;&quot;&quot;
        Read command line arguments and set input and output sources.

        :return: parsed command line arguments
        &quot;&quot;&quot;

        parser = argparse.ArgumentParser(prog=&quot;fixvcf.py&quot;, description=&quot;Fixes deletion represented by -&quot;)
        parser.add_argument('--version', action='version', version='%(prog)s 0.1')

        parser.add_argument(&quot;input&quot;, help=&quot;input file, use - to read from stdin&quot;)
        parser.add_argument(&quot;-f&quot;, &quot;--fasta&quot;, help=&quot;fasta reference file&quot;, required=True)
        parser.add_argument(&quot;-o&quot;, &quot;--output&quot;, help=&quot;output file&quot;)

        if len(sys.argv) == 1:
            parser.print_help(sys.stderr)
            sys.exit(1)

        args = parser.parse_args()

        if args.output:
            sys.stdout = open(args.output, &quot;w&quot;)

        if args.input != &quot;-&quot;:
            sys.stdin = open(args.input, &quot;r&quot;)

        return args

    def fix_record(self, record: pysam.libcbcf.VariantRecord) -&gt;     pysam.libcbcf.VariantRecord:
        &quot;&quot;&quot;
        If the ALT column of the records contains a `-`, the variant position is shift one position to the left.
        For reaching this the reference base for the position is determined and prepended to the current REF bases.
        The ALT value `-` is replaced with the determined ref base on position before. In case there are multiple ALT
        values the determined ref base is prepended to them.

        :param record: vcf file record
        :return: fixed vcf record
        &quot;&quot;&quot;

        if &quot;*&quot; in record.alts:
            ref = self.fasta.fetch(reference=record.chrom, start=record.pos - 1, end=record.pos)

            record.pos = record.pos - 1
            record.ref = f&quot;{ref}{record.ref}&quot;
            record.alts = tuple(map(lambda alt: ref if alt == &quot;*&quot; else f&quot;{ref}{alt}&quot;, record.alts))

        return record

    def start(self):
        &quot;&quot;&quot;
        Print the header of the vcf file. Afterwards the fix_record method is applied to each record in the vcf file.

        For more information about python's map() see: 
https://docs.python.org/3.6/library/functions.html#map

        For more information about the unpacking mechanism using `*`see:
        https://docs.python.org/3/tutorial/controlflow.html#tut-unpacking-arguments
        &quot;&quot;&quot;

        print(self.vcf.header, end=&quot;&quot;)
        #print(*map(self.fix_record, self.vcf), sep=&quot;&quot;, end=&quot;&quot;)
        for line in self.vcf:
            print(self.fix_record(line), sep=&quot;&quot;, end=&quot;&quot;)

if __name__ == &quot;__main__&quot;:
    app = Application()
    app.start()
</code></pre>

CC BY-SA 4.0
2020-02-22T23:45:42.910Z
2020-07-09T18:19:35.523Z
2020-07-09T18:19:35.523Z
Namshin Kim
<p>According to the <a href="http://samtools.github.io/hts-specs/VCFv4.3.pdf" rel="nofollow noreferrer">specs</a> the ALT is not allowed to contain <code>-</code>. While technically supported, 'N' as the alternative allele is sometimes used unnecessarily and this script can also fix those cases if one exchanges the two &quot;-&quot; within the fix_record function for &quot;N&quot;.</p>
<p>Here is a little python (&gt;=3.6) script that is able to fix it. It makes use of the pysam module. You need a reference file and this fasta file must be indexed by <code>samtools faidx reference.fa</code>. <strike>Please note that it doesn't support multiple values in the ALT column for records that contain a <code>-</code>.</strike></p>
<p><strong>Usage:</strong></p>
<pre><code>python fixvcf.py -f reference.fasta -o out.vcf in.vcf
</code></pre>
<p><strong>The script:</strong></p>
<pre class="lang-py prettyprint-override"><code>#!/usr/bin/env python
# -*- coding: utf-8 -*-
import argparse
import sys

import pysam


class Application:
    def __init__(self):
        self.args = Application.get_args()
        self.vcf = pysam.VariantFile(sys.stdin)
        self.fasta = pysam.FastaFile(self.args.fasta)

    @staticmethod
    def get_args() -&gt; argparse.Namespace:
        &quot;&quot;&quot;
        Read command line arguments and set input and output sources.

        :return: parsed command line arguments
        &quot;&quot;&quot;
        parser = argparse.ArgumentParser(prog=&quot;fixvcf.py&quot;, description=&quot;Fixes deletion represented by -&quot;)
        parser.add_argument('--version', action='version', version='%(prog)s 0.1')

        parser.add_argument(&quot;input&quot;, help=&quot;input file, use - to read from stdin&quot;)
        parser.add_argument(&quot;-f&quot;, &quot;--fasta&quot;, help=&quot;fasta reference file&quot;, required=True)
        parser.add_argument(&quot;-o&quot;, &quot;--output&quot;, help=&quot;output file&quot;)

        if len(sys.argv) == 1:
            parser.print_help(sys.stderr)
            sys.exit(1)

        args = parser.parse_args()

        if args.output:
            sys.stdout = open(args.output, &quot;w&quot;)

        if args.input != &quot;-&quot;:
            sys.stdin = open(args.input, &quot;r&quot;)

        return args

    def fix_record(self, record: pysam.libcbcf.VariantRecord) -&gt; pysam.libcbcf.VariantRecord:
        &quot;&quot;&quot;
        If the ALT column of the records contains a `-`, the variant position is shift one position to the left.
        For reaching this the reference base for the position is determined and prepended to the current REF bases.
        The ALT value `-` is replaced with the determined ref base on position before. In case there are multiple ALT
        values the determined ref base is prepended to them.

        :param record: vcf file record
        :return: fixed vcf record
        &quot;&quot;&quot;
        if &quot;-&quot; in record.alts:
            ref = self.fasta.fetch(region=f&quot;{record.chrom}:{record.pos - 1}-{record.pos - 1}&quot;)    
            record.pos = record.pos - 1
            record.ref = f&quot;{ref}{record.ref}&quot;
            record.alts = tuple(map(lambda alt: ref if alt == &quot;-&quot; else f&quot;{ref}{alt}&quot;, record.alts))

        return record

    def start(self):
        &quot;&quot;&quot;
        Print the header of the vcf file. Afterwards the fix_record method is applied to each record in the vcf file.

        For more information about python's map() see: https://docs.python.org/3.6/library/functions.html#map

        For more information about the unpacking mechanism using `*`see:
        https://docs.python.org/3/tutorial/controlflow.html#tut-unpacking-arguments
        &quot;&quot;&quot;
        print(self.vcf.header, end=&quot;&quot;)
        print(*map(self.fix_record, self.vcf), sep=&quot;&quot;, end=&quot;&quot;)


if __name__ == &quot;__main__&quot;:
    app = Application()
    app.start()
</code></pre>
<p>A lot of the code above is boilerplate for running a script on the command line. The critical code for the transformation is in the <code>fix_record</code> function. The relevant code is below, expanded, with line numbers and a description of each command.</p>
<pre><code>[1]   ref = self.fasta.fetch(
          region=f&quot;{record.chrom}:{record.pos - 1}-{record.pos - 1}&quot;
      )
[2]   record.pos = record.pos - 1
[3]   record.ref = f&quot;{ref}{record.ref}&quot;
[4]   record.alts = tuple(
          map(
              lambda alt: ref if alt == &quot;-&quot; else f&quot;{ref}{alt}&quot;,
              record.alts
          )
      )
</code></pre>
<ol>
<li>This grabs the base preceding the inserted/deleted sequence.</li>
<li>This corrects the position of the variant to account for prepending another nucleotide.</li>
<li>This defines the new corrected REF allele: concatenate the base grabbed in [1] with the original REF allele</li>
<li>Apply an anonymous (lambda) function to each ALT allele; the function will replace <code>-</code> with the base grabbed in [1], or preprend the base to the allele if it is not <code>-</code></li>
</ol>

CC BY-SA 4.0
2019-08-20T14:59:58.700Z
+1, thanks for sharing a working script. It might be even more helpful for innocent bystanders to emphasize and explain the code that does the actual transformation.
Daniel Standage
CC BY-SA 4.0
2019-08-20T16:59:49.723Z
Done by adding type hints and doc strings :)
finswimmer
CC BY-SA 4.0
2019-08-20T17:16:15.120Z
Sorry, I should have been more clear. My edit is more of what I had in mind. :-)
Daniel Standage
CC BY-SA 4.0
2019-10-24T15:05:31.673Z
Thanks finswimmer for the script. It's been very helpful to me. I would like to extend it for the case of REF alleles with "-" as in: *chr1 6029086 - A* to: *chr1 6029086 CA C* I have made the attempt to do it by extend this solution for other cases but couldn't find the right way to do it for all of them. For example in: *chr1 1684347 - CCT* I would change it to (REF would change to "C"(the position grabbed)+ALT and ALT would change to the position grabbed("C") which worked in the previous example): *chr1 1684347 CCCT C* But it's wrong cause actually should be like: *chr1 1684347 C CCCT* How
Dagsbio
CC BY-SA 4.0
2020-02-16T01:15:00.510Z
Generator version of the python script. It won't consume your memmory at all. #print(*map(self.fix_record, self.vcf), sep="", end="") for line in self.vcf: print(self.fix_record(line), sep="", end="")
Namshin Kim
CC BY-SA 4.0
2019-08-20T04:29:47.187Z
2023-01-25T11:30:21.040Z
2023-01-25T11:30:21.040Z
finswimmer
<p>I have a VCF file that has deletions specified like this, with the REF allele containing only the deleted portion and the ALT allele just saying <code>-</code>:</p>

<pre><code>chr1 101 . AAA -
</code></pre>

<p>I would like to combine this with another VCF in which deletions are specified like this, with REF and ALT both extended by 1bp to the left to ensure neither is empty:</p>

<pre><code>chr1 100 . GAAA G
</code></pre>

<p>Is there a tool to convert from one of these representations to the other? I have already tried <code>vt normalize</code> and <code>bcftools norm</code> but neither of them seem to do this.</p>

<p>I don't really mind which representation I end up with although I think the second is the one more consistent with the spec.</p>

CC BY-SA 4.0
2019-08-19T13:45:58.670Z
Cross-posted on biostars: https://www.biostars.org/p/394866/
Ram RS
CC BY-SA 4.0
2019-08-19T13:49:03.437Z
OP, the first of the two representations is so outdated that most tools might error out. You're better off insisting on converting to format #2
Ram RS
CC BY-SA 4.0
2019-08-19T14:25:53.487Z
@RamRS I agree. Would definitely prefer a way to convert format 1 to format 2 than vice versa. The other way round could be done with a simple awk solution though, since it doesn't require knowledge of the reference sequence.
Nils
<vcf>
Fix dash/empty ALT alleles for deletions in VCF
# Fix dash/empty ALT alleles for deletions in VCF

(Asked by: username_0 on 2019-08-19)

I have a VCF file that has deletions specified like this, with the REF allele containing only the deleted portion and the ALT allele just saying -:
chr1 101 . AAA -

I would like to combine this with another VCF in which deletions are specified like this, with REF and ALT both extended by 1bp to the left to ensure neither is empty:
chr1 100 . GAAA G

Is there a tool to convert from one of these representations to the other? I have already tried vt normalize and bcftools norm but neither of them seem to do this.
I don't really mind which representation I end up with although I think the second is the one more consistent with the spec.

**Tags:** vcf
> **Comments:**
> **username_1**: Cross-posted on biostars: https://www.biostars.org/p/394866/  
> **username_1**: OP, the first of the two representations is so outdated that most tools might error out. You're better off insisting on converting to format #2  
> **username_0**: @RamRS I agree. Would definitely prefer a way to convert format 1 to format 2 than vice versa. The other way round could be done with a simple awk solution though, since it doesn't require knowledge of the reference sequence.  

---

## 5 answers

### Answer by username_6 on 2019-08-20. Score: 6 | Accepted answer

According to the specs the ALT is not allowed to contain -. While technically supported, 'N' as the alternative allele is sometimes used unnecessarily and this script can also fix those cases if one exchanges the two "-" within the fix_record function for "N".
Here is a little python (>=3.6) script that is able to fix it. It makes use of the pysam module. You need a reference file and this fasta file must be indexed by samtools faidx reference.fa. Please note that it doesn't support multiple values in the ALT column for records that contain a -.
Usage:
python fixvcf.py -f reference.fasta -o out.vcf in.vcf

The script:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import argparse
import sys

import pysam


class Application:
    def __init__(self):
        self.args = Application.get_args()
        self.vcf = pysam.VariantFile(sys.stdin)
        self.fasta = pysam.FastaFile(self.args.fasta)

    @staticmethod
    def get_args() -> argparse.Namespace:
        """
        Read command line arguments and set input and output sources.

        :return: parsed command line arguments
        """
        parser = argparse.ArgumentParser(prog="fixvcf.py", description="Fixes deletion represented by -")
        parser.add_argument('--version', action='version', version='%(prog)s 0.1')

        parser.add_argument("input", help="input file, use - to read from stdin")
        parser.add_argument("-f", "--fasta", help="fasta reference file", required=True)
        parser.add_argument("-o", "--output", help="output file")

        if len(sys.argv) == 1:
            parser.print_help(sys.stderr)
            sys.exit(1)

        args = parser.parse_args()

        if args.output:
            sys.stdout = open(args.output, "w")

        if args.input != "-":
            sys.stdin = open(args.input, "r")

        return args

    def fix_record(self, record: pysam.libcbcf.VariantRecord) -> pysam.libcbcf.VariantRecord:
        """
        If the ALT column of the records contains a `-`, the variant position is shift one position to the left.
        For reaching this the reference base for the position is determined and prepended to the current REF bases.
        The ALT value `-` is replaced with the determined ref base on position before. In case there are multiple ALT
        values the determined ref base is prepended to them.

        :param record: vcf file record
        :return: fixed vcf record
        """
        if "-" in record.alts:
            ref = self.fasta.fetch(region=f"{record.chrom}:{record.pos - 1}-{record.pos - 1}")    
            record.pos = record.pos - 1
            record.ref = f"{ref}{record.ref}"
            record.alts = tuple(map(lambda alt: ref if alt == "-" else f"{ref}{alt}", record.alts))

        return record

    def start(self):
        """
        Print the header of the vcf file. Afterwards the fix_record method is applied to each record in the vcf file.

        For more information about python's map() see: https://docs.python.org/3.6/library/functions.html#map

        For more information about the unpacking mechanism using `*`see:
        https://docs.python.org/3/tutorial/controlflow.html#tut-unpacking-arguments
        """
        print(self.vcf.header, end="")
        print(*map(self.fix_record, self.vcf), sep="", end="")


if __name__ == "__main__":
    app = Application()
    app.start()

A lot of the code above is boilerplate for running a script on the command line. The critical code for the transformation is in the fix_record function. The relevant code is below, expanded, with line numbers and a description of each command.
[1]   ref = self.fasta.fetch(
          region=f"{record.chrom}:{record.pos - 1}-{record.pos - 1}"
      )
[2]   record.pos = record.pos - 1
[3]   record.ref = f"{ref}{record.ref}"
[4]   record.alts = tuple(
          map(
              lambda alt: ref if alt == "-" else f"{ref}{alt}",
              record.alts
          )
      )


This grabs the base preceding the inserted/deleted sequence.
This corrects the position of the variant to account for prepending another nucleotide.
This defines the new corrected REF allele: concatenate the base grabbed in [1] with the original REF allele
Apply an anonymous (lambda) function to each ALT allele; the function will replace - with the base grabbed in [1], or preprend the base to the allele if it is not -

> **Comments:**
> **username_7**: +1, thanks for sharing a working script. It might be even more helpful for innocent bystanders to emphasize and explain the code that does the actual transformation.  
> **username_6**: Done by adding type hints and doc strings :)  
> **username_7**: Sorry, I should have been more clear. My edit is more of what I had in mind. :-)  
> **username_8**: Thanks finswimmer for the script. It's been very helpful to me. I would like to extend it for the case of REF alleles with "-" as in: *chr1 6029086 - A* to: *chr1 6029086 CA C* I have made the attempt to do it by extend this solution for other cases but couldn't find the right way to do it for all of them. For example in: *chr1 1684347 - CCT* I would change it to (REF would change to "C"(the position grabbed)+ALT and ALT would change to the position grabbed("C") which worked in the previous example): *chr1 1684347 CCCT C* But it's wrong cause actually should be like: *chr1 1684347 C CCCT* How  
> **username_4**: Generator version of the python script. It won't consume your memmory at all. #print(*map(self.fix_record, self.vcf), sep="", end="") for line in self.vcf: print(self.fix_record(line), sep="", end="")  

---

### Answer by username_2 on 2019-08-19. Score: 1

Probably it is better to use dedicated tools, however, here is a quick R workaround. It is relatively straightforward to remove the extra base (first letter of column 4) in the second type of output:
# just a workaround to encolose the second file in a data frame
# you can just read your file as a table using fread(), read.csv(), ...
test <- data.frame(
  col1 = "chr1",
  col2 = 100,
  col3 = ".",
  col4 = "GAAA",
  col5 = "G",
  stringsAsFactors = FALSE
)

> test
  col1 col2 col3 col4 col5
1 chr1  100    . GAAA    G

# remove the first letter in column 4
test$col4 <- substring(test$col4, 2)
# converting column 4 to "-"
test$col5 <- "-"

> test
  col1 col2 col3 col4 col5
1 chr1  100    .  AAA    -

EDIT:
Here is how it can be done with awk for speed and memory concerns: The logic is the same as above, remove the first letter in column 4 and then change column 5 to "-".
awk '{$4 = substr($4,2); print}' infile > outfile
awk '{$5 = "-"; print}' outfile > outfile2

infile
chr1 100 . GAAA G

outfile
chr1 100 . AAA G

outfile2
chr1 100 . AAA -

> **Comments:**
> **username_0**: Thanks but my real VCF is several GB in size so I don't think this will work.  
> **username_2**: I have edited my answer so as to include a solution making use of awk.  

---

### Answer by username_4 on 2020-02-16. Score: 1

One more thing. If empty ALT allele is not "-", you can change it such as "*", as follows.
    if "*" in record.alts:
        ref = self.fasta.fetch(reference=record.chrom, start=record.pos - 1, end=record.pos)
        record.pos = record.pos - 1
        record.ref = f"{ref}{record.ref}"
        record.alts = tuple(map(lambda alt: ref if alt == "*" else f"{ref}{alt}", record.alts))

> **Comments:**
> **username_5**: Hi Namshin, thanks for contributing, but this is not the way we usually answer questions on this website. You should not assume that a reader reads the whole thread. If it's an extension for a different answer, meaning which one and also explain what is the benefit of your extension. Note you can [[edit]] your answer.  

---

### Answer by username_4 on 2020-02-22. Score: 1

This is my first post and I will add some explanations. When I tried to use this script, print(*map(self.fix_record, self.vcf), sep="", end=""), it will increase memory usage because it will load everything into memory and then start to apply fix_record function. So, I just used two lines for line in self.vcf: print(self.fix_record(line), sep="", end=""). Because pysam uses generator by default, if we process one by one by pysam's VariantFile function, it will consume very little memory. In my case, the VCF file was more than 200GB, so I had to fix this script for my VCF file.
Second my VCF file used "*" for empty ALT, so I fixed two lines (1) if "*" in record.alts: (2) record.alts = tuple(map(lambda alt: ref if alt == "*" else f"{ref}{alt}", record.alts))
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import argparse
import sys

import pysam


class Application:
    def __init__(self):
        self.args = Application.get_args()
        self.vcf = pysam.VariantFile(sys.stdin)
        self.fasta = pysam.FastaFile(self.args.fasta)

    @staticmethod
    def get_args() -> argparse.Namespace:
        """
        Read command line arguments and set input and output sources.

        :return: parsed command line arguments
        """

        parser = argparse.ArgumentParser(prog="fixvcf.py", description="Fixes deletion represented by -")
        parser.add_argument('--version', action='version', version='%(prog)s 0.1')

        parser.add_argument("input", help="input file, use - to read from stdin")
        parser.add_argument("-f", "--fasta", help="fasta reference file", required=True)
        parser.add_argument("-o", "--output", help="output file")

        if len(sys.argv) == 1:
            parser.print_help(sys.stderr)
            sys.exit(1)

        args = parser.parse_args()

        if args.output:
            sys.stdout = open(args.output, "w")

        if args.input != "-":
            sys.stdin = open(args.input, "r")

        return args

    def fix_record(self, record: pysam.libcbcf.VariantRecord) ->     pysam.libcbcf.VariantRecord:
        """
        If the ALT column of the records contains a `-`, the variant position is shift one position to the left.
        For reaching this the reference base for the position is determined and prepended to the current REF bases.
        The ALT value `-` is replaced with the determined ref base on position before. In case there are multiple ALT
        values the determined ref base is prepended to them.

        :param record: vcf file record
        :return: fixed vcf record
        """

        if "*" in record.alts:
            ref = self.fasta.fetch(reference=record.chrom, start=record.pos - 1, end=record.pos)

            record.pos = record.pos - 1
            record.ref = f"{ref}{record.ref}"
            record.alts = tuple(map(lambda alt: ref if alt == "*" else f"{ref}{alt}", record.alts))

        return record

    def start(self):
        """
        Print the header of the vcf file. Afterwards the fix_record method is applied to each record in the vcf file.

        For more information about python's map() see: 
https://docs.python.org/3.6/library/functions.html#map

        For more information about the unpacking mechanism using `*`see:
        https://docs.python.org/3/tutorial/controlflow.html#tut-unpacking-arguments
        """

        print(self.vcf.header, end="")
        #print(*map(self.fix_record, self.vcf), sep="", end="")
        for line in self.vcf:
            print(self.fix_record(line), sep="", end="")

if __name__ == "__main__":
    app = Application()
    app.start()


---

### Answer by username_3 on 2021-03-10. Score: 0

Edited version of Namshin's code this can handle if REF or ALT is '-' ( these records in vcf file are observed when there is insertion/deletion).
Added documentation in the script to explain code block I have modified.
Hope this useful.
Tested example with GRCh37
python fix_vcf.py -f genome.fa -o out_fixed.vcf input.vcf

example input.vcf:
##fileformat=VCFv4.2
#CHROM  POS ID  REF ALT QUAL  FILTER  INFO
7 6027157 . - T 1 PASS  NS=1  GQ  10
9 98218616  . - C 1 PASS  NS=1  GQ  10
17  41245200  . AT  - 1 PASS  NS=1  GQ  10
13  32929213  . C - 1 PASS  NS=1  GQ  10


out_fixed.vcf:
##fileformat=VCFv4.2
##FILTER=
#CHROM  POS ID  REF ALT QUAL  FILTER  INFO
7 6027157 . T TT  1 PASS  NS=1
9 98218616  . A AC  1 PASS  NS=1
17  41245199  . GAT G 1 PASS  NS=1
13  32929212  . CC  C 1 PASS  NS=1

# -*- coding: utf-8 -*-
import argparse
import sys

import pysam


class Application:
    def __init__(self):
        self.args = Application.get_args()
        self.vcf = pysam.VariantFile(sys.stdin)
        self.fasta = pysam.FastaFile(self.args.fasta)

    @staticmethod
    def get_args() -> argparse.Namespace:
        """
        Read command line arguments and set input and output sources.

        :return: parsed command line arguments
        """

        parser = argparse.ArgumentParser(prog="fixvcf.py", description="Fixes deletion represented by -")
        parser.add_argument('--version', action='version', version='%(prog)s 0.1')

        parser.add_argument("input", help="input file, use - to read from stdin")
        parser.add_argument("-f", "--fasta", help="fasta reference file", required=True)
        parser.add_argument("-o", "--output", help="output file")

        if len(sys.argv) == 1:
            parser.print_help(sys.stderr)
            sys.exit(1)

        args = parser.parse_args()

        if args.output:
            sys.stdout = open(args.output, "w")

        if args.input != "-":
            sys.stdin = open(args.input, "r")

        return args

    def fix_record(self, record: pysam.libcbcf.VariantRecord) ->     pysam.libcbcf.VariantRecord:
        """
        If the ALT column of the records contains a `-`, the variant position is shift one position to the left.
        For reaching this the reference base for the position is determined and prepended to the current REF bases.
        The ALT value `-` is replaced with the determined ref base on position before. In case there are multiple ALT
        values the determined ref base is prepended to them.

        If the REF column of the records contains a `-`, the variant position will change.
        For reaching this the reference base for the position is determined and prepended to the current ALT bases.
        The REF value `-` is replaced with the determined ref base on position. In case there are multiple ALT
        values the determined ref base is prepended to them.

        :param record: vcf file record
        :return: fixed vcf record
        """

        if "-" in record.alts:
            ref = self.fasta.fetch(reference=record.chrom, start=record.pos - 2, end=record.pos -1)

            record.pos = record.pos - 1
            record.ref = f"{ref}{record.ref}"
            record.alts = tuple(map(lambda alt: ref if alt == "-" else f"{ref}{alt}", record.alts))

        if "-" in record.ref:
            ref = self.fasta.fetch(reference=record.chrom, start=record.pos - 1, end=record.pos)

            record.pos = record.pos
            record.ref = f"{ref}"
            record.alts = tuple(map(lambda alt: f"{ref}{alt}", record.alts))

        return record

    def start(self):
        """
        Print the header of the vcf file. Afterwards the fix_record method is applied to each record in the vcf file.

        For more information about python's map() see: 
https://docs.python.org/3.6/library/functions.html#map

        For more information about the unpacking mechanism using `*`see:
        https://docs.python.org/3/tutorial/controlflow.html#tut-unpacking-arguments
        """

        print(self.vcf.header, end="")
        #print(*map(self.fix_record, self.vcf), sep="", end="")
        for line in self.vcf:
            print(self.fix_record(line), sep="", end="")

if __name__ == "__main__":
    app = Application()
    app.start()


---

