Extraindo colunas de um arquivo de texto grande e delimitado

0

Eu tenho um arquivo simples delimitado por pipe de 33 GB.

Eu preciso extrair colunas específicas do arquivo, das quais a primeira e a 20ª coluna atendem a uma condição.

Eu usei o código abaixo para processar o arquivo de entrada.

awk -F"|" '('$1~/^BL|^FR|^GF|^GP|^MC|^MF|^MQ|^NC|^PF|^PM|^RE|^TF|^WF|^YT/&&$20=="TRUE"') {print $0}' <input file> | cut -d'|' -f1-3,6,10,11,13,19,20 >> <output file>

$1 e $20 são a posição da coluna no arquivo de entrada

Este código funciona bem. No entanto, leva aproximadamente 1,5 horas para extrair os dados. Existe uma maneira de processar o arquivo mais rápido?

    
por royal23enfield 09.03.2017 / 14:45

4 respostas

3

Tente com grep .

(
  export LC_ALL=C
  grep -E '^(BL|FR|[GMTW]F|GP|M[CQ]|NC|PM|RE|YT)([^|]*\|){19}TRUE(\||$)' |
    cut -d'|' -f1-3,6,10,11,13,19,20
)

Como sugerido por @don_crissti, e supondo que todas as linhas contenham pelo menos 20 campos, você também pode tentar cortar primeiro o que, dependendo do número e comprimentos dos campos em cada linha, e a proporção de linhas correspondentes pode oferecer melhor desempenho :

(
  export LC_ALL=C
  cut -d'|' -f1-3,6,10,11,13,19,20 |
    grep -xE '(BL|FR|[GMTW]F|GP|M[CQ]|NC|PM|RE|YT).*\|TRUE'
)
    
por 09.03.2017 / 15:14
1

Tente falar maluco? Use a versão 1.34 ou superior. O possível aumento de velocidade para uma tarefa que processa um arquivo grande pode ser 8x no exemplo de uma pessoa:

link

Para fazer uma comparação absoluta com o seu desempenho atual, essa tarefa levou 1 minuto (com mawk) para processar 1 GB. Uma tentativa usando o código Java (JIT) não foi mais rápida.

Além disso, muitos utilitários parecem ter seu desempenho degradado quando o suporte a UTF-8 foi adicionado. A google search sugere que isso pode afetar pelo menos algumas versões do awk de forma muito dramática: Tente executar com a variável de ambiente LC_ALL=C (por exemplo, LC_ALL=C awk ... ).

    
por 09.03.2017 / 15:03
1

Você pode pelo menos se livrar do cut :

awk -F '|' 'BEGIN { OFS=FS } $20 == "TRUE" && /^(BL|FR|GF|GP|MC|MF|MQ|NC|PF|PM|RE|TF|WF|YT)/ { print $1,$2,$3,$6,$10,$11,$13,$19,$20 }' indata >outdata

Não sei se isso é mais rápido, mas evita dividir cada linha em campos pelo menos duas vezes.

Você também pode tentar cortar primeiro as colunas corretas (para reduzir o trabalho de awk apenas para a filtragem):

cut -d '|' -f 'columnspec' indata | awk -F '|' 'BEGIN { OFS=FS } $20 == "TRUE" && /^(BL|FR|GF|GP|MC|MF|MQ|NC|PF|PM|RE|TF|WF|YT)/ { print }' >outdata

Outra abordagem é dividir o arquivo em partes gerenciáveis, filtrá-las em paralelo e concatenar o resultado. Veja o manual de split no seu Unix. Talvez seja necessário usar o sinalizador -a com divisão se você gerar muitas centenas de arquivos, mas eu recomendo contar o número de linhas no arquivo em dados e dividir em aproximadamente 10 arquivos.

    
por 09.03.2017 / 14:59
0

Usando a abordagem por linha do Python

O script abaixo retorna um conjunto arbitrário de colunas em sua linha, se a primeira coluna for igual a uma string definida. Tanto a correspondência necessária quanto as colunas a serem retornadas são argumentos para executar o script. Um exemplo:

python3 /path/to/script.py /path/to/file.txt monkey 3 12 > output.txt

retorna a coluna 0, 2 e 11 (a primeira coluna é 0) das linhas no arquivo.txt, se a primeira coluna for igual a "macaco"

Tempo

Em um arquivo de 30.000.000 de poucos GBs, o script fez o trabalho em menos de um minuto na minha caixa com mais de 10 anos. Como o script lê e processa por linha , podemos presumir que o tempo consumido é mais ou menos linear, e o script fará o trabalho substancialmente mais rápido do que o seu comando.

O script

#!/usr/bin/env python3
import sys

s = sys.argv[2]; cols = [int(n) for n in sys.argv[3:]]

with open(sys.argv[1]) as src:
    for l in src:
        l = l.split("|"); match = l[0].strip()
        if match == s:
            print(match, " ".join(list(l[i].strip() for i in cols)))

Como usar

  1. Copie o script em um arquivo vazio, salve-o como get_cols.py
  2. Execute-o com os seguintes argumentos:

    • o arquivo de origem
    • a correspondência obrigatória da primeira coluna (string)
    • as colunas para saída

    Por exemplo:

    python3 /path/to/get_cols.py Germany 2 12 > output.txt
    

É isso

    
por 09.03.2017 / 17:08