Repository: chrovis/cljam
Pull request #276 (2023-06-01 06:57:40)

Support explicit first phasing indicator for VCF genotypes
The current latest VCF spec ([v4.4](https://github.com/samtools/hts-specs/blob/master/VCFv4.4.pdf)) allows explicitly specifying the first phasing indicator of a genotype (like `/0/1` or `|0/1/2`), and defines the semantics for when it's omitted as below:

> The first phasing indicator may be omitted and is implicitly defined as `/` if any phasing indicators are `/` and `|` otherwise.
https://github.com/samtools/hts-specs/blob/0dd3e0d7c6f02b0627a29d6cbab777df07b4daad/VCFv4.4.tex#LL509C5-L509C136

This PR supports explicitly specifying the first phasing indicator to genotypes, and changes the genotype stringifier to emit it if necessary.


Patch:
diff --git a/src/cljam/io/vcf/util.clj b/src/cljam/io/vcf/util.clj
index a17a202..0bf261f 100644
--- a/src/cljam/io/vcf/util.clj
+++ b/src/cljam/io/vcf/util.clj
@@ -101,15 +101,20 @@
                    (= phase "|")
                    (neg? (.indexOf ^String gt "/")))])))))
 
+(defn- stringify-allele [indicator-needed? [allele phase]]
+  [(if indicator-needed?
+     (if phase "|" "/")
+     "")
+   (or allele \.)])
+
 (defn stringify-genotype
-  "Stringifies genotype map into VCF-style GT string."
+  "Stringifies genotype map into VCF-style GT string.
+  The first phasing indicator will be omitted unless necessary."
   [gt-seq]
-  (when-not (or (nil? gt-seq) (empty? gt-seq))
-    (->> gt-seq
-         (mapcat
-          (fn [[allele phase]]
-            [(if phase "|" "/") (or allele \.)]))
-         rest
+  (when-let [[[_ phase :as a1] & as] (seq gt-seq)]
+    (->> as
+         (into (stringify-allele (not= phase (every? second as)) a1)
+               (mapcat (partial stringify-allele true)))
          (apply str))))
 
 (defn genotype-seq


Test patch:
diff --git a/test/cljam/io/vcf/util_test.clj b/test/cljam/io/vcf/util_test.clj
index 88d01b1..275441b 100644
--- a/test/cljam/io/vcf/util_test.clj
+++ b/test/cljam/io/vcf/util_test.clj
@@ -81,13 +81,16 @@
     "0/0" [[0 false] [0 false]]
     "0/1" [[0 false] [1 false]]
     "1/1" [[1 false] [1 false]]
+    "/0|1" [[0 false] [1 true]]
+    "|1/0" [[1 true] [0 false]]
     "./." [[nil false] [nil false]]
     "0|0" [[0 true] [0 true]]
     "0|1" [[0 true] [1 true]]
     "1|1" [[1 true] [1 true]]
     ".|." [[nil true] [nil true]]
     "0/1/2" [[0 false] [1 false] [2 false]]
-    "0/1|2" [[0 false] [1 false] [2 true]]))
+    "0/1|2" [[0 false] [1 false] [2 true]]
+    "|0/1/2" [[0 true] [1 false] [2 false]]))
 
 (deftest about-stringify-genotype
   (are [?gt ?expected]
@@ -95,8 +98,6 @@
     nil nil
     [[0 true]] "0"
     [[1 true]] "1"
-    [[0 false]] "0"
-    [[1 false]] "1"
     [[0 true] [0 true]] "0|0"
     [[0 true] [1 true]] "0|1"
     [[1 true] [1 true]] "1|1"
@@ -104,9 +105,13 @@
     [[0 false] [0 false]] "0/0"
     [[0 false] [1 false]] "0/1"
     [[1 false] [1 false]] "1/1"
+    [[0 false] [1 true]] "/0|1"
+    [[1 true] [0 false]] "|1/0"
     [[nil false] [nil false]] "./."
     [[0 false] [1 false] [2 false]] "0/1/2"
-    [[0 false] [1 false] [2 true]] "0/1|2"))
+    [[0 false] [1 false] [2 true]] "0/1|2"
+    [[0 true] [1 false] [2 false]] "|0/1/2"
+    [[2 false] [0 true] [1 true]] "/2|0|1"))
 
 (deftest genotype-seq
   (are [?ploidy ?n-alt-alleles ?expected]