Eliminación de entradas superpuestas basadas en valores de inicio/fin en bash
Tengo un archivo de entradas delimitadas por tabulaciones que tienen una posición inicial y final
# Name \t Start \t End
Name1 \t 1 \t 3
Name2 \t 7 \t 9
Name3 \t 5 \t 8
Name4 \t 5 \t 6
Quiero eliminar líneas que se superponen con líneas anteriores. En este ejemplo, el resultado deseado sería
Name1 \t 1 \t 3
Name2 \t 7 \t 9
Name4 \t 5 \t 6
Lo que tengo hasta ahora:
#!/bin/bash
while IFS=$'\n' read line; do
# Assign variable names
name=$(echo $line | cut -f 1)
start=$(echo $line | cut -f 2)
end=$(echo $line | cut -f 3)
# I envision an if statement structured so that:
# if [ $end < $PreviousStart ] || [ $start > $PreviousEnd ] ; then echo $line >> output.txt
done < file.txt
Aquí es donde me quedo atascado porque necesitaría verificar cada línea de salida.txt (todas las líneas anteriores de mi archivo original) y solo imprimir $line si la declaración if se cumple para todas las líneas actuales de salida.txt. Estaba pensando que awk podría tener una solución para esto que sea menos tortuosa...
Cualquier ayuda es muy apreciada
Puede mantener las posiciones iniciales y finales anteriores que no se superponen como índices y valores en una matriz en awk para poder iterar fácilmente a través de ellas en cada registro para probar si las posiciones iniciales y finales actuales se superponen con cualquiera de ellas y omitir el registro actual. si lo hacen:
awk '-F\t' '{for(s in a)if($2<=a[s]&&s<=$3)next;a[$2]=$3}1' file.txt
Demostración: https://awk.js.org/?snippet=REkxdr
Supuestos:
- A medida que leemos una nueva línea, debemos comprobar si hay superposiciones con todas las líneas anteriores que no se superponen.
- si una nueva línea no se superpone con ninguna de las líneas anteriores que no se superponen, entonces...
- a) guardamos la nueva línea como un nuevo miembro del grupo de líneas que no se superponen y
- b) imprimir la nueva línea en stdout
Una awk
idea:
awk '
BEGIN { FS=OFS="\t" }
{ for (i=1; i<=cnt; i++) # loop through array of previous lines
if ( ( $2 >= start[i] && $2 <= end[i] ) || # does current "start" overlap with a previous line?
( $3 >= start[i] && $3 <= end[i] ) ) # does current "end" overlap with a previous line?
next # if there is an overlap then skip this line and process the next line of input
start[++cnt] = $2 # we have a new non-overlapping line so save the start and end points
end[cnt] = $3
print # print current line to stdout
}
' file.txt
Esto genera:
Name1 1 3
Name2 7 9
Name4 5 6
#!/usr/bin/bash
declare name
declare -a startArr endArr
declare -i start end overlapping
while IFS=$'\t' read -r name start end; do
if ((${#name}==0)); then continue; fi
overlapping=0
for ((i=0; i<${#startArr[*]}; i++))
{
if (( start >= ${startArr[i]} && start <= ${endArr[i]} || end >= ${startArr[i]} && end <= ${endArr[i]} || start < ${startArr[i]} && end > ${endArr[i]} )); then
overlapping=1;
break;
fi
}
if ((overlapping)); then continue; fi
echo "$name"$'\t'"$start"$'\t'"$end"
startArr+=($start); endArr+=($end)
done < file1
archivo1:
Name1 1 3
Name2 7 9
Name3 5 8
Name4 5 6
Name5 4 10 # edited this line , other case of overlapping