Como converter linha para coluna

1

Por favor, ajude-me a converter os valores das linhas em colunas. Minha entrada file.dat é:

Os dois primeiros campos são string alfa e números respectivamente e a entrada sempre terá o mesmo número de campos, ou seja, 6 .

ENTRADA

A|1|DLT|07:30|10:30|34
A|1|STG|07:30|10:30|NA
A|1|MAIN|06:30|10:30|NA
A|2|STG|07:30|10:30|NA
A|2|UNLD|04:30|10:30|90
B|1|DLT|03:30|10:30|34
B|1|STG|07:30|09:30|NA
B|1|MAIN|07:25|10:30|NA
B|1|UNLD|05:30|12:30|8

Há uma mudança na saída. Os campos de saída têm 18 campos para todas as linhas e cada linha terá primeiro dois campos de entrada, seguido por 3º, ..., 18º campos na ordem abaixo na qual os campos em branco estão faltando linhas no arquivo de entrada e a ordem dos registros é DLT STG, MAIN, UNLD seguindo com os registros correspondentes.

OUTPUT

A|1|DLT|07:30|10:30|34|STG|07:30|10:30|NA|MAIN|06:30|10:30|NA|||| 
A|2|||||STG|07:30|10:30|NA|||||UNLD|04:30|10:30|90
B|1|DLT|03:30|10:30|34|STG|07:30|09:30|NA|MAIN|07:25|10:30|NA|UNLD|05:30|12:30|8
  • O UNLD está ausente para A | 1
  • DLT e MAIN estão ausentes para A | 2
  • nenhum dos registros está faltando

Da mesma forma, se algum dos registros correspondentes ao 3º campo do arquivo de entrada estiver faltando, ele deverá ser substituído por valores em branco para que os campos de saída tenham 18 campos .

    
por Yuva Raj 16.11.2016 / 09:38

3 respostas

2

Minha awk proposição:

awk -F'|' '{ if (a[$1$2] == "") {
               a[$1$2] = $0
             } 
             else {
               a[$1$2] = a[$1$2]","$3"|"$4"|"$5"|"$6
             }
           }
           END {
             for (key in a) {
               print a[key]
             }
           }' <input.txt | sort

Explicação

A opção -F'|' define o separador de campos (que awk usa para analisar campos dentro de uma linha) como caractere '|', pois é assim que o arquivo é formatado.

a[...] é uma matriz. Matrizes em awk funcionam um pouco como os dicionários de python, em vez de índices você pode ter chaves que são de fato cadeias de caracteres. Para cada linha do arquivo de entrada, o teste if (a[$1$2] == "") verifica a chave que corresponde aos dois primeiros campos ( $1$2 = A1 para a primeira linha, por exemplo) se houver uma entrada. Se não (primeiro A|1|... linha lida), toda a linha é armazenada nessa chave ( a[$1$2] = $0 ). Se já houver algo (outra A|1|... line já foi armazenada), então concatenaremos a entrada com uma vírgula e os campos de 3 a 6 serão separados por "|" ( a[$1$2] = a[$1$2]","$3"|"$4"|"$5"|"$6 ).

Finalmente, quando passamos pelo arquivo, precisamos exibir as entradas para cada chave. Fazemos isso em um bloco END (essas instruções são executadas quando todo o arquivo é lido). Para fazer isso, simplesmente passamos por todas as chaves da matriz ( for (key in a) ) e imprimimos a entrada para cada uma delas.

A saída final é canalizada para sort , porque awk não será executado por meio de chaves de matriz em ordem alfanumérica, portanto, é mais limpo classificar a saída para obter a linha A|1|... seguida por A|2|... e assim por diante.

Sua última edição tornou a coisa toda um pouco mais complicada. As instruções awk necessárias ficarão um pouco confusas, então aconselho que você crie um arquivo de script awk (crie um arquivo de texto com .awk extensão, por exemplo, myScript.awk ). Copie o seguinte script dentro dele:

BEGIN { FS="|" }

$3 == "DLT" {
  dlt[$1"|"$2]=$3"|"$4"|"$5"|"$6
  a[$1"|"$2]++
}

$3 == "STG" {
  stg[$1"|"$2]=$3"|"$4"|"$5"|"$6
  a[$1"|"$2]++
}

$3 == "MAIN" {
  main[$1"|"$2]=$3"|"$4"|"$5"|"$6
  a[$1"|"$2]++
}

$3 == "UNLD" {
  unld[$1"|"$2]=$3"|"$4"|"$5"|"$6
  a[$1"|"$2]++
}

END {
  for (key in a) {
    if (dlt[key] == "") dlt[key]="|||"
    if (stg[key] == "") stg[key]="|||"
    if (main[key] == "") main[key]="|||"
    if (unld[key] == "") unld[key]="|||"
    print key"|"dlt[key]"|"stg[key]"|"main[key]"|"unld[key]          
  }
}

Para usá-lo:

awk -f myScript.awk <input.txt | sort

Se você entendeu a explicação para a minha resposta inicial, você deve ser capaz de entender esse algoritmo. Desta vez, fazemos uma matriz para cada tipo de dados (dlt, stg, main e unld) e armazenamos seus valores na chave correspondente aos dois primeiros campos. A matriz " a " é usada para rastrear todas as chaves possíveis. No final, percorremos as chaves da matriz a e, se uma das matrizes de dados estiver vazia nessa chave, preencha-a "|||" como você queria, para que cada linha acabe com 18 campos.

    
por 16.11.2016 / 10:57
0

Uma pequena variação na resposta de Valentin B. :

awk -F"|" '
     {
        key = $1 "|" $2
        a[key] = 1
        b[key][$3] = $3 "|" $4 "|" $5 "|" $6
     }
 END {
        subtype[1] = "DLT"
        subtype[2] = "STG"
        subtype[3] = "MAIN"
        subtype[4] = "UNLD"
        for (key in a)
            {
                output = key
                for (i = 1; i <= 4; i++)
                    {
                        st = subtype[i]
                        if (b[key][st] == "")
                                output = output "||||"
                        else
                                output = output "|" b[key][st]
                    }
                print output
            }
     }' file.dat

Como em resposta de Valentin B. :

  • -F"|" define o separador de campo como | , para que possamos extrair os campos das linhas de entrada.
  • key é definido como A|1 , A|2 ou B|1 (ou, em geral, a concatenação dos dois primeiros campos). Usamos isso para combinar os dados das (até) quatro linhas que se relacionam com essa combinação de teclas.
  • Defina a[key] para 1 para manter um registro dos dados (chaves) que vimos.
  • Defina b[key][$3] para o restante da linha após a chave. Por exemplo, b["A|1"]["DLT"] será definido como "DLT|07:30|10:30|34" . Dessa forma, mantemos um registro de todos os dados que vimos (supondo que as linhas não tenham mais de seis campos, conforme especificado na pergunta). Note que fazemos isso sem saber o que pode estar em $3 .

Depois de lermos todos os dados:

  • Definir uma matriz dos subtipos (ou seja, valores válidos de $3 , que são "DLT", "STG", "MAIN" e "UNLD") por isso, não precisamos listá-los mais de uma vez.
  • Iterar sobre todas as chaves que vimos. Comece a construir a linha de saída.
  • Iterar os quatro subtipos (listados acima). Se tivermos dados para essa chave e esse subtipo, anexá-lo à linha de saída; caso contrário, anexe quatro campos em branco.
  • Imprima a linha.

Canalize isso em sort se quiser que a saída seja classificada. (Eu digo "se" porque, enquanto a pergunta mostra o resultado do exemplo classificado, não diz que a saída deve ser classificada.)

A pergunta é imprecisa sobre o que será no segundo campo; diz apenas que são "números". Se eles podem ser inteiros com números variados de dígitos, e você quer que eles sejam classificados como números, use

sort -t"|" -k1,1 -k2,2n
    
por 24.11.2016 / 02:38
-1

capture os valores do 4º campo e armazene-os na variável chamada "val"

crie uma matriz e anexe o valor da matriz com base no primeiro e no segundo campos.

no final, imprima os valores da matriz e faça pequenos ajustes (substituir, remover..etc)

$ awk -F\| '{ val=$3;
              for (i=4;i<=NF;i++) {val=val"|"$i}
              Arr[$1OFS$2]=Arr[$1OFS$2]","val;
              next
            }
        END { for (i in Arr) {
                  A=Arr[i];
                  sub(" ","|",i);
                  print i,A
              }
            }' test.txt | sed "s/ ,/\|/"
A|1|DLT|07:30|10:30|34 ,STG|07:30|10:30|NA ,MAIN|06:30|10:30|NA
B|1|DLT|03:30|10:30|34 ,STG|07:30|09:30|NA ,MAIN|07:25|10:30|NA ,UNLD|05:30|12:30|8
A|2|UNLD|04:30|10:30|90 ,DLT|08:30|11:30|4 ,STG|07:30|10:30|NA
    
por 16.11.2016 / 10:18