Skip to main content
edited the link to somewhy deleted image
Source Link

Posted here only to show grouping nucleotides approach as the decoder overhead is very large.
So let's start with the explanations. As can be seen form MN908947, the virus mRNA contains "head" (265 nucleotides), "tail" (229 nucleotides) and translated parts with some non-translated parts between them. So, we have 10 genes, named orf1ab, S, ORF3a, E, M, ORF6, ORF7a, ORF8, N, ORF10 in the link above.
It comes out we could use codon usage bias as the picture of frequency codon (=nucleotide triples) usage is very impressive, e.g. for orf1ab:

or for S (looks very similar):

and for N (looks different):
More precisely, here's the table of normed covariance of the codon usage distributions:

Posted here only to show grouping nucleotides approach as the decoder overhead is very large.
So let's start with the explanations. As can be seen form MN908947, the virus mRNA contains "head" (265 nucleotides), "tail" (229 nucleotides) and translated parts with some non-translated parts between them. So, we have 10 genes, named orf1ab, S, ORF3a, E, M, ORF6, ORF7a, ORF8, N, ORF10 in the link above.
It comes out we could use codon usage bias as the picture of frequency codon (=nucleotide triples) usage is very impressive, e.g. for orf1ab:

or for S (looks very similar):

and for N (looks different):
More precisely, here's the table of normed covariance of the codon usage distributions:

Posted here only to show grouping nucleotides approach as the decoder overhead is very large.
So let's start with the explanations. As can be seen form MN908947, the virus mRNA contains "head" (265 nucleotides), "tail" (229 nucleotides) and translated parts with some non-translated parts between them. So, we have 10 genes, named orf1ab, S, ORF3a, E, M, ORF6, ORF7a, ORF8, N, ORF10 in the link above.
It comes out we could use codon usage bias as the picture of frequency codon (=nucleotide triples) usage is very impressive, e.g. for orf1ab:

or for S (looks very similar):

and for N (looks different):
More precisely, here's the table of normed covariance of the codon usage distributions:

another approach for encoding the rest
Source Link

python 3.8, 161+1+287+269+6985=7702161169+1+287289+267193+6985=7702 7637

from zlib import decompress as D
from itertools import product as P
from functools import reduce as R
x=287x=289
c='AGCU'
d=open('f','rb').read()
print(eval(D(d[:x]))(d[x:]))
(lambda f,
rest_len=[265,7,8,24,50,10,6,134,14,24,229],
genes_len=[7097,1274,276,76,223,62,122,122,420,39],
cut=lambda s,x:R(
    lambda acc,n:[acc[0][n:],acc[1]+[acc[0][:n]]],
    x,[s,[]])[1],
cut_out=lambda x,n:x[:n]+x[n+1:],
C=[''.join(i) for i in P('AGCU'c,repeat=3)]:
cut_out(''.join(i+''.join(j)
    for i,j in zip(
        cut(D''.join(c[(i//r)%4] for i in f[:269]).decode('utf-8'193] for r in [1,4,16,64]),rest_len),
        cut([C[i-32] for i in D(f[269f[193:])],genes_len)+[[]])
    ),13468)
)

and the file f is here (produced with decodedecode_1.py).
Try it online!Try it online!

python 3.8, 161+1+287+269+6985=7702

from zlib import decompress as D
from itertools import product as P
from functools import reduce as R
x=287
d=open('f','rb').read()
print(eval(D(d[:x]))(d[x:]))
(lambda f,
rest_len=[265,7,8,24,50,10,6,134,14,24,229],
genes_len=[7097,1274,276,76,223,62,122,122,420,39],
cut=lambda s,x:R(
    lambda acc,n:[acc[0][n:],acc[1]+[acc[0][:n]]],
    x,[s,[]])[1],
cut_out=lambda x,n:x[:n]+x[n+1:],
C=[''.join(i) for i in P('AGCU',repeat=3)]:
cut_out(''.join(i+''.join(j)
    for i,j in zip(
        cut(D(f[:269]).decode('utf-8'),rest_len),
        cut([C[i-32] for i in D(f[269:])],genes_len)+[[]])
    ),13468)
)

and the file f is here (produced with decode.py).
Try it online!

python 3.8, 161169+1+287289+267193+6985=7702 7637

from zlib import decompress as D
from itertools import product as P
from functools import reduce as R
x=289
c='AGCU'
d=open('f','rb').read()
print(eval(D(d[:x]))(d[x:]))
(lambda f,
rest_len=[265,7,8,24,50,10,6,134,14,24,229],
genes_len=[7097,1274,276,76,223,62,122,122,420,39],
cut=lambda s,x:R(
    lambda acc,n:[acc[0][n:],acc[1]+[acc[0][:n]]],
    x,[s,[]])[1],
cut_out=lambda x,n:x[:n]+x[n+1:],
C=[''.join(i) for i in P(c,repeat=3)]:
cut_out(''.join(i+''.join(j)
    for i,j in zip(
        cut(''.join(c[(i//r)%4] for i in f[:193] for r in [1,4,16,64]),rest_len),
        cut([C[i-32] for i in D(f[193:])],genes_len)+[[]])
    ),13468)
)

and the file f is here (produced with decode_1.py).
Try it online!

answered some questions from the comments
Source Link

Answering on stux's comments:

However it looks like you need auxiliary tables to do so?

Almost no. I can number out nucleotide triplets just like numbers at base \$4\$ of length \$3\$. itertools.product does the job well. But yes, we have to know what nucleotides do encode peptid amino-acids and which do not, for this we need the first link.

However, it's rather tricky to get the right sequence for orf1ab because of it's code consists of \$2\$ overlapping sequences, which is rather rare in the real world, IMHO. They do overlap on the nucleotide at position \$13468\$ (see join(266..13468,13468..21555) in the above link), so we double this nucleotide, compress, and then cut it out after de-compression (with cut_out).

Then we compress joined sequences of triplets (not separately because orf1ab+S is rather large compared to other parts and the encoding table (frequences) will not differ much for orf1ab+S compared to all peptid-coding parts joined). Also we compress the non-peptid-encoding parts because we also need them in the output. Joined too.

Then we have to cut both sequences into apropriate pieces (pieces lengths are genes_len and rest_len) and interleave (with for i,j in zip(cut(...,rest_len),cut(...,genes_len))). \$269\$ is compressed non-peptid-encoding nucleotides length and \$6985\$ is compressed triplets length.

Looking back at the comments it's left only to explain how cut works. The python reduce function takes \$3\$ argumets: function(accumulator,item), the sequence and the optional accumulator initial value. Then accumulator on the next step = function(accumulator on the previous step,item from sequence). In other words, reduce feeds the sequence into the function and returns the accumulator when the sequence runs out. E.g. a way to replace math.prod: lambda z:reduce(lambda x,y:x*y,z,1).

So the accumulator in this particular case is chosen to contain [the sequence to cut,array ot already cut pieces] and acc[0] is the sequence to cut, acc[1] is already cut pieces, so acc[0][n:],acc[1]+[acc[0][:n]] makes perfect sense now, doesn't it? And we need only the reduce return value at index \$1\$ (i.e. the pieces sequence).

Did I answer all the questions?)

Answering on stux's comments:

However it looks like you need auxiliary tables to do so?

Almost no. I can number out nucleotide triplets just like numbers at base \$4\$ of length \$3\$. itertools.product does the job well. But yes, we have to know what nucleotides do encode peptid amino-acids and which do not, for this we need the first link.

However, it's rather tricky to get the right sequence for orf1ab because of it's code consists of \$2\$ overlapping sequences, which is rather rare in the real world, IMHO. They do overlap on the nucleotide at position \$13468\$ (see join(266..13468,13468..21555) in the above link), so we double this nucleotide, compress, and then cut it out after de-compression (with cut_out).

Then we compress joined sequences of triplets (not separately because orf1ab+S is rather large compared to other parts and the encoding table (frequences) will not differ much for orf1ab+S compared to all peptid-coding parts joined). Also we compress the non-peptid-encoding parts because we also need them in the output. Joined too.

Then we have to cut both sequences into apropriate pieces (pieces lengths are genes_len and rest_len) and interleave (with for i,j in zip(cut(...,rest_len),cut(...,genes_len))). \$269\$ is compressed non-peptid-encoding nucleotides length and \$6985\$ is compressed triplets length.

Looking back at the comments it's left only to explain how cut works. The python reduce function takes \$3\$ argumets: function(accumulator,item), the sequence and the optional accumulator initial value. Then accumulator on the next step = function(accumulator on the previous step,item from sequence). In other words, reduce feeds the sequence into the function and returns the accumulator when the sequence runs out. E.g. a way to replace math.prod: lambda z:reduce(lambda x,y:x*y,z,1).

So the accumulator in this particular case is chosen to contain [the sequence to cut,array ot already cut pieces] and acc[0] is the sequence to cut, acc[1] is already cut pieces, so acc[0][n:],acc[1]+[acc[0][:n]] makes perfect sense now, doesn't it? And we need only the reduce return value at index \$1\$ (i.e. the pieces sequence).

Did I answer all the questions?)

added tio.run link
Source Link
Loading
Source Link
Loading