在Linux命令中随机选择列

5

我有一个包含606,347个的平面文件(.txt),我想要提取50,000个随机列,但第一列除外,因为它是样本标识。我如何使用Linux命令来实现这个目标? 我的文件长这样:

ID  SNP1    SNP2    SNP3
1   0   0   2
2   1   0   2
3   2   0   1
4   1   1   2
5   2   1   0

它是以制表符分隔的。
非常感谢。
干杯,
Paula。

1
你是指“列”还是“行”? - hek2mgl
你能给一些数据的例子吗? - hek2mgl
2
你确定你是指列吗?一个文本文件中有1,000,000列是非常多的。你是指行吗?如果不是,你需要多少行呢?当你说“列”时,你是指字符还是[空格分隔]字段? - Craig Estey
1
列之间是如何分隔的? - Cyrus
大约有600k个变量,以TAB分隔。 - PaulaF
@karakfa的回答很好,但是在awk脚本的BEGIN{}部分无法获取NF值。请参考:https://stackoverflow.com/questions/41450087/how-to-get-number-of-fields-in-awk-prior-to-processing - Minstein
3个回答

4
< p > awk 可以解救你!

$ cat shuffle.awk

   function shuffle(a,n,k) {
     for(i=1;i<=k;i++) {
       j=int(rand()*(n-i))+i
       if(j in a) a[i]=a[j]
       else a[i]=j
       a[j]=i;
     }
   }

   BEGIN {srand()}
   NR==1 {shuffle(ar,NF,ncols)}
         {for(i=1;i<=ncols;i++) printf "%s", $(ar[i]) FS; print ""}

一般用法

$ echo $(seq 5) | awk -f shuffle.awk -v ncols=5
3 4 1 5 2

在您的特殊情况下,您可以打印$1并从2开始执行函数循环。
即将
for(i=1;i<=k;i++)
改为
a[1]=1; for(i=2;i<=k;i++)

好的回答。请查看以下链接以获得“更好”的随机性:https://dev59.com/0VHTa4cB1Zd3GeqPR3yZ#4048538 - hek2mgl
是的,这是个好观点,但我不确定它是否适用于这种情况,因为运行超过600k列需要一些时间。谁会使用这样的记录结构呢? - karakfa
@karakfa非常感谢。这对我有用。唯一的问题是我无法将第1列与其他列分开。如何要求将其作为第一列打印并不包括在随机选择中?抱歉,我是Unix命令的初学者。 - PaulaF
1
@PaulaF,请查看更新。我认为建议的更改应该足够了。 - karakfa

2

试试这个:

echo {2..606347} | tr ' ' '\n' | shuf | head -n 50000 | xargs -d '\n' | tr ' ' ',' | xargs -I {} cut -d $'\t' -f {} file

更新:

echo {2..606347} | tr ' ' '\n' | shuf | head -n 50000 | sed 's/.*/&p/' | sed -nf - <(tr '\t' '\n' <file) | tr '\n' '\t'

我遇到了这个错误:xargs: argument list too long。有什么可以改变的吗?谢谢 :) - PaulaF

0

@karakfa的回答很好,但是在awk脚本的BEGIN{}部分无法获取NF值。请参考:如何在处理之前获取AWK中的字段数

我已经编辑了代码:

head -4 10X.txt | awk '
function shuffle(a,n,k){
  for(i=1;i<=k;i++) {
    j=int(rand()*(n-i))+i
    if(j in a) a[i]=a[j]
    else a[i]=j
    a[j]=i;
  }
}
BEGIN{
  FS=" ";OFS="\t"; ncols=10;
  }NR==1{shuffle(tmp_array,NF,ncols);
    for(i=1;i<=ncols;i++){
      printf "%s", $(tmp_array[i]) OFS;
    }
    print "";
  }NR>1{
    printf "%s", $1 OFS;
    for(i=1;i<=ncols;i++){    
      printf "%s", $(tmp_array[i]+1) OFS;
    }
    print "";
    }' 

因为我正在处理单细胞基因表达谱,所以从第二行开始,第一列将是基因名称。 我的输出是:

D4-2_3095   D6-1_3010   D16-2i_1172 D4-1_337    iPSCs-2i_227    D4-2_170    D12-serum_1742  D4-1_1747   D10-2-2i_1373   D4-1_320    
Sox17   0   0   0   0   0   0   0   0   0   0   
Mrpl15  0.987862442831866   1.29176904082314    2.12650693025845    0   1.33257747910871    0   1.58815046312948    1.18541326956528    1.12103842107813    0.656789854017254   
Lypla1  0   1.29176904082314    0   0   0.443505832809852   0.780385141793088   0.57601629238987    0   0   0.656789854017254

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接