Como posso filtrar entre duas matrizes?

7

Arquivo1:

91  23  56  44  87  77
99  34  56  22  22  95
41  88  26  79  60  27
95  55  66  69  92  25

Arquivo2:

pass fail pass pass pass fail
pass fail pass fail fail pass
pass pass fail pass pass fail
pass pass fail pass pass fail

Como eu quero resumir as marcas totais de falhas para cada linha, aqui está a saída esperada.

saída:

100
78
53
91

Gostaria de perguntar como posso fazer a filtragem no arquivo1 com base na palavra "falha" no arquivo2 para obter a soma das marcas de falha.

    
por Owen 08.11.2018 / 09:23

7 respostas

4

Eu não acho que você precise de uma seção END :

awk '
NR == FNR       {for (i=1; i<=NF; i++) F[i,NR] = $i
                 next
                }
                {T = 0
                 for (i=1; i<=NF; i++) T += ($i=="fail")?F[i,FNR]:0
                 print T
                }
' file[12]
100
78
53
91
    
por 08.11.2018 / 15:19
10

Eu usaria uma linguagem de matriz para tal tarefa, por exemplo Oitava GNU.

Supondo que você converteu o arquivo de aprovação / reprovação em valores numéricos, por exemplo:

sed 's/pass/1/g; s/fail/0/g' passfail > passfail.nums

Agora você pode fazer o seguinte:

marks    = dlmread('marks');
passfail = dlmread('passfail.nums');

for i = 1:size(marks)(1)
  sum(marks(i,:)(passfail(i,:) == 0))
end

Saída:

ans =  100
ans =  78
ans =  53
ans =  91
    
por 08.11.2018 / 13:47
7

Embora eu ache que usar awk seja bom para portabilidade, outras linguagens parecem mais fáceis de escrever e ler para esta tarefa. O GNU Octave foi mencionado, mas não vem pré-instalado na maioria das máquinas. Por outro lado, a maioria dos sistemas tem uma versão do python pré-instalada. Aqui está uma versão em python:

for marks, decisions in zip(open('file1').readlines(), open('file2').readlines()):
    row_score = 0
    for mark, decision in zip(marks.split(), decisions.split()):
        if decision == 'fail':
            row_score += int(mark)
    print(row_score)

que retorna as saídas esperadas.

    
por 08.11.2018 / 15:42
6

Aqui está minha abordagem awk :

awk 'NR==FNR{for(i=1;i<=NF;i++) a[NR"-"i]=$i; next} \
            {for(j=1;j<=NF;j++) if($j=="fail") b[FNR]+=a[FNR"-"j]} \
         END{for(k in b) print b[k]}' file1 file2

Awk não suporta arrays bidimensionais, então nós os cozinhamos combinando dois números (linha e campo) no mesmo índice de array. A saída é:

100
78
53
91
    
por 08.11.2018 / 09:55
4
awk '
  BEGIN{ pf=ARGV[2]; ARGV[2]="" }
  { getline l <pf; split(l, a); n=0;
    for(i=1;i<=NF;i++) if(a[i]=="fail") n+=$i;
    print n }
' file1 file2
100
78
53
91

Assim como na versão em python do Maxim, mas diferente de todas as outras respostas, isso está processando os dois arquivos em paralelo, linha por linha, ao invés de carregar um deles inteiro na memória.

    
por 08.11.2018 / 19:28
2

Acho que usar um script Awk tornaria esse requisito um pouco fácil de resolver. Faça algo como abaixo. Eu acho que é um pouco mais lento do que a resposta de jimmij agora postada

#!/usr/bin/awk -f


FNR == NR {
    for(i=1;i<=NF;i++)
        if ( $i == "fail")
            idxArray[FNR] = (idxArray[FNR]) ? (idxArray[FNR]" "i):(i)
        next
}{
    delete Array
    delete Line
    i=""
    j=""
    sum=""
    n=split(idxArray[FNR],Array," ")
    l=split($0,Line," ")
    for (i=1;i<=n;i++)
        for (j=1;j<=l;j++)
            if (Array[i] == j )
                sum += Line[j]
    print sum
}

e execute o script como

awk -f script.awk file2 file1
    
por 08.11.2018 / 10:09
0

One-liner:

paste file[12] | awk '{T=0; for (i=1; i<=NF/2; i++) T += ($(i+NF/2)=="fail")?$i:0; print T}'
100
78
53
91
    
por 09.11.2018 / 09:47