Eu tenho dois arquivos grandes delimitados por tabulação (> 10 GB) e sei que quando eles são classificados, eles são idênticos em conteúdo.
No entanto, estou interessado na ordem das linhas e no índice das trocadas quando elas compartilham a mesma "chave" (chave aqui sendo definida como linhas agrupadas com base em Source
colunas Location
).
Em outras palavras, as linhas entre esses dois arquivos só devem ser comparadas entre si quando vierem do mesmo grupo (ou seja, quando compartilharem a mesma Origem e Local).
Por exemplo, no exemplo abaixo, as linhas 4, 5, 6 de file1.tsv
devem ser comparadas com 4, 5, 6 defile2.tsv
Nota: os arquivos são TSV normais. Espaços adicionais só são adicionados aqui para tornar as colunas alinhadas ao centro e à direita para melhor visibilidade. Esses espaços não fazem parte dos arquivos originais
arquivo1.tsv
Identifier Position Source Location
AY1:2301 87 ch1 14
BC1U:4010 105 ch1 14
AC44:1230 90 ch1 15
AJC:93410 83 ch1 16
ABYY:0001 101 ch1 16
ABC:01 42 ch1 16
HH:A9CX 413 ch1 17
LK:9310 2 ch1 17
JFNE:3410 132 ch1 18
MKASDL:11 14 ch1 18
MKDFA:9401 18 ch1 18
MKASDL1:011 184 ch2 50
LKOC:AMC02 18 ch2 50
POI:1100 900 ch2 53
MCJE:09HA 11 ch2 53
ABYCI:1123 15 ch2 53
MNKA:410 1 ch2 53
arquivo2.tsv
Identifier Position Source Location
AY1:2301 87 ch1 14
BC1U:4010 105 ch1 14
AC44:1230 90 ch1 15
ABC:01 42 ch1 16
ABYY:0001 101 ch1 16
AJC:93410 83 ch1 16
HH:A9CX 413 ch1 17
LK:9310 2 ch1 17
MKASDL:11 14 ch1 18
JFNE:3410 132 ch1 18
MKDFA:9401 18 ch1 18
MKASDL1:011 184 ch2 50
LKOC:AMC02 18 ch2 50
MNKA:410 1 ch2 53
POI:1100 900 ch2 53
ABYCI:1123 15 ch2 53
MCJE:09HA 11 ch2 53
Eu quero fazer algo semelhante a um "diff", mas no nível de 'grupo' (onde as linhas são comparadas apenas quando compartilham o mesmo Source
e Location
)
Eu quero extrair os "números de linha" originais quando a ordem das linhas é 'trocada' dentro do mesmo grupo "Fonte/Local" " " (ou chave).
A linha inteira deve corresponder em termos de conteúdo.
Mas não tenho ideia de como fazer isso. Só consigo pensar em escrever um loop for que seria extremamente ineficiente quando meu conjunto de dados original tem milhões de linhas.
Resultado esperado:
Group_Source:Location df1.index df2.index
ch1:16 4 6
ch1:16 6 4
ch1:18 9 10
ch1:18 10 9
ch2:53 14 15
ch2:53 15 17
ch2:53 17 14
Suposições:
- Ambos os dataframes têm o mesmo número de linhas
- Ambos os dataframes são idênticos (apenas a ordem das linhas é trocada, portanto, se ambos forem classificados por Origem, Local, Posição e Identificador, eles serão exatamente idênticos)
- As linhas 'trocadas' sempre correspondem exatamente em termos de conteúdo em todas as colunas
Esta é uma daquelas raras ocasiões em que eu provavelmente usaria
getline
devido ao tamanho de seus arquivos de entrada, então salvamos apenas algumas linhas na memória por vez, em vez de > 10G:Para obter mais informações sobre
getline
, leia http://awk.freeshell.org/AllAboutGetline .O acima funcionaria mesmo se um
Identifier
e/ouPosition
fosse repetido na entrada, pois está comparando todos os 4 campos entre os 2 arquivos. Ele assume que os valores de Origem e Local estão na mesma ordem entre os 2 arquivos, conforme mostrado na entrada de exemplo.Isso é relativamente simples em
awk
. Por exemplo:Explicação
if(FNR==1){ next }
:FNR
contém o número da linha (número do registro) do arquivo que está sendo lido. Portanto, se esta for a primeira linha de qualquer arquivo de entrada, pule-a, pois não queremos processar o cabeçalho.else if(FNR==NR){ ... }
:NR
contém o número da linha de entrada atual, independentemente de qual arquivo está sendo lido. Então, seFNR
for igual aNR
, isso significa que estamos lendo o primeiro arquivo.a[$1]=FNR-1
: então, se este for o primeiro arquivo, armazene o primeiro campo como um índice (chave) em um array associativo cujo valor será o número da linha do arquivo atual (FNR
), mas menos um porque não queremos contar o cabeçalho .else if ( a[$1] != FNR-1 ){
: esteelse if
está vinculado ao anterior, então só entraremos neste seFNR
não for igual aNR
, portanto, somente quando estivermos lendo o segundo arquivo. Portanto, se estivermos lendo o segundo arquivo e o valor armazenado noa
array para o primeiro campo desta linha não for igual ao número da linha do arquivo atual menos um, então queremos imprimir.print $3":"$4, FNR-1, a[$1]
: então imprimimos o 3º campo,:
ae o 4º campo, e então o FNR menos um e o valor armazenado noa
array para este primeiro campo.Finalmente, para tê-lo bem impresso com preenchimento e cabeçalho, use:
Importante : esta abordagem requer que você mantenha uma pequena quantidade de dados na memória para cada linha do primeiro arquivo (com exceção do cabeçalho). Isso pode ser um problema para arquivos grandes, embora provavelmente não na maioria das máquinas em que você provavelmente fará esse tipo de operação. Se isso for um problema, recomendo a resposta de Ed, que deve ser significativamente mais rápida e não ter problemas de memória.