¿Cómo tomar solo filas uniq basadas en una columna usando Linux cmd?

¿Cómo tomar solo filas uniq basadas en una columna usando Linux cmd?

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 col1varias veces, se eliminarán todas las filas asociadas.

¿Puedo solucionarlo usando Linux sorto uniqcualquier 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 a FNR, el contador de líneas por archivo, registramos la frecuencia con la que cada valor de la columna 1 se encuentra en una matriz cnt(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 awksintaxis que una expresión fuera de los bloques de reglas que se evalúa trueindica que awkse 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/datapuede 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

  1. 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
    
  2. 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 separador

  • NR>1 después de la primera línea

  • count[$1]++contar elemento de la primera columna

  • line[$1]=$0 linea de tienda

  • ENDdespués del final del archivo

  • for ( c in count)bucle a través del elemento

  • if (count[c] ==1)si solo uno

  • print line[c]línea de impresión

  • ay col[]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

información relacionada