Aquí está mi conjunto de datos:
col1,col2,col3
a,b,c
a,d,f
d,u,v
f,g,h
d,u,g
x,t,k
Rendimiento esperado:
f,g,h
x,t,k
Criteria de selección:
Si algo ocurre col1
varias veces, se eliminarán todas las filas asociadas.
¿Puedo solucionarlo usando Linux sort
o uniq
cualquier otra cosa?
Respuesta1
Aquí hay un enfoque de dos pasadas "sin almacenamiento en búfer" (1)awk
con (solo funcionará en archivos normales).
awk -F',' 'NR==FNR{cnt[$1]++;next} FNR>1&&cnt[$1]==1' input.csv input.csv
Esto procesará el archivo dos veces, por lo que se indica dos veces como argumento en la línea de comandos.
- El argumento
-F','
establece que el separador de campo sea,
. - En la primera pasada, cuando
NR
, el contador de líneas global, es igual aFNR
, el contador de líneas por archivo, registramos la frecuencia con la que cada valor de la columna 1 se encuentra en una matrizcnt
(que toma el valor como "índice de matriz"), pero salte inmediatamente el procesamiento a la siguiente línea. - En la segunda pasada, verificamos si el contador de ocurrencias para el valor actual de la primera columna es exactamente 1 y el número de línea dentro del archivo es mayor que 1 (para omitir el encabezado). Sólo si eso es cierto se imprimirá la línea actual. Esto hace uso de la
awk
sintaxis que una expresión fuera de los bloques de reglas que se evalúatrue
indica queawk
se imprima la línea actual.
(1) En reacción a un comentario que pusesin almacenamiento en búferentre comillas, porque dado que la solución almacenará algunos datos del archivo temporalmente en la RAM,haceviene con el uso de RAM. Sin embargo, no almacenará el contenido del archivo palabra por palabra.ademása cualquier otro dato de mantenimiento de desplazamiento en la RAM (queIconsideraría "almacenamiento en búfer" en el sentido real).
Respuesta2
Suponiendo que el archivo esté, /tmp/data
puede hacerlo con una sola línea de Perl:
perl -e 'while(<STDIN>) { /(^\S+?),/; $show->{$1}=$_; $count->{$1}++;}; foreach(keys %$show) {print $show->{$_} if($count->{$_} == 1);}' < /tmp/data
O más legible...:
while(<STDIN>) { #loop through all lines in the input and put the lines in "$_"
/(^\S+?),/; #Everything before the first "," now ends up in "$1"
$show->{$1} = $_; #a hash will be created with as keys the "$1" and as values the "$_"
$count->{$1}++; #In the hash $count the number of occurrences will be increased everytime the same $1 appears
}
foreach(keys %$show) { #loop trough all lines
print $show->{$_} if($count->{$_} == 1); #only print them if they occur once
}
Respuesta3
awk
única solución
no mantener el orden
awk -F, 'NR>1 { count[$1]++ ; line[$1]=$0 ;} END { for ( c in count) if (count[c] ==1) print line[c]}' data
mantener el orden
awk -F, 'NR>1 { row[a]=$0; col[a]=$1; count[$1]++; ++a; } END { for (i=0; i<a; ++i) if (count[col[i]]==1) print row[i]; }' data
dónde
-F,
decirle a awk que lo use,
como separadorNR>1
después de la primera líneacount[$1]++
contar elemento de la primera columnaline[$1]=$0
linea de tiendaEND
después del final del archivofor ( c in count)
bucle a través del elementoif (count[c] ==1)
si solo unoprint line[c]
línea de impresióna
ycol[]
se utilizan para almacenar el orden de la línea en orden preservando la variante.
esto puede estar en una línea, lo doblo para facilitar la lectura
Respuesta4
decorar/ordenar/usar/desdecorar usando cualquier versión de las herramientas POSIX obligatorias y cualquier carácter en su entrada (a menos que su entrada sea en realidad un CSV con campos entrecomillados que pueden contener comas y/o nuevas líneas, pero todas las demás respuestas también fallarían) y reteniendo el orden de las líneas de entrada para la salida y solo abriendo la entrada una vez para que funcione si la entrada proviene de una tubería o un archivo y sin almacenar toda la entrada en la memoria:
$ awk 'BEGIN{FS=OFS=","} NR>1{print ++cnt[$1], NR, $0}' file |
sort -nt, -k1,1r -k2,2 |
awk -F, '(!seen[$3]++) && ($1==1)' |
cut -d, -f3-
f,g,h
x,t,k