ファイルの先頭と末尾で条件が異なる行の平均値

ファイルの先頭と末尾で条件が異なる行の平均値

編集:わかりやすくするために編集され、ヘルプをより簡単にするためにサンプルファイルをより小さく、より再現可能にしました。ありがとうございます!

私のファイルは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は開始時刻と終了時刻です。たぶん、ある種の日付を10進数のコマンドとして使用するかもしれません。日付の平均を求める方法はありますか?あるデータを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-「年」「月」「日」「時間」-「高度番号」の形式であるとします。 .std

2020年1月7日19:00に標高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イベントが発生するたびに外部コマンドを実行することはリソース集約型であり、一部のOSバージョンでは使用できません。私の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

2つの時間の間に日付が変更されると、時間の計算がより面白くなりますが、通常これをデータに表示する方法がないので、練習として残します(ヒント:終了時間が日付より短い場合)。開始時間)時間と間隔は24時間を超えることはできません。これにより、1日が経過したことがわかり、終了時刻に24時間を追加できます。間隔が24時間を超える可能性がある場合は幸運)。

関連情報