From 7c9ff3edd02d2c8f66a801ee278445bbeeb87075 Mon Sep 17 00:00:00 2001 From: Ben Jeffery Date: Fri, 9 May 2025 08:13:49 +0100 Subject: [PATCH] Add tskit CLI --- bio2zarr/__main__.py | 1 + bio2zarr/cli.py | 50 ++++++++++++ bio2zarr/tskit.py | 9 ++- pyproject.toml | 3 + tests/data/ts/example.trees | Bin 0 -> 10156 bytes tests/test_cli.py | 147 +++++++++++++++++++++++++++++++++++- tests/test_core.py | 2 +- tests/test_ts.py | 59 +++++++-------- 8 files changed, 236 insertions(+), 35 deletions(-) create mode 100644 tests/data/ts/example.trees diff --git a/bio2zarr/__main__.py b/bio2zarr/__main__.py index cab080b6..13728659 100644 --- a/bio2zarr/__main__.py +++ b/bio2zarr/__main__.py @@ -17,6 +17,7 @@ def bio2zarr(): bio2zarr.add_command(cli.vcf2zarr_main) bio2zarr.add_command(cli.plink2zarr) bio2zarr.add_command(cli.vcfpartition) +bio2zarr.add_command(cli.tskit2zarr) if __name__ == "__main__": bio2zarr() diff --git a/bio2zarr/cli.py b/bio2zarr/cli.py index cfe64580..a2ae88d3 100644 --- a/bio2zarr/cli.py +++ b/bio2zarr/cli.py @@ -9,6 +9,7 @@ import tabulate from . import plink, provenance, vcf_utils +from . import tskit as tskit_mod from . import vcf as vcf_mod logger = logging.getLogger(__name__) @@ -630,3 +631,52 @@ def vcfpartition(vcfs, verbose, num_partitions, partition_size): ) for region in regions: click.echo(f"{region}\t{vcf_path}") + + +@click.command(name="convert") +@click.argument("ts_path", type=click.Path(exists=True)) +@click.argument("zarr_path", type=click.Path()) +@click.option("--contig-id", type=str, help="Contig/chromosome ID (default: '1')") +@click.option( + "--isolated-as-missing", is_flag=True, help="Treat isolated nodes as missing" +) +@variants_chunk_size +@samples_chunk_size +@verbose +@progress +@worker_processes +@force +def convert_tskit( + ts_path, + zarr_path, + contig_id, + isolated_as_missing, + variants_chunk_size, + samples_chunk_size, + verbose, + progress, + worker_processes, + force, +): + setup_logging(verbose) + check_overwrite_dir(zarr_path, force) + + tskit_mod.convert( + ts_path, + zarr_path, + contig_id=contig_id, + isolated_as_missing=isolated_as_missing, + variants_chunk_size=variants_chunk_size, + samples_chunk_size=samples_chunk_size, + worker_processes=worker_processes, + show_progress=progress, + ) + + +@version +@click.group() +def tskit2zarr(): + pass + + +tskit2zarr.add_command(convert_tskit) diff --git a/bio2zarr/tskit.py b/bio2zarr/tskit.py index eb68ad45..4dccedaa 100644 --- a/bio2zarr/tskit.py +++ b/bio2zarr/tskit.py @@ -13,7 +13,7 @@ class TskitFormat(vcz.Source): def __init__( self, ts_path, - individuals_nodes, + individuals_nodes=None, sample_ids=None, contig_id=None, isolated_as_missing=False, @@ -25,6 +25,9 @@ def __init__( self.positions = self.ts.sites_position + if individuals_nodes is None: + individuals_nodes = self.ts.individuals_nodes + self._num_samples = individuals_nodes.shape[0] if self._num_samples < 1: raise ValueError("individuals_nodes must have at least one sample") @@ -213,8 +216,8 @@ def generate_schema( def convert( ts_path, zarr_path, - individuals_nodes, *, + individuals_nodes=None, sample_ids=None, contig_id=None, isolated_as_missing=False, @@ -225,7 +228,7 @@ def convert( ): tskit_format = TskitFormat( ts_path, - individuals_nodes, + individuals_nodes=individuals_nodes, sample_ids=sample_ids, contig_id=contig_id, isolated_as_missing=isolated_as_missing, diff --git a/pyproject.toml b/pyproject.toml index f847f5be..f838e084 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,8 @@ dependencies = [ # colouredlogs pulls in humanfriendly", "cyvcf2", "bed_reader", + # TODO Using dev version of tskit for CI, FIXME before release + "tskit @ git+https://github.com/tskit-dev/tskit.git@main#subdirectory=python", ] requires-python = ">=3.10" classifiers = [ @@ -51,6 +53,7 @@ documentation = "https://sgkit-dev.github.io/bio2zarr/" [project.scripts] vcf2zarr = "bio2zarr.cli:vcf2zarr_main" vcfpartition = "bio2zarr.cli:vcfpartition" +tskit2zarr = "bio2zarr.cli:tskit2zarr_main" [project.optional-dependencies] dev = [ diff --git a/tests/data/ts/example.trees b/tests/data/ts/example.trees new file mode 100644 index 0000000000000000000000000000000000000000..4910ec22d9bb884749bf9e81e468f24c4d5251f3 GIT binary patch literal 10156 zcmeHMe~c7&6<=s;kro84EnvlEsA8JCz1_XNQ!i}F*wZa&h8ED&a5-D z=Uop7{-^;df=RFTj~d$;u~BO=ji$CXDPpCerr1OXHm24ZLRutgtsv5x`aU!7?T@)z zcf6o~5GH(Y=KH?y^WOWu_r7nx-!Hp9v1a|exl88Gip63drS{_$^yj~Lu4LY)-W7aY z!~KElf{zbzzt!uVJn$^z=W_o>H|8EEcwZpTz*i9jPWl=C9@`Nw^7XNiIUi`m<@w3B# zP+#W1n(;UC_)f;RvtRI^WBd}v#}@}5?eJp1%lL(ie~j^xk4&EOlKlM|<2h|(FEM@! zAVd)T_c8u`JTCZl{+IlFG^BrlI3lDk+!g(YL;3|DRsYK&{6Iwg62E^j{yl79RJ_=~ zfCDFL1>eqo@yE@KpTmTT2oi=i?=<7N48)@1W&Uesh?o2unjv2DM`OJBKdSxWe~0nn zHo=ShR_>GWyF>Uf5!4FEbn)N*(EOv~#eY9zJf~qSDqi$I&3JxA#*6;rGqhj)carg3 z_G|j39=CF%#P2P}FJ;D^?0?DUR(R3B>{vWW+*?e=e?KNG^wVf-Ug zG&U0i|7pgv8)AUs$j7W&H7w{hy797yFNf?BB%rsPR9|c&T?Ij2Amwy+!{& zL-q?ks{Z+$_!7TLME&Cb<%}2m1uy<>Wxvok63SX=DD#lAa*~$2uS^=1D z-=cwVhrfr#4-WVhG(4d)?Yd9WJHQUJ>PScoUl63fmT4I=yiHS}G(>f{<3JhN<+t6t4S#~d~CnoERMWU;0O5@YIOgwm2# z?5@f1&93zV&(`I+W}`JQ8T_0c!Dl^IL0?R0?y*53VvyYdv0_)MCEpuCAIj=9w@c(s zM`P*J4h1?Yjy-Bvx|Jj29V168VJbzWG_b-53Q4)r$Xz2cz%{;DHAu2nGOVIE0-w2E zzADe5wdIF&TB6{6fXpx8l-{%~9K?XLV$+)zCalt`mg%|Gs+o`B_YE~1_g47rLj6Mx zMcqU#M*W^g4aW;yiQ(rQXKa*voK;ZoQ2J1ZW$%Hb1FnDYTZ(NUwnErfV{d?MO`IC) z>N09qQ(I0AzxxU`)XNW2yZl^Q9zc$uA*axgOK8X;G~^2!@&FAvgod0#LoP`^NlqcJ zkWZ3Rar#D1p&^&hkXvZTE6FY77q*wY#iXy-RQhxI9l;NtyT$Ypo?{rU?=jR3tYJK} zsG(kcGZ zxnj}_kZ&IpMR(BvmPGVT|BHFyJPjYihq1s#k?+N zo9VpU;lxJd4;o^C2LGdB9ibs6Qd1BMtTm}Eh>^teV!TP*<>wOjz{0Q3KD2t*qAtLtvNa`SDSPpF?- zboIH(mA_W+{{7L)tEY~rU%hMc{P?No)a8#{bI&tpj;fXWe>Lmjn~$r{-8lZ_ZzoTx zFTAvF-$LzG^-q(#K6Yx=Y4!ZEcy`Bvzo`fJRhPa#dQSc3FL$r1{$lpPo{zk|^YxA^ z1|}3bKjj>=f<3#^uP7Y~&hPX^rxz`$Y-@R9AP)RB&k&@pMot!8!3%l7Ms`x9JX` zGBQpSOcoY2pVhz$PLo1HDzqYomj4hbY0x zSF2x3)R}pxKyK1>r&u4Tr=R8@%>G!IXO{_qtcs^I*<>o6O!Z}Gs*X`9nRLyBJ1e@tqTwVGz_3P5$F`t=NE3=3B!WBc z(&YqElv_=!I)(&*fgoTTJ8bAq9z@3|8M^D!dlIRhM6xs0-Q9^0Ic6@Db<0zkOy}*x zRm-c!*MD*-ev4zqZ!w1BE92>8e`aNWcW?am4Qr{B?g6r_(*>X9iyrIEYS~PXu;bnc z);5`HA|YY8pFl8e?NBgE=!R_XPNdR_J|A5&haKJV*Ek8-qPw2W3#NfTok%58KD)nf z_jNT4Clgs(aPVExMea;A>PjcF$~Job-z&9ts|3#>LX-o+#ffjBhUG+QY^@cN^20^jqxP&VhXeGnOK2~1t;ekF}7TMVN@dbtq2lT>ksAh9zC1O