Yet Another Methylation Encoder

clilatest

Bit-packing for DNA methylation. Arrays and whole genomes in one binary format family, where analysis is bitwise — and so stays fast across a thousandfold of scale, from 28K probes to 29M CpGs.

Fetch — nothing ships with the binary

No reference data is bundled: no coordinates, no feature sets, no example methylomes. yame fetch opens a browser over the catalogue — species, then platform or genome build, then its files.

yame fetch
yame fetch   •   ~/.local/share/yame
        TARGET          KIND                                 TAG        IN STORE
  ▪ HUMAN                                                                       
  [*] hg38            genome                               mixed ▰▱▱▱▱    5/47
  ├──    ●  cpg_nocontig.cr                                  v2          28.9 MB
  ├──  [ ] KYCG        sets                                 v2    ▱▱▱▱▱    0/32
  ├──   ✓  data        sets                                 v3    ▰▰▰▰▰     5/5
  ────────────────────────────────────────
  hg38   genome build

Arrows move, / filters by name, source, collection or title, x takes a row (a folder takes everything under it) and f fetches. What the store already holds shows as present and cannot be re-taken.

Files land in a digest-verified store shared by every tool in the suite — $YAME_DATA_HOME, default ~/.local/share/yame — so what you fetch here is what KnowYourCG and SeSAMe find. Everything else on this page runs against it.

The store is laid out the way the tree reads. What you see under a name is what lands under that name on disk, so hg38/data in the browser is $YAME_DATA_HOME/hg38/data, and a genome's knowledgebase simply nests inside it. Which upstream repo published a directory is a column in fetch -l, not a path component — one name, whether you are browsing, fetching or looking at the disk.

What it carries are the row spaces everything else is measured in. Each length is distinct, so the count is the identity — which is what lets -R and -m take a name instead of a path.

row space        rows   row space        rows
─────────────────────────────────────────────
hg38       29,401,795   HM450         486,427
mm10       21,867,837   MM285         287,692
mm39       21,889,506   MSA           284,309
EPICv2        937,690   Mammal40       38,607
EPIC          866,553   HM27           27,722

Piped or redirected, fetch prints the catalogue as TSV instead, so a script never blocks on a keystroke. Naming a target, filtering it, dry runs and the rest: yame fetch -h.

Data conversion — three kinds of data, text in and out

Almost everything in practice is one of three kinds of data, and they land in two of the seven formats. One table covers how each is stored and what pack reads to build it.

the data                 format           on disk     pack  one line per CpG
────────────────────────────────────────────────────────────────────────────────
methylation, M/U counts  3  M/U counts    1-8 B/site  -fm   M ⇥ U
methylation, binarized   6  set+universe  2 bits/row  -fd   call ⇥ covered
a CpG set                6  set+universe  2 bits/row  -fd   in set ⇥ in universe

pack is the only way in. Input is one row per CpG already in the reference's order — nothing is sorted or matched for you. Columns are tab-separated, and under -fd the first column must be exactly 1 to count as in-set while the second is any nonzero int.

yame fetch -c hg38/data/human_hg38_test.cg
yame fetch hg38/cpg_nocontig.cr   # the coordinates, into the store
yame unpack -R hg38 human_hg38_test.cg | awk '$4!=2' | head -5
chr1  659296  659298  1
chr1  779099  779101  0
chr1  795737  795739  1
chr1  933194  933196  0
chr1  958160  958162  1

Format 6 prints one column by default; -f -1 gives set and universe separately, which is what pack -f6 reads back:

yame unpack -f -1 human_hg38_test.cg | awk '$1!="NA"' | head -3
1  1
0  1
1  1
yame unpack -f -1 human_hg38_test.cg > rt.txt
yame pack -f6 rt.txt rt.cg  # and back again

Verified byte-identical on the round trip.

Visualization — look at it without leaving the shell

Rows are samples, columns are CpGs or windows. The reference is inferred from the row count, so -r alone is enough.

yame fetch -c hg38/data/human_hg38_celltypes.cg
yame fetch hg38/cpg_nocontig.cr   # the coordinates, into the store
yame hprint -r chr20:30000000-30001000 human_hg38_celltypes.cg
                      chr20:30000011-30000862 (10 CpGs)
                      |.........
                      30000011  
Oligodendrocyte       .....
Pancreas-Beta         .......
Blood-NK              ....
Blood-Monocytes       .....

-w caps the width — wider regions are window-averaged rather than truncated — and -R alone gives the whole-genome view.

Multi-sample indexing — many samples, one file

Records concatenate, so a cohort is one file. The .idx beside it maps sample names to offsets, which is what makes them addressable by name.

yame fetch -c hg38/data/human_hg38_celltypes.cg
yame info human_hg38_celltypes.cg | cut -f2-7
Sample           Ncol      Nrow  Format  UnitBytes  Keys
Oligodendrocyte     4  29401795       6          2    NA
Pancreas-Beta       4  29401795       6          2    NA
Blood-NK            4  29401795       6          2    NA
Blood-Monocytes     4  29401795       6          2    NA
yame chunk -s 500000 human_hg38_celltypes.cg chunks/  # fixed row blocks
yame subset -o two.cg human_hg38_celltypes.cg Blood-NK Blood-Monocytes
yame info human_hg38_celltypes.cg | cut -f2 | tail -n +2 > names.txt
yame split -s names.txt human_hg38_celltypes.cg out_  # one file each
yame index -s names.txt human_hg38_celltypes.cg  # rebuild the index
Sample               Nrow  Format
Blood-NK         29401795       6
Blood-Monocytes  29401795       6

info -1 reports the first record of each file — the cheap way to see what a directory holds:

File                              Sample                               Nrow  Format
human_hg38_40_celltypes_chr20.cg  GSM5652176_Adipocytes-Z000000T7    773477       3
human_hg38_celltypes.cg           Oligodendrocyte                  29401795       6
human_hg38_immune_mixture.cg      1                                29401795       3
human_hg38_test.cg                1                                29401795       6
human_hg38_test.truth.cg          1                                29401795       6

Compare & summarize — the analysis command

With no mask, summary describes the file. With one it gives the overlap table — universe, query, mask, overlap, log2 odds ratio, beta, depth. The mask is named, not spelled out: the query's row count says which row space to resolve it in.

yame fetch -c hg38/data/human_hg38_test.cg
yame fetch hg38/KYCG -g CGI   # the mask, into the store
yame summary -m CGI human_hg38_test.cg
QFile               Query  MFile            Mask     N_univ  N_query  N_mask  N_overlap  Log2OddsRatio   Beta  Depth
human_hg38_test.cg      1  CGI.20220904.cm  OpenSea   29000    23857   23534      21149           3.17  0.899     NA
human_hg38_test.cg      1  CGI.20220904.cm  Shelf     29000    23857    1224       1076           0.67  0.879     NA
human_hg38_test.cg      1  CGI.20220904.cm  Shore     29000    23857    2074       1263          -1.74  0.609     NA

A format 2 mask yields one row per state, so -m ChromHMM tests every chromatin state at once. -b picks the set in the browser instead.

yame fetch -c hg38/data/human_hg38_immune_mixture.cg
yame fetch -c hg38/data/human_hg38_40_celltypes_chr20.cg
yame binarize -t 0.5 -c 3 -o calls.cg human_hg38_immune_mixture.cg  # M/U -> set + universe
yame subset -o a.cg human_hg38_40_celltypes_chr20.cg GSM5652176_Adipocytes-Z000000T7
yame subset -o b.cg human_hg38_40_celltypes_chr20.cg GSM5652181_Saphenous-Vein-Endothel-Z000000RM
yame pairwise -H 1 -c 5 -d 0.2 a.cg b.cg  # differential, fmt3 -> fmt6
yame rowop -o binasum human_hg38_40_celltypes_chr20.cg pseudobulk.cg  # collapse
Sample    Nrow  Format
     1  773477       3

That last one is how pseudobulks are made: subset the cells of a cluster, pipe into rowop.

Perturb & mask — for benchmarking

Two different jobs. mask is deterministic and file-driven — it decides which sites are valid. dsample and perturb are stochastic and seed-driven — they decide how much signal is left.

yame fetch -c hg38/data/human_hg38_test.cg
yame dsample -N 5000 -s 42 -o ds.cg human_hg38_test.cg
Query  N_univ  N_query   Beta
    0    5000     4087  0.817
yame perturb -p 0.05 -s 7 -o noisy.cg human_hg38_test.cg
Query  N_univ  N_query   Beta
    1   29000    22953  0.791

Set -s. Without a fixed seed neither is reproducible. mask takes its mask as a positional argument, not -m, and both files must have the same row count.

yame fetch -c hg38/KYCG -g blacklist
yame mask -o clean.cg human_hg38_test.cg Blacklist.20220304.cm