文件头和尾部不同条件的行的平均值

文件头和尾部不同条件的行的平均值

编辑:为了清晰起见对其进行了编辑,并使示例文件最小化且更具可重复性,以便更轻松地获得帮助。谢谢!

我的文件有 1000 多行。每个文件都使用相同数量的行进行格式化。的格式有 3 个“标题行”、1000 多行值(正值和负值,尾随小数点后 6 位),后面跟着 13 个“尾行”。行的格式如下所示。在我的真实文件中,在某些行上,我想要不同的命令,例如从行打印文本、对实际数据取平均值、复制文本和数据平均值的行以及对日期和时间取平均值。

这是一个大纲或一些长文件,其中包含有关每行目标的一些注释。

下面的概要是一个简化的示例。包含数据的行(示例中的第 4-9 行)实际上是真实文件中的第 4-1436 行。那么大纲中的第 10 行就是实际文件中的第 1437 行。 (希望这是有道理的)。数据行可以包含负数或正数,范围为 -100 到 +5000。

ABCDEFGH               # Line 1... print text into output file (same on across all files)
1                      # Line 2... Take average of values across all the files in this line
2048                   # Line 3... Take average of values across all the files in this line
8.123456               # Line 4... Take average of values across all the files in this line (could be positive or negative)
5.123456               # Line 5... Take average of values across all the files in this line (could be positive or negative)
5.654321               # Line 6... Take average of values across all the files in this line (could be positive or negative)
4.654321               # Line 7... Take average of values across all the files in this line (could be positive or negative)
9.654321               # Line 8... Take average of values across all the files in this line (could be positive or negative)
1.654321               # Line 9... Take average of values across all the files in this line (could be positive or negative)
90.00                  # Line 10... Check and make sure value in this line across print if same
Sprite                 # Line 11... check and see if text is same across all values and print if same
cats10                 # Line 12... check and see if text is same across all values and print if same
07/02/20               # Line 13... See below for explantion on next 3 lines
08:32                  # Line 14...
08:32                  # Line 15...
290.000000             # Line 16... average across all files on this line
10.750000              # Line 17... average across all files on this line
SCANS23                # Line 18... output should be SCANS "average of values"
INT_TIME57500          # Line 19... output should be INT_TIME "sum of values"
SITE northpole         # Line 20...Check if all lines are same if so print line
LONGITUDE -147.850037  # Line 21... Output should be LONGITUDE "average"
LATITUDE 64.859375     # Line 22... Output should be LONGITUDE "average"

第 13 行是数据的来源日期,第 14 行是开始时间和结束时间。可能使用某种日期到十进制命令..有没有办法取日期的平均值?如果一个数据是在 07/02/20 获取的,另一个数据是在 07/02/18 获取的,那么输出可以是 07/02/19 吗?时间的平均值也会被考虑在内。

我认为一些扩展的三元运算符可能是一条路径,但使用这么多不同的情况根本不起作用。

awk -F: '
  FNR==1     { c++ };
  /^LATITUDE/    { a[FNR] += $6 };
  /^LONGITUDE/    { a[FNR] += $5 };
  /^SITE/    { a[FNR] += $4 };
  /^INT_TIME/    { a[FNR] += $3 };
  /^SCANS/    { a[FNR] += $2 };
  /^[+-]?([0-9]*[.])?[0-9]+$/ { a[FNR] += $1 };

  END {
    for (i in a) {
      printf (i==22 ? "LATITUDE%f": 
              i==21 ? "LONGITUDE%2.3f": 
              i==20 ? "SITE%2.3f": 
              i==19 ? "INT_TIME%2.3f": 
              i==18 ? "SCANS%2.3f": "%f") "\n", a[i] / c 
    }
  }' /home/test/test1.* > /home/average

假定所有示例文件都位于其中,/home/test/aaaaaa-bbbb-cc10dddd-L1-2020070119*-01.std并希望“平均”文件输出的/home/dir/aaaaaa-bbbb-cc10dddd-L1-2020070119-01.std格式为 /aaaaaa-bbbb-cc10-dddd-L1-"year""month""day""hour"-"elevation number “.std

输入文件于 2020 年 1 月 7 日 19 点在海拔 1 处拍摄:

/home/dir/dir2/aaaaaa-bbbb-cc10dddd-L1-202007011918-01.std
/home/dir/dir2/aaaaaa-bbbb-cc10dddd-L1-202007011929-01.std
/home/dir/dir2/aaaaaa-bbbb-cc10dddd-L1-202007011941-01.std
/home/dir/dir2/aaaaaa-bbbb-cc10dddd-L1-202007011953-01.std

输出文件将是

/home/dir/aaaaaa-bbbb-cc10dddd-L1-2020070119-01.std

/home/dir/dir2/aaaaaa-bbbb-cc10dddd-L1-202007011918-01.std

ABCDEFGH
1
2048
-3.249389
-4.544701
5.822962
2.372011
-17.937092
20.000408
5.00
Sprite
cats10
07/01/20
19:18
19:18
290.000000
10.690000
SCANS23
INT_TIME57500
SITE northpole
LONGITUDE -147.850037
LATITUDE 64.859375

/home/dir/dir2/aaaaaa-bbbb-cc10dddd-L1-202007011929-01.std

ABCDEFGH
1
2048
-6.369022
-4.957337
-2.715081
1.766033
-20.002853
21.522350
5.00
Avantes
buoy10
07/01/20
19:29
19:29
290.000000
10.310000
SCANS23
INT_TIME57500
SITE giroof
LONGITUDE -147.850037
LATITUDE 64.859375

/home/dir/dir2/aaaaaa-bbbb-cc10dddd-L1-202007011926-01.std

ABCDEFGH
1
2048
2.961413
-14.236549
19.784035
2.711583
-18.305300
9.369226
5.00
Avantes
buoy10
07/02/20
19:26
19:26
290.000000
10.310000
SCANS23
INT_TIME57500
SITE giroof
LONGITUDE -147.850037
LATITUDE 64.859375

答案1

这可能接近您所需要的,paste将(希望不是太多)输入文件放入awk,关闭任何locale影响:

paste file[1-3] | LC_ALL=C awk -v"LNCT=$(wc -l <file1)" '

function avg(  sum)     {for (i=1; i<=NF; i++) sum += $i
                         return sum/NF
                        }

function same()         {for (i=2; i<=NF; i++) if ($1 != $i) return 0
                         return 1
                        }

NR == 1                 {print $1
                         next
                        }
NR <= (LNCT-13) ||
NR >= (LNCT-6)  &&
NR <= (LNCT-5)          {print avg()
                         next
                        }

NR >  (LNCT-13) &&
NR <= (LNCT-10)         {print (same()?$1:"") 
                        }
NR >= (LNCT-9) &&
NR <= (LNCT-7)          {if (NR == (LNCT-9))    FMT = "%m/%d/%y"
                           else                 FMT = "%H:%M"

                         for (i=1; i<=NF; i++)  {CMD = "date +%s -d\"" $i"\""
                                                 CMD | getline  $i
                                                 close (CMD)
                                                }
                         CMD = "date +" FMT " -d\"@" avg() "\""
                         CMD | getline ITEM
                         close (CMD)
                         print ITEM
                        }

                        {ITEM = $1
                         gsub (/[0-9]*/, "", ITEM)
                         if (gsub (/SCANS|INT_TIME|LONGITUDE|LATITUDE/, ""))    {print ITEM, avg()
                                                                                }
                         if (gsub (/SITE/, ""))         print ITEM, (same()?$1:"") 
                        }
'
ABCDEFGH
1
2048
-2.219
-7.91286
7.63064
2.28321
-18.7484
16.964
5.00


07/01/20
19:24
19:24
290
10.4367
SCANS 23
INT_TIME 57500
SITE 
LONGITUDE -147.85
LATITUDE 64.8594

它有点笨拙,因为它通过行号检测“特殊处理”行,尤其是。日期/时间的,但它似乎做了所要求的事情。我们需要预先计算行数,并wc - l通过awk变量传递输出,假设所有文件都具有相同的长度。可能还有其他/更好的方法。对于日期/时间计算:date对于每次发生的事件运行外部命令来说,这是相当消耗资源的,而且并非在所有操作系统版本上都可用。它适用于我的 Linux 系统,但我愿意接受更好的想法。

答案2

这可能就是您正在寻找的内容,在日期平均值计算中使用 GNU awk 进行时间函数,并假设您的时区是 UTC 并且所有日期都是本世纪并且您没有任何空输入行:

$ cat tst.sh
#!/usr/bin/env bash

paste "$@" |
awk '
    BEGIN { FS="\t"; CONVFMT="%0.6f" }
    ( 1 <= NR) && (NR <=  1) { print chkSameStrnums() }
    ( 2 <= NR) && (NR <=  9) { print getTagAveNr() }
    (10 <= NR) && (NR <= 12) { print chkSameStrnums() }
    (13 <= NR) && (NR <= 13) { print getAveDate() }
    (14 <= NR) && (NR <= 15) { print getAveTime() }
    (16 <= NR) && (NR <= 17) { print getTagAveNr() }
    (18 <= NR) && (NR <= 18) { print getTagAveNr() }
    (19 <= NR) && (NR <= 19) { print getTagSumNr() }
    (20 <= NR) && (NR <= 20) { print chkSameStrnums() }
    (21 <= NR) && (NR <= 22) { print getTagAveNr() }

    function sumNrFlds(         i,sum,val) {
        for (i=1; i<=NF; i++) {
            val = $i
            sub(/^[^0-9-]+/,"",val)
            sum += val
        }
        return sum
    }

    function getTagAveNr(       tag) {
        tag = $1
        sub(/[0-9.-]+$/,"",tag)
        return tag (sumNrFlds() / NF)
    }

    function getTagSumNr(       tag) {
        tag = $1
        sub(/[0-9.-]+$/,"",tag)
        return tag sumNrFlds()
    }

    function getAveDate(        i,sum,d,secs) {
        for (i=1; i<=NF; i++) {
            split($i,d,"/")
            secs = mktime("20"d[3] " " d[1] " " d[2] " 12 00 00", 1)
            sum += secs
        }
        return strftime("%m/%d/%y",int(sum/NF))
    }

    function getAveTime(        i,sum,t,ave,hrs,mins) {
        for (i=1; i<=NF; i++) {
            split($i,t,":")
            mins = (t[1] * 60) + t[2]
            sum += mins
        }
        ave = sum/NF
        hrs = int(ave/60)
        mins = int(ave - (hrs * 60))
        return (hrs ":" mins)
    }

    function chkSameStrnums(    i,diff) {
        for (i=2; i<=NF; i++) {
            if ($i != $1) {
                diff = 1
                break
            }
        }
        return (diff ? "different" : $1)
    }
'

$ ./tst.sh file?
ABCDEFGH
1
2048
-2.218999
-7.912862
7.630639
2.283209
-18.748415
16.963995
5.00
different
different
07/01/20
19:24
19:24
290
10.436667
SCANS23
INT_TIME172500
different
LONGITUDE -147.850037
LATITUDE 64.859375

如果两次之间的日期发生变化,时间计算会变得更有趣,但您通常没有办法在数据中表示这一点,所以我将其作为练习(提示:如果结束时间小于开始时间)时间并且您的间隔永远不能超过 24 小时,那么您就知道您已经过了一天,因此可以在结束时间上添加 24 小时 - 如果间隔可以超过 24 小时,那么您就不走运了)。

相关内容