我想从一个表创建多个单独的 CSV 文件。这是一个示例表:
gene REF_S1_host REF_S1_FL S1_host1 S1_host2 S1_FL REF_S2_host REF_S2_FL S2_host1 S2_host2 S2_FL
gene1 1 0 0 0 0 0 0 0 0 0
gene2 1 1 1 1 0 0 0 0 0 0
gene3 0 1 0 0 1 0 0 0 0 0
gene4 1 0 0 0 0 1 0 0 0 0
gene5 0 0 0 0 0 1 0 1 0 0
gene6 1 0 0 0 0 0 0 0 1 1
gene7 0 1 0 0 0 0 0 0 0 1
我想创建一个 CSV(或其他制表符分隔文件):
提取包含“S1”的列标题下包含“1”的所有数据,但对于同一基因,包含“S2”的所有标题的值为“0”。例如:
gene REF_S1_host REF_S1_FL S1_host1 S1_host2 S1_FL REF_S2_host REF_S2_FL S2_host1 S2_host2 S2_FL gene1 1 0 0 0 0 0 0 0 0 0 gene2 1 1 1 1 0 0 0 0 0 0 gene3 0 1 0 0 1 0 0 0 0 0
仅拉出其中任何 REF 文件(S1 或 S2)的值为“1”的行,但所有其他字段(即不包含“REF”的行标题)仅拉出“0”的行。例如:
gene REF_S1_host REF_S1_FL S1_host1 S1_host2 S1_FL REF_S2_host REF_S2_FL S2_host1 S2_host2 S2_FL gene1 1 0 0 0 0 0 0 0 0 0 gene4 1 0 0 0 0 1 0 0 0 0
其中 REF_S1* 包含“1”+ 其中全部其他(即非 REF)S1 样本为“0”+ 其中全部REF_S2* 为“0”+,但任何其他 S2 样本(非 REF)为“1”。例如:
gene REF_S1_host REF_S1_FL S1_host1 S1_host2 S1_FL REF_S2_host REF_S2_FL S2_host1 S2_host2 S2_FL gene6 1 0 0 0 0 0 0 0 1 1 gene7 0 1 0 0 0 0 0 0 0 1
最后,其中任何 *FL 为“1”,所有 *host 为“0”。例如:
gene REF_S1_host REF_S1_FL S1_host1 S1_host2 S1_FL REF_S2_host REF_S2_FL S2_host1 S2_host2 S2_FL gene3 0 1 0 0 1 0 0 0 0 0 gene7 0 1 0 0 0 0 0 0 0 1
但我不知道如何去做这件事。欢迎任何建议。
答案1
我假设
- 正如您(某种程度上)所示,您的数据由空格分隔。
- 您的表始终有十一列(但大概可以有任意数量的行)。
- 单元格值从不包含空格。 (特别是,除了第 1 行(标题)和第 1 列(基因)之外的所有内容都是
0
或1
。)
这很容易与awk
.
-
...在包含“S1”的列标题下包含“1”的所有数据,但对于同一基因,包含“S2”的所有标题的值为“0”。
换句话说,
-
(第 2 列为 1 OR 第 3 列为 1 OR 第 4 列为 1 OR 第 5 列为 1 OR 第 6 列为 1)
AND
第 7 列为 0
AND
第 8 列为 0
AND
第 9 列为 0
AND
第 10 列为 0
AND
第 11 列为 0awk -v OFS=',' ' NR==1 { next } ($2==1 || $3==1 || $4==1 || $5==1 || $6==1) && $7==0 && $8==0 && $9==0 && $10==0 && $11==0 { $1=$1; print } '
OFS
是“输出字段分隔符”。-v OFS=','
告诉awk
用逗号分隔字段写入数据,即使输入是制表符分隔或空格分隔。- 告诉跳过第一行(标题行)
NR==1 { next }
。awk
如果要打印标题行,请将其更改为NR==1 { $1=$1; print; next }
。 - 接下来的两行对上面阐述的 AND/OR 逻辑进行编码。
{ $1=$1; print }
打印该行(如果满足条件)。该$1=$1
方法将第一个字段设置为等于其自身。这听起来似乎没有什么作用。事实上,强制awk
使用新的(用户指定的)输出字段分隔符(我们将其指定为逗号)重建行是一个技巧。如果您改变主意并希望完全按照输入中显示的方式输出行,请删除-v OFS=','
和$1=$1;
。
-
...仅那些对于任何 REF 文件(S1 或 S2)都有“1”值,但对于所有其他字段只有“0”值的行...
awk -v OFS=',' ' NR==1 { next } ($2==1 || $3==1 || $7==1 || $8==1) && $4==0 && $5==0 && $6==0 && $9==0 && $10==0 && $11==0 { $1=$1; print } '
-
其中 REF_S1* 包含“1” + 所有其他(即非 REF)S1 样本为“0” + 所有 REF_S2* 为“0”但任何其他 S2 样本(非 REF)为“1” 。
awk -v OFS=',' ' NR==1 { next } ($2==1 || $3==1) && $4==0 && $5==0 && $6==0 && $7==0 && $8==0 && ($9==1 || $10==1 || $11==1) { $1=$1; print } '
-
...其中任何 *FL 为“1”,所有 *host 为“0”。
awk -v OFS=',' ' NR==1 { next } ($3==1 || $6==1 || $8==1 || $11==1) && $2==0 && $4==0 && $5==0 && $7==0 && $9==0 && $10==0 { $1=$1; print } '
答案2
这个问题的解决方法是使用珀尔分三步进行。
- 编写一个子例程,在给定选择标准正则表达式的情况下,吐出满足标准的字段编号。
- 使用子例程,在读取第一行或标题行时为每个列出的条件生成字段索引。
- 最后,借助这些不同的数组,我们使用从 List::MoreUtils 模块导入的函数执行布尔选择标准。
- 注意:以one-hot方式分别运行四种情况。否则输出将交错。
perl -MList::MoreUtils=any,all -lane '
BEGIN {
sub mkAry {
my $re = shift;
grep { $_ }
map { $h{$_} }
grep { /$re/ } keys %h
}
}
if ($. == 1) {
print;
@h{@F} = (0..$#F);
@S1 = mkAry qr/S1/;
@S2 = mkAry qr/S2/;
@REF = mkAry qr/REF/;
@notREF = mkAry qr/^(?!.*REF)/;
@REF_S1 = mkAry qr/REF_S1/;
@REF_S2 = mkAry qr/REF_S2/;
@notREF_S1 = mkAry qr/^(?!.*REF)(?=.*S1)/;
@notREF_S2 = mkAry qr/^(?!.*REF)(?=.*S2)/;
@FL = mkAry qr/FL/;
@host = mkAry qr/host/;
next;
}
##_1_:
print if
any { $_ == 1 } @F[@S1] and
all { $_ == 0 } @F[@S2]
;
##_2_:
print if
any { $_ == 1 } @F[@REF] and
all { $_ == 0 } @F[@notREF]
;
##_3_:
print if
any { $_ == 1 } @F[@REF_S1] and
all { $_ == 0 } @F[@notREF_S1] and
all { $_ == 0 } @F[@REF_S2] and
any { $_ == 1 } @F[@notREF_S2]
;
##_4_:
print if
any { $_ == 1 } @F[@FL] and
all { $_ == 0 } @F[@host]
;
' file | column -t