删除作为其他行子字符串的行
Posted
技术标签:
【中文标题】删除作为其他行子字符串的行【英文标题】:Remove lines which are substrings of other lines 【发布时间】:2016-06-04 15:31:22 【问题描述】:如何删除作为文件中其他行的子字符串的行,同时保留包含它们的较长字符串?
我有一个文件,其中包含作为字符串的肽序列 - 每行一个序列字符串。我想保留包含所有序列的字符串并删除所有作为文件中其他行的子字符串的行。
输入:
GSAAQQYW
ATFYGGSDASGT
GSAAQQYWTPANATFYGGSDASGT
GSAAQQYWTPANATF
ATFYGGSDASGT
NYARTTCRRTG
IVPVNYARTTCRRTGGIRFTITGHDYFDN
RFTITGHDYFDN
IVPVNYARTTCRRTG
ARTTCRRTGGIRFTITG
预期输出:
GSAAQQYWTPANATFYGGSDASGT
IVPVNYARTTCRRTGGIRFTITGHDYFDN
输出应该只保留最长的字符串,并删除所有作为最长字符串的子字符串的行。因此,在上面的输入中,第 1、2、4 和 5 行是第 3 行的子字符串,因此输出保留的第 3 行。类似地,第 6、8、9 和 10 行的字符串都是第 7 行的子字符串,因此行7 被保留并写入输出。
【问题讨论】:
什么是“更长”? “最长的两个”? 啊,你想删除任何作为另一个字符串的子字符串的字符串吗?到目前为止,您尝试过什么? gnu.org/software/gawk/manual/html_node/… 是的,我的意思是两个最长的字符串。 抱歉给您带来了困惑。我只是进行了编辑以使其清楚。希望它现在清楚。此外,由于这些是肽序列,我将其转换为 fasta 文件并使用 CD-HIT 程序,该程序将具有 100% 同一性的相似序列聚类并产生输出。稍后,将该 fasta 转换为文本文件以供进一步分析。 【参考方案1】:也许:
input=./input_file
while read -r str
do
[[ $(grep -c "$str" "$input") == 1 ]] && echo $str
done < "$input"
产生:
GSAAQQYWTPANATFYGGSDASGT
IVPVNYARTTCRRTGGIRFTITGHDYFDN
它很慢 - 但很简单..
【讨论】:
【参考方案2】:这应该做你想做的:
$ cat tst.awk
arr[$0]; strs=strs $0 RS
END
for (str in arr)
if ( split(strs,tmp,str) == 2 )
print str
$ awk -f tst.awk file
IVPVNYARTTCRRTGGIRFTITGHDYFDN
GSAAQQYWTPANATFYGGSDASGT
它遍历 arr 中的每个字符串,然后将其用作 split() 的分隔符值 - 如果字符串出现一次,则整个文件内容将被分成两半,因此 split() 将返回 2 但如果字符串是其他字符串的子集,则文件内容将被拆分为多个段,因此拆分将返回大于 2 的数字。
如果一个字符串可以在输入中出现多次,并且您希望它在输出中多次打印(请参阅下面@G.Cito 评论中的问题),那么您可以将上面的内容修改为:
!cnt[$0]++ strs=strs $0 RS
END
for (str in cnt)
if ( split(strs,tmp,str) == 2 )
for (i=1;i<=cnt[str];i++)
print str
【讨论】:
我在底部添加了解释 @EdMorton so of all the strings infile
, awk
只查找和打印那些可以一分为二的字符串(跳过那些不能被分割的或多次分割的)。 ++ 又好又简单!有没有一种简单的方法来处理“长”字符串(我在下面更巴洛克式的 perl 解决方案中称为“主字符串”)多次出现的情况?使用您的 awk
脚本和 perl %uniq
哈希,它将被排除在输出之外。
如果要求只打印一次,那么您只需将第一行更改为!arr[$0]++ strs=strs $0 RS
(习惯上arr
将被命名为seen
或count
在该上下文中使用时) 所以它只在 strs 字符串中出现一次,稍后会被拆分。如果要求打印它与输入中出现的次数一样多,那么您还需要将print str
更改为for (i=1;i<=arr[str];i++) print str
。我更新了我的答案以表明这一点。【参考方案3】:
作为 perl 的“单行”(这应该适用于剪切和粘贴到终端):
perl -E 'chomp(@r=<>);
for $i (0..$#r)
map $uniq$_++ if ( index( $r[$i], $_ ) != -1 ) @r;
for (sort keys %uniq) say if ( $uniq$_ == 1 ); ' peptide_seq.txt
我们从 STDIN (<>
) 读取文件 (peptide_seq.txt
) 并将其保存在 @r
中,这将是一个数组,其中每个元素都是文件中每一行的字符串。
value
是一个数字,当发现一行是另一行的子字符串时递增。使用index
,我们可以check whether a string contains a sub-string 并增加相应的哈希值if
index()
不会返回“未找到”的值(-1
)。
“主”字符串包含所有其他字符串作为它们自己的子字符串,并且只会增加一次,因此我们再次循环以打印具有值 == 1
的 %uniq
哈希的键。第二个循环可能是map
:
map say if ( $uniq$_ == 1 ) sort keys uniq ;
作为一个独立的脚本,可以:
#!perl -l
chomp(@r=<DATA>);
for $i (0..$#r)
map $uniq$_++ if ( index( $r[$i], $_ ) != -1 ) @r ;
map print if ($uniq$_ == 1) sort keys %uniq ;
__DATA__
GSAAQQYW
ATFYGGSDASGT
GSAAQQYWTPANATFYGGSDASGT
GSAAQQYWTPANATF
ATFYGGSDASGT
NYARTTCRRTG
IVPVNYARTTCRRTGGIRFTITGHDYFDN
RFTITGHDYFDN
IVPVNYARTTCRRTG
ARTTCRRTGGIRFTITG
输出:
GSAAQQYWTPANATFYGGSDASGT
IVPVNYARTTCRRTGGIRFTITGHDYFDN
【讨论】:
【参考方案4】:这将帮助您满足您的真正需要:
awk ' 打印长度(), NR, $0 | “排序-rn”' sed_longer.txt |头 -n 2
【讨论】:
以上是关于删除作为其他行子字符串的行的主要内容,如果未能解决你的问题,请参考以下文章