【问题标题】:awk: manipulations with multi-column dataawk:多列数据的操作
【发布时间】:2021-05-11 11:49:56
【问题描述】:

以下 AWK 脚本(作为 bash 代码的一部分)从 input.csv 的选定列中提取数字,并对这些数字进行一些简单的统计操作,最终将结果保存为 output.csv 中的 1 行:

awk -F ", *" '                  # set field separator to comma, followed by 0 or more whitespaces
FNR==1 {
   if (n) {                     # calculate the results of previous file
      m = s / n                 # mean
      var = s2 / n - m * m      # variance
      if (var < 0) var = 0      # avoid an exception due to round-off error
      mean[suffix] = m          # store the mean in an array
      rmsd[suffix] = sqrt(var)
      lowest[suffix] = min      # lowest dG
      highest[suffix] = fourth  # dG in cluster with highest pop
   }
   prefix=suffix=FILENAME
   sub(/_.*/, "", prefix)
   sub(/\/[^\/]+$/, "", suffix)
   sub(/^.*_/, "", suffix)
   s = 0                        # sum of $3
   s2 = 0                       # sum of $3 ** 2
   n = 0                        # count of samples
   min = 0                      # lowest value of $3 (assuming all $3 < 0)
   max = 0                      # highest value of $2 (assuming all $2 > 0)
}
FNR > 1 {
   s += $3
   s2 += $3 * $3
   ++n
   if ($3 < min) min = $3       # update the lowest value
   if ($2 > max) {
      max = $2                  # update popMAX
      fourth = $3               # update the value of dG corresponded to topPOP
   }
}
END {
  if (n) {                     # just to avoid division by zero
   m = s / n
   var = s2 / n - m * m
   if (var < 0) var = 0
   mean[suffix] = m
   rmsd[suffix] = sqrt(var)
   lowest[suffix] = min     # most negative dG
   highest[suffix] = fourth  # dG in a cluster with pop(MAX)
  }
   print "Lig(CNE)", "dG(min)", "dG(popMAX)", "dG(mean)"
   for (i in mean)
      printf "%s %.2f %.2f %.2f\n", i, lowest[i],  highest[i], mean[i]
}'  input.csv > output.csv

在使用 input.csv(如下所示)进行操作时,它会从日志的第三列 (dG) 中提取数字:i) 检测第三列 (dG(min) 中的最小值,该最小值始终对应于ID=1),以及第二列最大数对应的dG个数(POPmax):

# input.csv from the folder 10V1_cne_lig12
ID, POP, dG
1, 142, -5.6500 # this is dG min to be extracted
2, 10, -5.5000
3, 2, -4.9500
4, 150, -4.1200 # this is dG corresponded to pop(MAX) to be extracted

最后将结果保存在另一个多列的 output.csv 文件中,其中包含每个已处理 CSV 的部分名称(对应的前缀用作行的 ID),以及有关其 dG(min )、dG(popMAX) 以及为第 3 (dG) 列中的所有数字计算的平均值:

# output.csv
Lig(CNE)    dG(min) dG(popMAX) dG(mean)
lig12       -5.65   -4.12     −5.055

所以 dG(min) 是 input.csv 中 ID=1 的行中 $2 (dG) 的数量(最低 dG),dG(popMAX) 对应于该行中检测到的值 dG,它具有最高值2 美元(POP)

我需要修改脚本的 AWK 部分,在 output.csv 中添加两个附加列,其中包含有关每个对应 dG 值的 input.csv (POP) 的第二列的信息(取自第三列同一日志的列)。所以同样的日志应该是这样的

# output.csv
Lig(CNE).   dG(min) POP(min)    dG(popMAX) POP(max) dG(mean)
lig12       -5.65   (142)       -4.12      (150)    −5.055

在其他世界中,除了在第 3 列执行的操作之外,我还需要考虑第 2 列中的数字,然后在 output.csv 中匹配它们:所以 POP(min) 应该取自第一行的 $2(带有 dG(min) )和 POP(max) 从带有 dG(popMAX) 的行的 $2。

我尝试使用

定义第二列信息
'{print $2}'

但结果 output.csv 与原始 input.csv 的行顺序不匹配(例如,它从不属于 dG(min) 等的行中取出第二列)

【问题讨论】:

  • 您确实意识到for (i in mean) 循环以不同于创建顺序的顺序遍历关联数组?
  • 你是对的!非常感谢您的友好评论,我刚刚编辑了 AWK 代码,实际上我忘记在其中添加一部分 AWK 脚本,该脚本也计算 dG 列的平均值并将值存储在数组中!

标签: awk


【解决方案1】:

请你试试:

awk -F ", *" '                  # set field separator to comma, followed by 0 or more whitespaces
FNR==1 {
   if (n) {                     # calculate the results of previous file
      m = s / n                 # mean
      var = s2 / n - m * m      # variance
      if (var < 0) var = 0      # avoid an exception due to round-off error
      mean[suffix] = m          # store the mean in an array
      rmsd[suffix] = sqrt(var)
      lowest[suffix] = min      # lowest dG
      highest[suffix] = fourth  # dG in cluster with highest pop
      pop_min[suffix] = popmin  # pop in cluster with lowest dG
      pop_max[suffix] = max     # highest pop
   }
   prefix=suffix=FILENAME
   sub(/_.*/, "", prefix)
   sub(/\/[^\/]+$/, "", suffix)
   sub(/^.*_/, "", suffix)
   s = 0                        # sum of $3
   s2 = 0                       # sum of $3 ** 2
   n = 0                        # count of samples
   min = 0                      # lowest value of $3 (assuming all $3 < 0)
   max = 0                      # highest value of $2 (assuming all $2 > 0)
}
FNR > 1 {
   s += $3
   s2 += $3 * $3
   ++n
   if ($3 < min) {
      min = $3                  # update the lowest value
      popmin = $2               # newly introduced variable
   }
   if ($2 > max) {
      max = $2                  # update popMAX
      fourth = $3               # update the value of dG corresponded to topPOP
   }
}
END {
   if (n) {                     # just to avoid division by zero
      m = s / n
      var = s2 / n - m * m
      if (var < 0) var = 0
      mean[suffix] = m
      rmsd[suffix] = sqrt(var)
      lowest[suffix] = min      # most negative dG
      highest[suffix] = fourth  # dG in a cluster with pop(MAX)
      pop_min[suffix] = popmin  # pop in cluster with lowest dG
      pop_max[suffix] = max     # highest pop
   }
      print "Lig(CNE)", "dG(min)", "POP(dGmin)", "dG(popMAX)", "POP(max)", "dG(mean)"
   for (i in mean)
      printf "%s %.2f (%d) %.2f (%d) %.2f\n", i, lowest[i], pop_min[i], highest[i], pop_max[i], mean[i]
}' input.csv
  • dG(popMAX) 关联的最高pop 已分配给 现有变量max
  • 已引入变量popmin 来保存弹出值 与最低 dG 相关。每当min 时更新变量 (最低 dG)已更新。

顺便说一句,minmax 等变量名称越来越少 由于重复扩展,不言自明。可能会更好 重命名它们和/或重构代码以供将来维护。

【讨论】:

  • 效果很好!是的,确实问题是由于变量和最后通过平均数组的循环......非常感谢!
  • 只有一个问题,以便我可以使用数千个 csv 测试脚本。假设我们现在已经 pop_max[suffix] 及其对应的 d(G),定义为最高 [suffix]。我如何将这一行的 ID 定义为单独的值(并在输出中打印),通常位于第一列的 input.csv 中(在该示例中为 ID=4)?非常感谢!
  • 我想你已经知道如何做到这一点了。否则,无论何时要修改脚本,您都需要重复询问。请自己尝试,祝你好运!
  • 如果您尝试后遇到问题,请随时再次询问。
  • 比如通过引入一个新变量 id[suffix] = id, id = $1 或者每次都应该与其他变量一起更新?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-06-19
  • 2021-05-13
  • 1970-01-01
  • 1970-01-01
  • 2023-03-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多