파일의 시작과 끝에서 조건이 다른 라인의 평균값

파일의 시작과 끝에서 조건이 다른 라인의 평균값

편집: 명확성을 위해 편집되었으며 도움말을 더 쉽게 얻을 수 있도록 예제 파일을 더 작고 더 재현 가능하게 만들었습니다. 감사해요!

내 파일에는 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-"연도""월""일""시간"-"고도 번호 " 형식이기를 원한다고 가정합니다. .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

두 시간 사이에 날짜가 변경되면 시간 계산이 더 흥미로워지지만 일반적으로 이를 데이터에 표시할 방법이 없으므로 연습으로 남겨두겠습니다(힌트: 종료 시간이 날짜보다 짧은 경우). 시작 시간) 시간 및 간격은 24시간을 초과할 수 없습니다. 그러면 하루가 지났음을 알 수 있으므로 종료 시간에 24시간을 추가할 수 있습니다. 간격이 24시간을 초과할 수 있는 경우 운없는).

관련 정보