Custo eficiente emparelhar cada linha com linhas de outro arquivo

1

Eu tenho um arquivo muito grande (~ dados de 10Gb) que contém dados no formato abaixo -

'1','1'    
'2','2'    
'3','3'    
'4','4'    
'5','5'    
'6','6'    
'7','7'    
'8','8'    
'9','9'    
'10','10'

e formato de outro arquivo (que tem 300Kb de tamanho) é -

1,2    
1,3    
1,4    
1,5    
1,6    
1,7    
1,8    
1,9    
1,10    
2,1    
2,3    
2,4   
2,5    
2,6    
2,7    
2,8    
2,9

saída desejada -

'1','1','1,2',    
'2','2','1,3',    
'3','3','1,4',    
'4','4','1,5',    
'5','5','1,6',    
'6','6','1,7',    
'7','7','1,8',    
'8','8','1,9',    
'9','9','1,10',    
'10','10','2,1',

desde que o arquivo de entrada contenha mais de 10 milhões de registros. então, fazer isso por meio de um loop será uma operação muito cara.

    
por anurag 28.12.2015 / 07:53

4 respostas

0

Feito isso abaixo -

awk 'FNR==NR{a[i++]=$0; max=i; next} 
{if ((NR % max) == 0) {i=max-1} else {i=(NR%max) - 1}; 
printf "%s,%s\n",$0,a[i]}' smaller_file larger_file

Mas se alguém souber o caminho mais rápido do que isso, sugira

    
por 28.12.2015 / 08:24
0

Parece que você está procurando percorrer o conteúdo do arquivo menor

com awk

awk 'NR == FNR{a[++i]=$0; next}; 
 {print $0, a[FNR % i? FNR % i: i]}' smaller_file larger_file

e python

from itertools import cycle, izip
with open('larger_file') as f1, open('smaller_file') as f2:
    z = izip(f1, cycle(f2))
    for l, m in z:
           print l.rstrip('\n'), m.rstrip('\n')
    
por 28.12.2015 / 17:33
0
paste -d",''," ./file1 - ./file2 - - </dev/null >out

... dados dados de exemplo que são gravados na saída:

'1','1','1,2',
'2','2','1,3',
'3','3','1,4',
'4','4','1,5',
'5','5','1,6',
'6','6','1,7',
'7','7','1,8',
'8','8','1,9',
'9','9','1,10',
'10','10','2,1',
,'2,3',
,'2,4',
,'2,5',
,'2,6',
,'2,7',
,'2,8',
,'2,9',
,'',

É um pouco difícil para eu dizer exatamente quais são os critérios para parar a saída, mas escrever uma saída idêntica à saída do seu exemplo:

{   paste -d",''," ./file1 - ./file2 - - |
    sed -ne's/,/&/4p;t' -eq
}   </dev/null
'1','1','1,2',
'2','2','1,3',
'3','3','1,4',
'4','4','1,5',
'5','5','1,6',
'6','6','1,7',
'7','7','1,8',
'8','8','1,9',
'9','9','1,10',
'10','10','2,1',
    
por 28.12.2015 / 17:29
0

Como muitos já apontaram, colar é a ferramenta certa aqui.

paste -d ,\'\' file1 /dev/null file2 /dev/null

Se file2 for menor que file1 , então paste agirá como se tivesse tantas linhas vazias no final para corresponder a file2 .

Se você quiser agir como se file2 fosse repetido várias vezes, repita várias vezes até atingir a contagem de linhas de file1 .

while true; do cat file2; done | head -n "$(wc -l file1)" |
paste -d ,\'\' file1 /dev/null - /dev/null

Isso requer mais de file1 duas vezes. Dependendo da velocidade relativa de sua CPU e E / S, pode ser mais rápido evitar paste e, em vez disso, usar uma ferramenta que pode processar vários arquivos de maneira mais flexível, como o awk. Aqui está uma solução awk que não requer o carregamento de qualquer arquivo na memória por completo (se file2 for pequeno, o cache de disco cuidará disso de qualquer maneira).

awk -v file2=file2 '
    !getline s <file2 {close(file2); getline s <file2}
    {print $0 ",7" s "7"}' file1

Explicação: getline s <file2 lê a próxima linha de file2 , abrindo-a se necessário. Se isso falhar (porque o final do arquivo foi atingido), feche o arquivo e inicie novamente.

    
por 29.12.2015 / 00:31