Среднее по диагонали в матрице - PullRequest
1 голос
/ 15 апреля 2020

У меня есть матрица. например, матрица 5 x 5

$ cat input.txt
1       5.6        3.4     2.2     -9.99E+10
2       3          2       2       -9.99E+10
2.3     3          7       4.4     5.1
4       5          6       7       8
5       -9.99E+10  9       11      13

Здесь я бы хотел игнорировать значения -9,99E + 10.

Я ищу среднее значение всех записей после деления по диагонали. Вот четыре варианта (использование 999 вместо -9.99E+10 для экономии места на графике c):

enter image description here

Я хотел бы усреднить по всем значениям под разными заштрихованными треугольниками. Таким образом, вывод желаемого результата:

$cat outfile.txt
P1U  3.39    (Average of all values of Lower side of Possible 1 without considering -9.99E+10) 
P1L  6.88    (Average of all values of Upper side of Possible 1 without considering -9.99E+10)
P2U  4.90
P2L  5.59   
P3U  3.31
P3L  6.41
P4U  6.16
P4L  4.16

Трудно разработать правильный алгоритм, чтобы написать его на фортране или в скрипте оболочки. Я думаю о следующем алгоритме, но не могу думать, что будет дальше.

step 1: #Assign -9.99E+10 to the Lower diagonal values of a[ij]
      for i in {1..5};do
        for j in {1..5};do
           a[i,j+1]=-9.99E+10
         done
      done
 step 2: #take the average
      sum=0
      for i in {1..5};do
        for j in {1..5};do
          sum=sum+a[i,j]
        done
       done
  printf "%s %5.2f",P1U, sum
  step 3: #Assign -9.99E+10 to the upper diagonal values of a[ij]
      for i in {1..5};do
        for j in {1..5};do
           a[i-1,j]=-9.99E+10
         done
      done
 step 4: #take the average
      sum=0
      for i in {1..5};do
        for j in {1..5};do
          sum=sum+a[i,j]
        done
       done
  printf "%s %5.2f",P1L,sum

Ответы [ 2 ]

2 голосов
/ 15 апреля 2020

вы можете вычислить все за одну итерацию

$ awk -v NA='-9.99E+10' '{for(i=1;i<=NF;i++) a[NR,i]=$i} 
          END {for(i=1;i<=NR;i++) 
                 for(j=1;j<=NF;j++) 
                    {v=a[i,j]; 
                     if(v!=NA) 
                       {if(i+j<=6) {p["1U"]+=v; c["1U"]++} 
                        if(i+j>=6) {p["1L"]+=v; c["1L"]++} 
                        if(j>=i)   {p["2U"]+=v; c["2U"]++} 
                        if(i<=3)   {p["3U"]+=v; c["3U"]++} 
                        if(i>=3)   {p["3D"]+=v; c["3D"]++} 
                        if(j<=3)   {p["4U"]+=v; c["4U"]++} 
                        if(j>=3)   {p["4D"]+=v; c["4D"]++}}} 
                 for(k in p) printf "P%s %.2f\n", k,p[k]/c[k]}' file  | sort

P1L 6.88
P1U 3.39
P2U 4.90
P3D 6.41
P3U 3.31
P4D 6.16
P4U 4.16

Я забыл добавить P2D, но из шаблона должно быть ясно, что нужно сделать.

Для дальнейшего обобщения, как предложено. Утвердите NF==NR, в противном случае диагонали не определены четко. Пусть n=NFn=NR) Можно заменить 6 на n+1 и 3 на ceil(n/2). Который может быть реализован как function ceil(x) {return x==int(x)?x:x+1}

2 голосов
/ 15 апреля 2020

Просто сохраните все значения в массиве, проиндексированном по номеру строки и столбца, а затем в разделе END повторите этот процесс установки начальных и конечных разделителей строки и столбца l oop по мере необходимости при определении циклов для каждого раздела:

$ cat tst.awk
{
    for (colNr=1; colNr<=NF; colNr++) {
        vals[colNr,NR] = $colNr
    }
}
END {
    sect = "P1U"
    begColNr = 1; endColNr = NF; begRowNr = 1; endRowNr = NR
    sum = cnt = 0
    for (rowNr=begRowNr; rowNr<=endRowNr; rowNr++) {
        for (colNr=begRowNr; colNr<=endColNr-rowNr+1; colNr++) {
            val = vals[colNr,rowNr]
            if ( val != "-9.99E+10" ) {
                sum += val
                cnt++
            }
        }
    }
    printf "%s %.2f\n", sect, (cnt ? sum/cnt : 0)

    sect = "P1L"
    begColNr = 1; endColNr = NF; begRowNr = 1; endRowNr = NR
    sum = cnt = 0
    for (rowNr=begRowNr; rowNr<=endRowNr; rowNr++) {
        for (colNr=endColNr-rowNr+1; colNr<=endColNr; colNr++) {
            val = vals[colNr,rowNr]
            if ( val != "-9.99E+10" ) {
                sum += val
                cnt++
            }
        }
    }
    printf "%s %.2f\n", sect, (cnt ? sum/cnt : 0)
}

.

$ awk -f tst.awk file
P1U 3.39
P1L 6.88

Я предполагаю, что с учетом вышеизложенного для обработки первых диагональных половин квадранта вы сможете определить другие диагональные половины квадранта и горизонтальный / вертикальный квадрант половинки тривиальны (просто установите begRowNr в int (NR / 2) +1 или endRowNr в int (NR / 2), или begColNr в int (NF / 2) +1 или endColNr в int (NF / 2), затем l oop через результирующий полный диапазон значений каждого).

...