在没有/保持组顺序的情况下洗牌大文本文件

时间:2013-10-30 18:25:06

标签: linux sorting text random shuffle

根据第一列中的唯一元素,它不是制作一个脚本,而是根据一个大的制表符分隔文本文件。这意味着,对于第一列中的每个唯一元素,行数将相等并由用户指定。

有两种输出可能性,维持行顺序或随机行顺序。

输入:

chr1    3003204 3003454 *   37  +
chr1    3003235 3003485 *   37  +
chr1    3003148 3003152 *   37  -
chr1    3003461 3003711 *   37  +
chr11   71863609    71863647    *   37  +
chr11   71864025    71864275    *   37  +
chr11   71864058    71864308    *   37  -
chr11   71864534    71864784    *   37  +
chrY    90828920    90829170    *   23  -
chrY    90829096    90829346    *   23  +
chrY    90828924    90829174    *   23  -
chrY    90828925    90829175    *   23  -

输出(每个类别1行 - 由用户定义) Output1(随机 - 行顺序将改变):

chr1    3003235 3003485 *   37  +
chr11   71863609    71863647    *   37  +
chrY    90828925    90829175    *   23  -

输出1(随机 - 将保留行顺序):

chr1    3003204 3003454 *   37  +
chr11   71863609    71863647    *   37  +
chrY    90828920    90829170    *   23  -

我尝试在第一列使用sort -u cut来获取唯一元素,然后为每个元素运行grephead的组合以生成输出文件,可以使用shuf随机化,可能有更好的解决方案,因为文件可能很大> 5000万行。

干杯

3 个答案:

答案 0 :(得分:1)

当然,编写脚本会更容易吗?

perl -n -e 'BEGIN{ %c=qw(chr1 4 chr11 4 chrY 4); $c{$_}=int(rand($c{$_})) for keys %c;  $r="^(".join("|",keys %c).")\\s";} print if (/$r/o and !$c{$1}--);' filename.txt

脚本启动时执行一次BEGIN块。 print if..语句用于文件

中的每一行

%c关联数组具有要查找的键和每个键的项数

$ r是一个看似^(chr1|chr11|chrY)\s

的正则表达式

如果找到正则表达式,则匹配中的匹配键将用作对递减的关联数组的查找。当它为零时,打印行

答案 1 :(得分:1)

尝试使用

维护行顺序

awk '!($1 in a) {a[$1]=$0} END { asort(a,b); for (x in b) print b[x] }' file

输出:

chr1    3003204 3003454 *   37  +
chr11   71863609    71863647    *   37  +
chrY    90828920    90829170    *   23  -

随机排序

为此,只需将 shuf 的输出管道输出到上面的 awk 命令

shuf file | awk '!($1 in a) {a[$1]=$0} END { asort(a,b); for (x in b) print b[x] }'

输出(每次运行都不同)

chr1    3003148 3003152 *   37  -
chr11   71864025    71864275    *   37  +
chrY    90829096    90829346    *   23  +

可变行数

#!/bin/bash
numRow=3
awk 'n[$1]<'$numRow' {a[$1]=a[$1]"\n"$0; n[$1]++} END { asort(a,b); for (x in b) print b[x] }' file

输出:

chr1    3003204 3003454 *   37  +
chr1    3003235 3003485 *   37  +
chr1    3003148 3003152 *   37  -

chr11   71863609    71863647    *   37  +
chr11   71864025    71864275    *   37  +
chr11   71864058    71864308    *   37  -

chrY    90828920    90829170    *   23  -
chrY    90829096    90829346    *   23  +
chrY    90828924    90829174    *   23  -

答案 2 :(得分:1)

如果喜欢用Python使用熊猫来做到这一点。这是我的答案:

#!/bin/env python

import sys
import pandas as pd

column = 0
number = 1
method = pd.Series.head  # or pd.Series.sample

pd.read_table(sys.stdin, header=None) \
  .groupby(column) \
  .apply(method, n=number) \
  .to_csv(sys.stdout, sep="\t", index=False, header=False)

pd.read_table将读取表格文件。它的作用与pd.read_csv(..., sep='\t')相同。 header=None会告诉熊猫不要将第一行用作标题,这是默认情况。
.groupby将按DataFrame的给定列分组。
给定关键字参数.apply(method, n=number)method将在每个组上调用n=number
.to_csv会将DataFrame(在这种情况下为制表符分隔)写入,而没有DataFrame的索引和标头输出到stdout。

调用如下:

%$ python myscript.py < ${input_tsv} > ${output_tsv}

Pandas是一个大包装,需要时间来加载。因此,该脚本比awk脚本要慢。但是在更大的Python程序中可能很有用。

基准化:

包含49144条记录的BED文件。

在Zsh中从@jkshah运行Awk脚本:

%$ awk '!($1 in a) {a[$1]=$0} END { asort(a,b); for (x in b) print b[x] }' ${bedfile} | sort >/dev/null
%$ shuf ${bedfile} | awk '!($1 in a) {a[$1]=$0} END { asort(a,b); for (x in b) print b[x] }' | sort >/dev/null

大约21毫秒的挂墙时间(平均70次运行)。 第二个大约30毫秒的挂墙时间(平均70次运行)。

使用%timeit魔术在IPython中运行Python语句:

In [1]: %timeit pd.read_table("Every10cM.sort.bed", header=None).groupby(0).apply(pd.Series.head, n=1).to_csv(sep="\t", index=False, header=False)
In [2]: %timeit pd.read_table("Every10cM.sort.bed", header=None).groupby(0).apply(pd.Series.sample, n=1).to_csv(sep="\t", index=False, header=False)

大约两个时间为72 ms(平均70次运行)。所以它相当慢...