添加一列中的每两连续行并将其除以前者并打印

添加一列中的每两连续行并将其除以前者并打印

我有一个近 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-dcP打印 a[string]而不带后面的\newline,或者它将p打印一个数字或[string]带有 1 的 a。除了可能将x字符串作为dc宏执行之外,它不能对它们做任何其他事情。但是,从数字来看,它的能力非常强。

因此,如果当前行是最后一行sed,我首先将$!Next 行附加到当前行。在第二行,我将字符串插入到 stdout - 这是将精度设置为 3 的命令。!$i3kdc

然后我尝试替换:

s|\(\(.*  *\).*_1\)\n\(\(.*  *\).*_2\)$|[\1 ]P\2d\4+/p[\3]pc|

_1仅当模式空间当前包含至少一个空格,在某一点后紧跟一个\newline 字符,在某一点后跟至少一个空格,在某一点后_2紧跟$模式空间的末尾,这才会成功。

这意味着上述替换仅影响线对,例如...

...ID_1
...ID_2

...而不是任何其他人。当它确实影响到它们时,它会将它们的内容转换为可用的dc脚本。接下来,它t判断替换是否成功,如果成功,它将从脚本中分支出来,从而打印替换结果并不再执行任何sed命令。dcsed的标准输出作为标准输入,因此,例如,将sed前两行更改为如下所示:

[ 10 ID_1 ]P10d20+/p[ 20 ID_2]pc

...dc然后按以下方式处理该输入:

  • [ 10 ID_1 ]- 将方括号之间的字符串推到堆栈顶部(这会将堆栈中已有的所有内容推低一)
  • P-P打印堆栈顶部,不带尾随\newline 并将其弹出(这会将其下方堆栈上的所有值加一)
  • 10- 将数字 10 压入栈顶
  • d-d复制堆栈顶部
  • 20- 将数字 20 压入栈顶
  • +- 添加堆栈顶部和堆栈顶部的 2cd(同时弹出两者)并将结果压入栈顶
  • /- 从堆栈顶部划分 2cd(现在我们d复制10由堆栈顶部(我们的10 20 +结果) (同时弹出两者)并将结果压入栈顶
  • p-p打印堆栈顶部(不弹出它)后面跟着一个尾随的\newline。
  • [ 20 ID_2]- 将字符串压入栈顶
  • p-p打印堆栈顶部(再次,不弹出它)后面跟着一个\newline
  • c-c了解堆栈

所以dc打印:

 10 ID1_1 .333
 20 ID1_2

但是,如果sed没有成功匹配并改变模式空间,如已经描述的那样,那么就剩下其他行来处理。在这种情况下,可以将其sed第一个序列夹在 和 之间,同时还附加命令。然后,它将模式空间打印到模式空间中第一个出现的 ewline,然后在从剩余的开始之前删除相同的内容。因此,整个过程中都进行单行前瞻,始终打印工作脚本。[ ID_0-9]*[]pcP\nDseddcdc

这意味着整个文件在流中处理 - 因为dcsed两者在处理时都提供输出。通过这种方式,只要您的输入与问题中的示例类似,您就可以轻松地以相同的方式处理 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

相关内容