Awk 基于另一列减去一列中的值

Awk 基于另一列减去一列中的值,awk,Awk,我有如下输入文件 100A 2000 100B 150 100C 800 100A 1000 100B 100 100C 300 我想为第1列中的每个uniq值减去第2列中的值 所以输出应该是这样的 100A 1000 100B 50 100C 500 我试过了 awk '{if(!a[$1])a[$1]=$2; else a[$1]=$2-a[$1]}END{ for(i in a)print i" " a[i]}' file 但问题是: 100A 0 100B 0 100C 0

我有如下输入文件

100A 2000
100B 150
100C 800
100A 1000
100B 100
100C 300
我想为第1列中的每个uniq值减去第2列中的值 所以输出应该是这样的

100A 1000
100B 50
100C 500
我试过了

 awk '{if(!a[$1])a[$1]=$2; else a[$1]=$2-a[$1]}END{ for(i in a)print i" " a[i]}' file 
但问题是:

100A 0
100B 0
100C 0

请告知

此awk一号班轮可完成以下工作:

 awk '{if($1 in a)a[$1]=a[$1]-$2;else a[$1]=$2}
      END{for(x in a) print x, a[x]}' file

此awk one liner完成以下工作:

 awk '{if($1 in a)a[$1]=a[$1]-$2;else a[$1]=$2}
      END{for(x in a) print x, a[x]}' file

您可以使用此
awk

awk 'a[$1]{a[$1]=a[$1]-$2; next} {a[$1]=$2} END{for(v in a){print v, a[v]}}' file

您可以使用此
awk

awk 'a[$1]{a[$1]=a[$1]-$2; next} {a[$1]=$2} END{for(v in a){print v, a[v]}}' file
在同一主题上有如此多(轻微)的变化

awk '
  !($1 in a) {a[$1]=$2; next}
  {a[$1]-=$2}
  END {for (i in a) printf "%s %d\n",i,a[i]}
' input.txt
如果你愿意的话,把它叠成一行

请记住,awk结构由多个
条件{statement}
对组成,因此有时您可以比使用
if..else
更优雅地表达您的需求。(这里并不是说情况就是这样——这是一个足够简单的awk脚本,它可能并不重要,除非你是一个纯粹主义者。:)

此外,请注意在问题中的
if
条件下测试值的方式。请注意,
a[$1]
都测试该数组索引处的值是否为非零,如果该索引以前不存在,则会导致该索引以空值存在。如果要检查索引是否存在,请在中使用
$1


根据对您的问题的评论进行更新

如果你想从第一个条目中减去最后一个条目,忽略中间的条目,那么你需要记录你的第一个和最后一个条目。这样的事情可能就足够了

awk '
  !($1 in a){a[$1]=$2;next}
  {b[$1]=$2}
  END {for(i in b)if(i in a)print i,a[i]-b[i]}
' input.txt
请注意,正如Ed提到的,这会以随机顺序生成输出。如果希望输出有序,则需要一个附加数组来跟踪顺序。例如,这将使用第一次看到项目的顺序:

awk '
  !($1 in a) {
    a[$1]=$2;
    o[++n]=$1;
    next
  }
  {
    b[$1]=$2
  }
  END {
    for (n=1;n<=length(o);n++)
      print o[n],a[o[n]]-b[o[n]]
  }
' i
awk'
!(每星期一元){
a[$1]=$2;
o[++n]=$1;
下一个
}
{
b[$1]=$2
}
结束{
因为(n=1;n在同一主题上有如此多(轻微)的变化

awk '
  !($1 in a) {a[$1]=$2; next}
  {a[$1]-=$2}
  END {for (i in a) printf "%s %d\n",i,a[i]}
' input.txt
如果你愿意的话,把它叠成一行

请记住,awk结构由多个
条件{statement}
对组成,因此有时您可以比使用
if..else
更优雅地表达您的需求(这里并不是说这种情况-这是一个足够简单的awk脚本,可能不重要,除非您是一个纯粹主义者。:)

另外,注意在问题中的
if
条件下测试值的方式。注意
a[$1]
这两种方法都测试该数组索引处的值是否为非零,如果该索引以前不存在,则会导致该索引以空值存在。如果要检查索引是否存在,请在
中使用
$1


根据对您的问题的评论进行更新

如果你想从第一个条目中减去最后一个条目,忽略中间的条目,那么你需要记录你的第一个条目和最后一个条目。类似的东西可能就足够了

awk '
  !($1 in a){a[$1]=$2;next}
  {b[$1]=$2}
  END {for(i in b)if(i in a)print i,a[i]-b[i]}
' input.txt
请注意,如Ed所述,这将以随机顺序生成输出。如果您希望对输出进行排序,则需要额外的数组来跟踪顺序。例如,这将使用项目第一次看到的顺序:

awk '
  !($1 in a) {
    a[$1]=$2;
    o[++n]=$1;
    next
  }
  {
    b[$1]=$2
  }
  END {
    for (n=1;n<=length(o);n++)
      print o[n],a[o[n]]-b[o[n]]
  }
' i
awk'
!(一年一美元){
a[$1]=$2;
o[++n]=$1;
下一个
}
{
b[$1]=$2
}
结束{

对于awk中的(n=1;n),使用条件运算符进行值放置/减法以保持紧密:

$ awk '{ a[$1]+=($1 in a?-$2:$2) } END{ for(i in a)print i, a[i] }' file
100A 1000
100B 50
100C 500
解释:

{ 
    a[$1]+=($1 in a?-$2:$2)  # if $1 in a already, subtract from it 
                                # otherwise add value to it
} 
END { 
    for(i in a)              # go thru all a
        print i, a[i]          # and print keys and values
}

在awk中,使用条件运算符进行值放置/减法以保持紧密:

$ awk '{ a[$1]+=($1 in a?-$2:$2) } END{ for(i in a)print i, a[i] }' file
100A 1000
100B 50
100C 500
解释:

{ 
    a[$1]+=($1 in a?-$2:$2)  # if $1 in a already, subtract from it 
                                # otherwise add value to it
} 
END { 
    for(i in a)              # go thru all a
        print i, a[i]          # and print keys and values
}

根据您提供的示例输入,您只需:

$ awk '$1 in a{print $1, a[$1]-$2} {a[$1]=$2}' file
100A 1000
100B 50
100C 500

如果这还不是您所需要的全部,那么提供更具代表性的样本输入/输出,包括那些不够好的情况。

鉴于您提供的样本输入,您所需要的只是:

$ awk '$1 in a{print $1, a[$1]-$2} {a[$1]=$2}' file
100A 1000
100B 50
100C 500

如果这还不是您所需要的全部,那么就提供更具代表性的示例输入/输出,包括那些不够好的情况。

使用If/else而不是awk提供的
条件{statement}的隐式结构是否有好处
constructs?使用if/else而不是awk为
condition{statement}
constructs提供的隐式结构是否有好处?我同意,但我在索引不存在时分配了一个值a[$1]=$2,那么我的解决方案有什么问题吗?您的测试是
if(!a[$1])
,它不测试是否不存在,它会导致索引存在(如果它还没有存在)我不确定你为什么会得到你在问题中提到的结果,但是你已经把减法倒过来了。你从第一个值中减去第二个值,所以当我运行你的示例代码时,我看到负数(1000-2000100-150300-800)。它们可能是负数,因为我必须找到整数差,我刚刚意识到我的解决方案也在工作,而且我的解决方案没有问题。我只是用错误的输入文件运行它。否,您的解决方案有问题,当遇到的第一个$2值为零时,它将失败。请参阅@ghoti的评论了解原因。仅供参考(array)现在是POSIX,所以现在人们普遍认为它是可移植的。你可以使用
for(n=1;n in o;n++)
来保证所有AWK的可移植性,而你不需要在
中测试
o[n],因为它不可能是可移植的。我同意,但我已经指定了一个值a[$1]=$2当索引不存在时,那么我的解决方案有什么问题?您的测试是
如果(!a[$1])
,它不会测试索引是否不存在,它会导致索引存在(如果它不存在)我不确定你为什么会得到你在问题中提到的结果,但是你已经把减法倒过来了。你从第一个值中减去第二个值,所以当我运行你的示例代码时,我看到负数(1000-2000100-150300-800)。它们可能是负数,因为我必须找到整数差,我刚刚意识到我的解决方案也在工作,我的解决方案没有问题。我只是用错误的输入文件运行它