Estabelecer um par de cada linha de um arquivo de forma econômica em relação a todos os outros

2

Eu tenho um arquivo muito grande contendo apenas números. arquivo -

123212
234234
12324
1243223
5453443

E quero emparelhar cada linha com todas as outras. Saída como abaixo

123212,234234
123212,12324
123212,1243223
123212,5453443
234234,123212
234234,12324
234234,1243223
234234,5453443
12324,123212
12324,234234
12324,1243223
12324,5453443
1243223,123212
1243223,234234
1243223,12324
1243223,5453443
5453443,123212
5453443,234234
5453443,12324
5453443,1243223

desde que o arquivo de entrada contenha mais de 50L registros. então, fazer isso por meio de um loop será uma operação dispendiosa.

    
por anurag 03.09.2015 / 06:36

4 respostas

1

Todos os métodos para criar este resultado serão dispendiosos. Essa abordagem, no entanto, funcionará mesmo se o arquivo for muito maior que a RAM:

$ while read n; do awk -v n="$n" '$1!=n{print n "," $1}' file; done <file
123212,234234
123212,12324
123212,1243223
123212,5453443
234234,123212
234234,12324
234234,1243223
234234,5453443
12324,123212
12324,234234
12324,1243223
12324,5453443
1243223,123212
1243223,234234
1243223,12324
1243223,5453443
5453443,123212
5453443,234234
5453443,12324
5453443,1243223

Escrito em várias linhas

while read n
do
    awk -v n="$n" '$1!=n{print n "," $1}' file
done <file

read n lê números de file , um de cada vez. Para cada n , o script awk é executado para criar essa parte da saída para a qual n está na primeira coluna. A opção -v n="$n" cria uma variável awk chamada n que tem o mesmo valor que a variável n da shell. A condição $1!=n seleciona as linhas em file para as quais o número nessa linha do arquivo difere de n . Para essas linhas, imprimimos o número n , seguido de uma vírgula, seguido do número na linha.

    
por 03.09.2015 / 06:42
1

Concordo com John, isso vai ser caro, não importa o que aconteça.

join -o 1.2,1.3,2.2,2.3 -j 1 <(awk '{printf "%s %d %s\n", "x", FNR, $0}' file) \
<(awk '{printf "%s %d %s\n", "x", FNR, $0}' file) |
awk '$1 != $3{print $2, $4}'

Você pode iniciar duas instâncias de substituição de processo, cada uma usando awk , retornar o conteúdo do arquivo com dois campos sintéticos inseridos no início de cada registro, o primeiro campo contendo um valor fixo ( x no exemplo acima) e o segundo campo, o número da linha. Isso pode ser alimentado para join estipulando o campo 1 como o campo de junção. Isso faz com que cada registro da primeira instância da substituição do processo corresponda a todos os registros do segundo. Use um pós-processador awk para descartar instâncias de registros correspondentes (usando o fato de que os números de linha serão iguais nesses casos)

    
por 03.09.2015 / 14:32
0

Você também consideraria usar um aplicativo totalmente diferente, como o kdb + ?

(sua versão de 32 bits é gratuita - como em cerveja com um limite de memória de 4 GB)

Algumas noções básicas:

  1. Carregando seu arquivo como uma lista numérica de coluna única.

    flip (enlist "I";",") 0: hsym '$"/path/to/input"
    
    • 0: é uma função multifuncional para carregar a partir do arquivo de entrada. Para o propósito desta pergunta, trate (enlist "I";",") simplesmente como a especificação de formato de arquivo e, em seguida, aplique um flip para transformar a saída em uma lista utilizável.
  2. Aplicando a função cross .

    a cross a:... <from above>
    
    • q (o idioma de kdb +) pode ser bastante conciso, mas isso também significa atribuição de variável (por exemplo, a:42 para definir 42 a a ) pode ser designada e usada de forma ordenada. Aqui, atribuímos nossa entrada de arquivo a uma variável a , para que possamos cross em si.
  3. Prepare a saída da string.

    "," 0: flip a... <from above>
    
    • Mais uma vez, 0: é usado para preparar os resultados em strings delimitadas por vírgulas aqui.
  4. Escreva para o arquivo de saída.

    (hsym '$"/path/to/output") 0: ","... <from above>
    
    • Dessa vez, precisamos de () em torno do argumento esquerdo de 0: para tornar o uso funcional de hsym explícito. Finalmente, 0: é usado aqui pelo tempo terceiro para gravar em um arquivo.

Juntando tudo:

(hsym'$"/path/to/output")0:","0:flip a cross a:flip(enlist"I";",")0:hsym'$"/path/to/input"

E agora, pelas más notícias ...

A limitação de 4 GB de RAM da versão gratuita de 32 bits manipula apenas cerca de 6000 linhas ...

q)\ts (hsym'$"output6k.txt")0:","0:flip a cross a:flip(enlist"I";",")0:hsym'$"test6k.txt"
23428 3378126736
q)count distinct flip (enlist "I";",") 0:hsym'$"test6k.txt"
6000

\ts mostra que o tempo gasto é de pouco menos de 24 segundos, usando-se quase 3,4 GB de memória.

( eu decidi ainda postar isso como uma resposta, para não deixar meus esforços serem desperdiçados ... )

    
por 03.09.2015 / 17:56
0

Crie um banco de dados SQLite para unir cada linha:

sqlite3 tmp.db
sqlite> CREATE TABLE T (x INTEGER);
sqlite> .import input_file T
sqlite> .mode csv
sqlite> .output output_file
sqlite> SELECT * FROM T JOIN T AS S WHERE T.x != S.x;

Esta solução não garante a ordem das linhas de entrada, mas inicia apenas um processo, não tem loops externos e deve funcionar com RAM limitada.

Atualização: Corrija a instrução select para que ela não associe um valor a si mesma. Se valores iguais forem OK, desde que não sejam da mesma linha, use WHERE T.rowid != S.rowid .

    
por 03.09.2015 / 14:36