如何使用 wget 仅下载压缩 (bgz) 文件的某些列?

Mat*_*sar 12 download wget

我们需要下载一个 182 GB 的压缩文件(未压缩为TSV)。但是,我们只需要文件的前五列,大约相当于 1 GB。

是否有一些奇特的 shell 魔法可以用来下载文件的子集?

下载整个文件只是为了删除 99% 的内容,这确实会耗尽我们服务器的存储空间。

正在下载的内容: 来自gnomad.broadinstitute.org的gnomad.genomes.v3.1.2.sites.chr1.vcf.bgz

欢迎任何替代解决方案。我对也适用于非压缩文件的解决方案感兴趣。

要点是,当我只需要该文件的子集时,如何避免下载大量文件?

roa*_*ima 31

您可以下载该文件,对其进行过滤,然后将结果写入本地磁盘

\n
curl https://storage.googleapis.com/gcp-public-data--gnomad/release/3.1.2/vcf/genomes/gnomad.genomes.v3.1.2.sites.chr1.vcf.bgz |\n    bgzip -d |\n    cut -f1-5\n
Run Code Online (Sandbox Code Playgroud)\n

这仍然需要您下载完整的文件,但只有过滤后的文件才会写入磁盘。

\n

在 Debian 上,该bgzip命令由软件包提供tabix。但如果未安装,您也可以使用来读取压缩文件 ( gzip) 。zcatbgzipcurl \xe2\x80\xa6 | zcat | cut -f1-5

\n
\n

我对该管道所需的数据存储量有一些疑问。这是一次真正的跑步。请注意,在此系统上我总共只有 2GB 可用存储空间;即使压缩,下载和保存文件所需的 182GB 也相差甚远:

\n
# How much disk space available in my current directory?\ndf -h .\nFilesystem      Size  Used Avail Use% Mounted on\n/dev/root       7.9G  5.6G  2.0G  75% /\n\n# Download and filter the file, saving only the result\ncurl https://storage.googleapis.com/gcp-public-data--gnomad/release/3.1.2/vcf/genomes/gnomad.genomes.v3.1.2.sites.chr1.vcf.bgz |\nbgzip -d |\ncut -f1-5 > bigfile\n\n# What did we get, and how much disk space remains?\nls -lh bigfile\n-rw-r--r-- 1 roaima roaima 1.7G Feb  7 05:09 bigfile\n\ndf -h .\nFilesystem      Size  Used Avail Use% Mounted on\n/dev/root       7.9G  7.2G  347M  96% /\n
Run Code Online (Sandbox Code Playgroud)\n

有趣的是,我注意到该文件并不是严格的TSV(制表符分隔值)格式。在其 59,160,934 行中,942 行不包含制表符分隔的数据。

\n
file bigfile\nbigfile: Variant Call Format (VCF) version 4.2, ASCII text, with very long lines\n
Run Code Online (Sandbox Code Playgroud)\n

  • @MooingDuck 好的,我想我理解你的担忧。您无需担心 - `sort` 确实必须读取其整个输入(除非您正在进行合并排序)。`uniq` 在写入任何内容之前不需要读取其整个输入。`bgzip`、`zcat` 和 `cut` - 以及大多数其他过滤器实用程序 - 也没有。我目前正在一个只有 2GB 可用空间的文件系统上运行一个可证明的证明。到目前为止我已经下载了 5.5GB,只写了 51MB (15认同)
  • @MooingDuck 我不明白为什么 GlenYates(或你?)认为这甚至是一个问题。这是一个命令管道。没有临时文件。这就是为什么我写了“_仅将过滤后的数量写入磁盘_” (10认同)
  • @MooingDuck只是 roaima 评论的附录,“uniq”特别要求对其输入进行预排序以获得有效的“完全唯一”结果,因为它比较相邻行而不是读取整个输入来比较所有内容。 (8认同)
  • “...只有过滤后的量才会写入磁盘”您确定吗?我知道 2040 年的人们回头看时会嘲笑这个评论,但目前没有多少计算机拥有 182GB RAM 来在写入磁盘之前保存文件。 (4认同)
  • @GlenYates 是的,我确定。你看到了什么我没有看到的?我们将数据下载到一个管道,该管道根据要求剪切出前五个制表符分隔的列。如果 OP 没有空间来存储,那么任何人都无法提供任何帮助 (4认同)
  • @GlenYates:“.bgz”是一种流式压缩格式,实际上使用非常小的最大块大小(仅 64KiB),因此这是解压缩器在将输出写入管道之前需要先查看压缩输入的最大数据。(实际上这是一个非常保守的上限)。其他压缩方案(例如 LZMA)可以使用相当大的压缩字典,例如 1GiB(如果您告诉压缩器这样做),而这会增加解压缩器的内存需求。但仍然不是用于缓冲 I/O 流,而是用于(解)压缩字典。 (2认同)
  • @MooingDuck,顺便说一句,_no_ `sort` 不会“绝对”将数据读入内存:当 BSD sort 给出的文件大于(默认情况下)可用 RAM 的 90% 时,它会将其分区为磁盘上不同的临时文件,对它们进行单独排序,然后进行合并排序以将它们组合回单个流。你是对的,它确实需要在_某处_存储内容,但断言有问题的某个地方肯定在 RAM 中是错误的。(GNU sort 具有类似的功能,但 90% 的数字来自 BSD 文档) (2认同)

小智 18

根本不存在。

我查看了数据,正如你所说,文件很大,所以我没有下载它。

你说的第二件事是 TSV 文件:

...我们只需要二进制 TSV 格式文件的前 5 列...

如果我正确解释您的缩写,您的意思TSV是 as in Tab Separated Valuefile,在这种情况下它是常规文本文件而不是二进制文件。当然,从某些角度来看,即使文本文件也是二进制文件 - 但在这种情况下我们讨论的是文本文件。

然而该文件确实被压缩了:

: curl -s -L https://storage.googleapis.com/gcp-public-data--gnomad/release/3.1.2/vcf/genomes/gnomad.genomes.v3.1.2.sites.chr1.vcf.bgz | file -
/dev/stdin: Blocked GNU Zip Format (BGZF; gzip compatible), block length 4462
Run Code Online (Sandbox Code Playgroud)

正如有人已经回答的那样,这意味着,您可以使用流技术动态解包文件,通过管道将其发送到解压缩器。bgzcat我的发行版中似乎不存在,但是zcat,它非常通用,在我的发行版中并且知道如何读取此压缩流:

: curl -s -L https://storage.googleapis.com/gcp-public-data--gnomad/release/3.1.2/vcf/genomes/gnomad.genomes.v3.1.2.sites.chr1.vcf.bgz | zcat | head -n 1              
##fileformat=VCFv4.2
Run Code Online (Sandbox Code Playgroud)

进一步调查,文件开头有注释:

curl -L https://storage.googleapis.com/gcp-public-data--gnomad/release/3.1.2/vcf/genomes/gnomad.genomes.v3.1.2.sites.chr1.vcf.bgz | zcat | head -n 80
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0##fileformat=VCFv4.2
##hailversion=0.2.77-684f32d73643
##FILTER=<ID=AC0,Description="Allele count is zero after filtering out low-confidence genotypes (GQ < 20; DP < 10; and AB < 0.2 for het calls)">
##FILTER=<ID=AS_VQSR,Description="Failed VQSR filtering thresholds of -2.7739 for SNPs and -1.0606 for indels">
##FILTER=<ID=InbreedingCoeff,Description="InbreedingCoeff < -0.3">
##FILTER=<ID=PASS,Description="Passed all variant filters">
##INFO=<ID=AC,Number=A,Type=Integer,Description="Alternate allele count">
##INFO=<ID=AN,Number=1,Type=Integer,Description="Total number of alleles">
##INFO=<ID=AF,Number=A,Type=Float,Description="Alternate allele frequency">
##INFO=<ID=popmax,Number=A,Type=String,Description="Population with maximum allele frequency">
##INFO=<ID=faf95_popmax,Number=A,Type=Float,Description="Filtering allele frequency (using Poisson 95% CI) for the population with the maximum allele frequency">
##INFO=<ID=AC_non_v2_XX,Number=A,Type=Integer,Description="Alternate allele count for XX samples in non_v2 subset">
##INFO=<ID=AN_non_v2_XX,Number=1,Type=Integer,Description="Total number of alleles in XX samples in non_v2 subset">
##INFO=<ID=AF_non_v2_XX,Number=A,Type=Float,Description="Alternate allele frequency in XX samples in non_v2 subset">
##INFO=<ID=nhomalt_non_v2_XX,Number=A,Type=Integer,Description="Count of homozygous individuals in XX samples in non_v2 subset">
##INFO=<ID=AC_non_cancer_fin_XX,Number=A,Type=Integer,Description="Alternate allele count for XX samples of Finnish ancestry in non_cancer subset">
##INFO=<ID=AN_non_cancer_fin_XX,Number=1,Type=Integer,Description="Total number of alleles in XX samples of Finnish ancestry in non_cancer subset">
##INFO=<ID=AF_non_cancer_fin_XX,Number=A,Type=Float,Description="Alternate allele frequency in XX samples of Finnish ancestry in non_cancer subset">
Run Code Online (Sandbox Code Playgroud)

最后@943行可能标题开始:

: curl -s -L https://storage.googleapis.com/gcp-public-data--gnomad/release/3.1.2/vcf/genomes/gnomad.genomes.v3.1.2.sites.chr1.vcf.bgz | zcat | tail -n+943 | head -n 1 
#CHROM  POS ID  REF ALT QUAL    FILTER  INFO
Run Code Online (Sandbox Code Playgroud)

我懒得确定第一个数据行中哪个实际上是列分隔符,但似乎“\t”确实是列分隔符:

: curl -s -L https://storage.googleapis.com/gcp-public-data--gnomad/release/3.1.2/vcf/genomes/gnomad.genomes.v3.1.2.sites.chr1.vcf.bgz | zcat | tail -n+944 | head -n 1 
chr1    10031   .   T   C   .   AC0;AS_VQSR AC=0;AN=56642;AF=0.00000;AC_non_v2_XX=0;AN_non_v2_XX=23674;AF_non_v2_XX=0.00000;nhomalt_non_v2_XX=0;AC_non_cancer_fin_XX=0;AN_non_cancer_fin_XX=1060;AF_non_cancer_fin_XX=0.00000;nhomalt_non_cancer_fin_XX=0;AC_non_neuro_nfe=0;AN_non_neuro_nfe=24462;AF_non_neuro_nfe=0.00000;nhomalt_non_neuro_nfe=0;AC_non_neuro_afr_XY=0;AN_non_neuro_afr_XY=5226;AF_non_neuro_afr_XY=0.00000;nhomalt_non_neuro_afr_XY=0;AC_non_neuro_nfe_XY=0;AN_non_neuro_nfe_XY=9974;AF_non_neuro_nfe_XY=0.00000;nhomalt_non_neuro_nfe_XY=0;AC_controls_and_biobanks_eas_XY=0;AN_controls_and_biobanks_eas_XY=392;AF_controls_and_biobanks_eas_XY=0.00000;nhomalt_controls_and_biobanks_eas_XY=0;AC_non_neuro_sas_XX=0;AN_non_neuro_sas_XX=260;AF_non_neuro_sas_XX=0.00000;nhomalt_non_neuro_sas_XX=0;AC_non_v2=0;AN_non_v2=44696;AF_non_v2=0.00000;nhomalt_non_v2=0;AC_non_topmed_nfe_XX=0;AN_non_topmed_nfe_XX=2800;AF_non_topmed_nfe_XX=0.00000;nhomalt_non_topmed_nfe_XX=0;AC_non_v2_mid=0;AN_non_v2_mid=192;AF_non_v2_mid=0.00000;nhomalt_non_v2_mid=0;AC_non_topmed_sas=0;AN_non_topmed_sas=1114;AF_non_topmed_sas=0.00000;nhomalt_non_topmed_sas=0;AC_non_cancer_eas_XX=0;AN_non_cancer_eas_XX=746;AF_non_cancer_eas_XX=0.00000;nhomalt_non_cancer_eas_XX=0;AC_amr_XY=0;AN_amr_XY=3650;AF_amr_XY=0.00000;nhomalt_amr_XY=0;AC_non_v2_nfe_XX=0;AN_non_v2_nfe_XX=12970;AF_non_v2_nfe_XX=0.00000;nhomalt_non_v2_nfe_XX=0;AC_controls_and_biobanks_XY=0;AN_controls_and_biobanks_XY=6960;AF_controls_and_biobanks_XY=0.00000;nhomalt_controls_and_biobanks_XY=0;AC_non_neuro_asj_XY=0;AN_non_neuro_asj_XY=722;AF_non_neuro_asj_XY=0.00000;nhomalt_non_neuro_asj_XY=0;AC_oth=0;AN_oth=782;AF_oth=0.00000;nhomalt_oth=0;AC_non_topmed_mid_XY=0;AN_non_topmed_mid_XY=82;AF_non_topmed_mid_XY=0.00000;nhomalt_non_topmed_mid_XY=0;AC_non_cancer_asj_XX=0;AN_non_cancer_asj_XX=770;AF_non_cancer_asj_XX=0.00000;nhomalt_non_cancer_asj_XX=0;AC_sas_XY=0;AN_sas_XY=860;AF_sas_XY=0.00000;nhomalt_sas_XY=0;AC_non_neuro_fin=0...
Run Code Online (Sandbox Code Playgroud)

这就是你的数据。

现在的问题是:

  • 因为文件是TSV,它基本上只是CSV格式的更通用的格式,不幸的是,这意味着它是面向行的格式,而不是面向列的格式,这意味着:
    • 传入的数据单位是行
    • 线路传入被分成列,反之亦然
    • 即要“仅”获取前 5 列,您仍然需要访问文件中的所有行
    • 由于行具有意外/不可预测的长度,因此数据结构不规则,因此您无法以任何有意义的方式计算所需的字节范围
  • 因为文件被bgzip压缩,所以无论如何都不可能使用字节范围
    • 由于在 HTTP 服务器中使用即时 gzip 压缩时大小计算错误,此类文件的字节范围经常被破坏
    • 我们不知道文件是否被预压缩(并放入下载目录)(并且由 httpd/nginx 动态压缩)
    • 给定大小的文件,它可能是预先压缩的,因此理论上字节范围可以工作,但是您仍然需要索引将各个未压缩的行映射到相应的 BGZIP 块中,这可能是高于您级别的任务(假设您问这个问题 - 它即使在我的水平上,考虑到时间投入比率,也不会盈利(我需要几周/几个月))
    • 即使您的索引工作正常,您仍然需要下载/解析整个文件,即使文件在上游更改时索引也会中断
  • 对您来说最简单的方法是希望谷歌下载服务器的设置方式不会终止您的连接,即使它持续数小时/数天。

鉴于最后一点,你可以按照@user10489所说的去做:

  1. 开始从谷歌服务器流式传输文件并将curl其通过管道传输到zcat
  2. 用管道将输出的末尾附加zcat到一些快速组合在一起的脚本的输入:perl、python、php、lua
  3. 在脚本中解析用制表符分隔的前五列的每个传入行,并将这些列存储到本地单独的数据文件中(TSV,或者我建议sqlite3) - 您还必须忽略上述数据集开头的注释。

最终的用法看起来像这样:

: curl -s -L https://storage.googleapis.com/gcp-public-data--gnomad/release/3.1.2/vcf/genomes/gnomad.genomes.v3.1.2.sites.chr1.vcf.bgz | zcat | myscript my-outfile.tsv
Run Code Online (Sandbox Code Playgroud)

你明白了。

这在几分钟/几小时内就可以完成,具体取决于您的能力,但现在您将遇到以下问题:

  • 你需要以某种方式设置你的管道,以便它可以在无人值守的情况下运行几个小时(tmux,屏幕)
  • 即使您实现行计数并将最后处理的行存储在哨兵文件中,如果您的脚本在处理过程中崩溃,您将需要从头开始重新下载,并且要么等到到达存储的行,要么,在这种情况下,重新启动时重新处理所有内容会更容易
  • 您不知道提取的“仅前 5 列”将大大小于压缩后的 128GB - 无论如何您可能会在处理过程中耗尽空间
  • 最后,我们不知道谷歌服务器是否没有一些“下载连接花费太长时间”的保护,因此它们可能会curl过早地切断与管道头部的链接 - 这意味着您仍然无法式传输整个内容文件下来。

因此,在您的情况下,我会去找项目/研究领导或管理人员并请求资源:驱动器,也许是虚拟机/节点,按预期下载整个文件并在本地进行预处理。分析生成的预处理 5 列文件长度/大小,然后仅将其上传到服务器。

您不会依赖谷歌服务器不切断您的管道,并且您可以根据需要多次重新运行和调整管道。您不必担心“存储空间‘只能’容纳 5 列吗?” 也有疑问。

希望有帮助。

  • 就我个人而言,我根本不会显示提示 - 这样人们就可以更轻松地复制和粘贴代码 (7认同)
  • 哦,还有,bgzip _is_ 已索引,索引文件几乎肯定可以从同一源下载(现在在移动设备上,无法检查)。你说这对你的水平有帮助,可能值得一看。 (3认同)
  • @CSM,您不需要添加 `pv` 来查看下载进度。只需从“curl”中删除“静默操作”标志(“-s”),它就会自行执行此操作 (3认同)

use*_*489 7

  • 如果文件未压缩,则可以在 http 协议中下载字节范围。然而,如果没有索引,即使有可能,这也可能没有用。
  • 如果文件是通过流压缩进行压缩的,则必须下载整个文件才能解压缩其中的任何内容。许多流压缩器实际上会执行顺序压缩块,但同样,如果没有这些块的索引,它基本上是不可行的,并且如果您试图获取单个列而不是一系列大块,这是不可能的。
  • 如果您遇到的问题不是网络带宽而是磁盘空间,那么有很多选择。将内容下载到内存中,在内存中流解压缩它,将未压缩的数据发送到提取所需列并将其写出的进程。这一切都可以在单个管道中完成wget -O - | bgzcat | ,并使用基于流的工具,这些工具可以在标准输入上获取输入并将所需内容写入标准输出或文件。

  • +1提及*HTTP范围请求*,即使它不会直接回答这个具体问题。不过,它可能有助于回答“相关”问题。我相信,如果不下载整个文件,上述问题实际上是不可能解决的(即使您没有义务“存储”整个文件)。 (4认同)