我有一个近 2000 行的输入文件。我必须进行计算并将其打印在第三列中。
我必须对输入文件执行的操作示例:
n ID1_1 n/(n+k)
k ID1_2
输入文件:
10 ID1_1
20 ID1_2
1 ID3_1
9 ID3_2
20 ID20_1
15 ID2_1
300 ID2_2
预期输出:
10 ID1_1 0.33
20 ID1_2
200 ID3_1 0.11
9 ID3_2
20 ID20_1 /*I would just leave it like that*/
15 ID2_1 0.047
300 ID2_2
你有什么简单的方法可以解决吗?感谢您。
答案1
鉴于您显示的输入,以下内容应该有效:
<infile sed -e '$!N;2i\' -e '3k
s|\(\(.* *\).*_1\)\n\(\(.* *\).*_2\)$|[\1 ]P\2d\4+/p[\3]pc|;t
s|^[ _ID0-9]*|[&]pc|;P;D' | dc
对我来说它打印...
10 ID1_1 .333
20 ID1_2
1 ID3_1 .100
9 ID3_2
20 ID20_1
15 ID2_1 .047
300 ID2_2
...因为我将dc
的精度设置为 3,但精度为 10...
10 ID1_1 .3333333333
20 ID1_2
1 ID3_1 .1000000000
9 ID3_2
20 ID20_1
15 ID2_1 .0476190476
300 ID2_2
除了输出精度之外,它也与您的不同预期产出在第三行 - 但我认为这更多是由于问题中的拼写错误?
不管怎样,要理解它,你必须考虑到我必须首先将输出解析为两种形式dc
-dc
将P
打印 a[string]
而不带后面的\n
ewline,或者它将p
打印一个数字或[string]
带有 1 的 a。除了可能将x
字符串作为dc
宏执行之外,它不能对它们做任何其他事情。但是,从数字来看,它的能力非常强。
因此,如果当前行是最后一行sed
,我首先将$!N
ext 行附加到当前行。在第二行,我将字符串插入到 stdout - 这是将精度设置为 3 的命令。!
$
i
3k
dc
然后我尝试替换:
s|\(\(.* *\).*_1\)\n\(\(.* *\).*_2\)$|[\1 ]P\2d\4+/p[\3]pc|
_1
仅当模式空间当前包含至少一个空格,在某一点后紧跟一个\n
ewline 字符,在某一点后跟至少一个空格,在某一点后_2
紧跟$
模式空间的末尾,这才会成功。
这意味着上述替换仅影响线对,例如...
...ID_1
...ID_2
...而不是任何其他人。当它确实影响到它们时,它会将它们的内容转换为可用的dc
脚本。接下来,它t
判断替换是否成功,如果成功,它将从脚本中分支出来,从而打印替换结果并不再执行任何sed
命令。dc
将sed
的标准输出作为标准输入,因此,例如,将sed
前两行更改为如下所示:
[ 10 ID_1 ]P10d20+/p[ 20 ID_2]pc
...dc
然后按以下方式处理该输入:
[ 10 ID_1 ]
- 将方括号之间的字符串推到堆栈顶部(这会将堆栈中已有的所有内容推低一)P
-P
打印堆栈顶部,不带尾随\n
ewline 并将其弹出(这会将其下方堆栈上的所有值加一)10
- 将数字 10 压入栈顶d
-d
复制堆栈顶部20
- 将数字 20 压入栈顶+
- 添加堆栈顶部和堆栈顶部的 2cd(同时弹出两者)并将结果压入栈顶/
- 从堆栈顶部划分 2cd(现在我们d
复制10
)由堆栈顶部(我们的10 20 +
结果) (同时弹出两者)并将结果压入栈顶p
-p
打印堆栈顶部(不弹出它)后面跟着一个尾随的\n
ewline。[ 20 ID_2]
- 将字符串压入栈顶p
-p
打印堆栈顶部(再次,不弹出它)后面跟着一个\n
ewlinec
-c
了解堆栈
所以dc
打印:
10 ID1_1 .333
20 ID1_2
但是,如果sed
没有成功匹配并改变模式空间,如已经描述的那样,那么就剩下其他行来处理。在这种情况下,可以将其sed
第一个序列夹在 和 之间,同时还附加命令。然后,它将模式空间打印到模式空间中第一个出现的 ewline,然后在从剩余的开始之前删除相同的内容。因此,整个过程中都进行单行前瞻,始终打印工作脚本。[ ID_0-9]*
[
]
pc
P
\n
D
sed
dc
dc
这意味着整个文件在流中处理 - 因为dc
和sed
两者在处理时都提供输出。通过这种方式,只要您的输入与问题中的示例类似,您就可以轻松地以相同的方式处理 200 万行,或者实时处理日志文件。
答案2
通过蟒蛇。
#!/usr/bin/python3
import re
import sys
fil = sys.argv[1]
with open(fil) as f:
m = re.split(r'[\n\r]+(?= *\d+\s+ID\d+_1)', f.read())
l = []
for i in m:
l.append(re.sub(r'(?s)^(\s*(\d+)\s+([^_]+)_1)([\n\r]+\s*(\d+)\s+\3_2)$', \
lambda m: m.group(1) + " "+ str(float(m.group(2))/(float(m.group(2))+float(m.group(5)))) + m.group(4),i))
print('\n'.join(l), end = "")
将上面的脚本保存为script.py
然后运行它,
python3 script.py inputfile
例子:
$ python3 f.py file
10 ID1_1 0.3333333333333333
20 ID1_2
1 ID3_1 0.1
9 ID3_2
20 ID20_1
15 ID2_1 0.047619047619047616
300 ID2_2
答案3
您可以用一个awk
命令完成整个事情:
$ awk '{if(NR%2){n=$1;last=$0;}else{print last,n/(n+$1)"\n"$0}}' file
10 ID1_1 0.333333
20 ID1_2
1 ID3_1 0.1
9 ID3_2
15 ID2_1 0.047619
300 ID2_2
这个想法是简单地检查当前行是否为偶数,i)如果是,我们打印上一行(last
)以及所需的计算,ii)如果不是,我们将当前行保存为last
第一个字段作为n
。
您可以使用以下命令控制打印的小数位数printf
:
$ awk '{if(NR%2){n=$1;last=$0;}else{printf "%s %.2f\n%s\n",last,n/(n+$1),$0}}' file
10 ID1_1 0.33
20 ID1_2
1 ID3_1 0.10
9 ID3_2
15 ID2_1 0.05
300 ID2_2
Perl 中也有同样的基本内容:
$ perl -lane 'if($.%2){$n=$F[0];$last=$_;}
else{printf "%s %.2f\n%s\n",$last,$n/($n+$F[0]),$_}' file
10 ID1_1 0.33
20 ID1_2
1 ID3_1 0.10
9 ID3_2
15 ID2_1 0.05
300 ID2_2
答案4
OP 编辑后(参见添加的术语):
awk '
/ID.*_1/{
n=$1
idx=$2
sub("_1","_2",idx)
printf s"%s",$0
s="\n"}
$2==idx{
printf " %.2f\n%s",n/(n+$1),$0}
END{
print""}' file