¿Cómo grep filas que tienen cierto valor en una columna específica?

9

Tengo un archivo como el siguiente

  200.000    1.353    0.086
  200.250    1.417    0.000
  200.500    1.359    0.091
  200.750    1.423    0.000
  201.000    1.365    0.093
  201.250    1.427    0.000
  201.500    1.373    0.093
  201.750    1.432    0.000
  202.000    1.383    0.091
  202.250    1.435    0.000
  202.500    1.392    0.087
  202.750    1.436    0.000
  203.000    1.402    0.081
  203.250    1.437    0.001
  203.500    1.412    0.073
  204.000    1.423    0.065
  204.500    1.432    0.055
  205.000    1.441    0.045  

Me gustaría seleccionar solo las filas que tienen en la primera columna el decimal .000 y .500 solo para que la salida sea así

  200.000    1.353    0.086
  200.500    1.359    0.091
  201.000    1.365    0.093
  201.500    1.373    0.093
  202.000    1.383    0.091
  202.500    1.392    0.087
  203.000    1.402    0.081
  203.500    1.412    0.073
  204.000    1.423    0.065
  204.500    1.432    0.055
  205.000    1.441    0.045  
Mohsen El-Tahawy
fuente
2
Se ve bastante fácil. ¿Qué has intentado hasta ahora? ¿Qué problemas tuvo su código?
John1024
tal vez sea fácil para ti, pero lo intenté con grep '.000' | grep '.005' pero también ordena las filas que tienen el mismo valor en otras columnas
Mohsen El-Tahawy
3
Muy bien. La gente aquí es mucho más comprensiva si muestra un intento honesto de resolver el problema usted mismo. El código en tu comentario lo muestra. En el futuro, si incluye intentos como ese en su pregunta, es probable que obtenga mejores respuestas más rápido.
John1024

Respuestas:

14

No usas grep. Uso awk.

"your data" | awk '$1 ~ /\.[05]00/'
azzid
fuente
Muy bien. Tal como está escrito, el código depende de que haya exactamente tres dígitos después del decimal. Sería más robusto de usar awk '$1 ~ /\.[05]0*$/'.
John1024
1
@ John1024, en realidad, tal como está escrito, el código depende de que haya al menos tres dígitos después del decimal. Me inclinaría hacia awk '$1 ~ /\.[05]00$/'mí mismo (requiere exactamente tres dígitos), a menos que tenga razones para pensar que se esperan lugares decimales variables en la entrada.
Comodín el
2
@Wildcard Si hay más de tres, el código puede fallar. Por ejemplo: echo 0.5001 | awk '$1 ~ /\.[05]00/'. Solo funciona de manera confiable si hay exactamente tres.
John1024
4
awk '$1 ~ /\.[50]00/ { print $0 }' myFile.txt

La primera columna $1se comparará con /\.500|\.000/los puntos que se escapan para que sean puntos literales y no regexen ningún carácter que ~sea ​​una coincidencia parcial, e imprima la línea completa$0

Dalvenjia
fuente
2
No hay razón para incluir { print $0 }; esa es la acción predeterminada de Awk.
Comodín el
4

Me gustaría grep solo las filas que tienen en la primera columna el decimal .000 y .500

Mi primer pensamiento

grep '^ *[0-9][0-9][0-9]\.[50]00' filename

Prueba rápida con WSL

$ head testdata
              200.000    1.353    0.086
              200.250    1.417    0.000
              200.500    1.359    0.091
              200.750    1.423    0.000
              201.000    1.365    0.093
              201.250    1.427    0.000
              201.500    1.373    0.093
              201.750    1.432    0.000
              202.000    1.383    0.091
              202.250    1.435    0.000
$ grep '^ *[0-9][0-9][0-9]\.[50]00' testdata
              200.000    1.353    0.086
              200.500    1.359    0.091
              201.000    1.365    0.093
              201.500    1.373    0.093
              202.000    1.383    0.091
              202.500    1.392    0.087
              203.000    1.402    0.081
              203.500    1.412    0.073
              204.000    1.423    0.065
              204.500    1.432    0.055
              205.000    1.441    0.045

Hay formas más concisas de expresar esto.

$ grep -E '^ *[0-9]{3}\.[50]00' testdata
              200.000    1.353    0.086
              200.500    1.359    0.091
              201.000    1.365    0.093
              201.500    1.373    0.093
              202.000    1.383    0.091
              202.500    1.392    0.087
              203.000    1.402    0.081
              203.500    1.412    0.073
              204.000    1.423    0.065
              204.500    1.432    0.055
              205.000    1.441    0.045

Si la primera columna puede tener una parte entera que no sea de 3 dígitos

grep -E '^ *[0-9]+\.[05]00' testdata

En algunas circunstancias, es posible que deba usar [:digit:]en lugar de [0-9].

Y así.

man grep es tu amigo.

RedGrittyBrick
fuente
Este uso de grepes más fácil de usar que el mío. No habría publicado una respuesta si hubiera visto esto primero. ¡Buen trabajo!
Yokai
2

Dependiendo de su caso de uso, también puede usar operaciones numéricas reales:

$ awk '{a = $1 % 1} a == 0 || a == 0.5' /tmp/foo
  200.000    1.353    0.086
  200.500    1.359    0.091
  201.000    1.365    0.093
  201.500    1.373    0.093
  202.000    1.383    0.091
  202.500    1.392    0.087
  203.000    1.402    0.081
  203.500    1.412    0.073
  204.000    1.423    0.065
  204.500    1.432    0.055
  205.000    1.441    0.045

Probado con BSD awk (OSX El Capitan, 20070501) y GNU awk 4.1.4.

muru
fuente
1
Advertencia: probar la igualdad exacta de punto flotante (que utiliza awk) a menudo da resultados "incorrectos" a menos que los valores no tengan una parte fraccionaria (y no tengan una magnitud demasiado grande), o la parte fraccionaria sea "binaria" (exactamente la mitad, un trimestre, etc.), lo cual es cierto para los datos en esta Q pero no en muchos otros que parecen similares a los no iniciados.
dave_thompson_085
1
@ dave_thompson_085 de hecho, pero con gawk puedes usar aritmética de precisión arbitraria , es cierto que no los estoy usando aquí.
muru
2
 grep -e '2[^ ]*.000' -e '2[^ ]*.500' file.txt
príncipe 987
fuente
2

Con awk:

$>awk '$1%.5==0' data.tsv 
200.000 1.353   0.086
200.500 1.359   0.091
201.000 1.365   0.093
201.500 1.373   0.093
202.000 1.383   0.091
202.500 1.392   0.087
203.000 1.402   0.081
203.500 1.412   0.073
204.000 1.423   0.065
204.500 1.432   0.055
205.000 1.441   0.045

Con mlr:

$>mlr --ifs tab --onidx filter '$1%.5==0' data.tsv 
200.000 1.353 0.086
200.500 1.359 0.091
201.000 1.365 0.093
201.500 1.373 0.093
202.000 1.383 0.091
202.500 1.392 0.087
203.000 1.402 0.081
203.500 1.412 0.073
204.000 1.423 0.065
204.500 1.432 0.055
205.000 1.441 0.045
Flo Mismo
fuente
2

Ok, un poco tarde agregando mi contribución, pero creo que vale la pena.

El requisito para cumplir, según el OP, es la primera columna que tiene el valor decimal de .000o .500solo. No hay estipulación en cuanto al valor inicial, ya sea por rango o longitud. Para robustez no debe suponerse que está limitado por nada, excepto que no hay caracteres no estén en blanco antes de la primera columna (o que ya no es la primera columna) y que el contenido de la primera columna se tiene un punto decimal, ., en alguna parte

El OP desea usar grep, lo que imprimirá toda la línea cuando se encuentre una coincidencia, por lo que lo único que debe hacer es crear el patrón que coincida con todo y solo con lo que se requiere.

La simplicidad en sí misma, y ​​no hay razón para usar sedo awkcomo `grep puede manejar la fuente como un archivo o una tubería.

Para grepusar un archivogrep '^[^.]*\.[05]0\{2\}\s' the_file.txt

Para grepdesde una tubería, usemy_command | grep '^[^.]*\.[05]0\{2\}\s'

El patrón es: ^comienza al principio de la línea; [^.], coincide con cualquier carácter no decimal; *, tantas veces como sea posible (sin incluir ninguna); \., coincide con un punto decimal; [05], coincide con un cinco o un cero; 0\{2\}, haga coincidir 2 ceros más (las barras invertidas antes de la llave de apertura y cierre evitan que la cubierta intente expandir la llave); \s, haga coincidir un carácter de espacio en blanco (es decir, el final de la columna; para usarlo en un caso de uso diferente, reemplácelo con el separador de columna, generalmente un comandante, un punto y coma o una pestaña \t).

Tenga en cuenta que esto coincidirá exactamente con lo que solicitó el OP. Será no coincida .5000o .0000aunque numéricamente equivalentes, debido a que las miradas del patrón para un cinco o un cero, seguido por exactamente 2 más ceros seguidos por espacios en blanco. Si eso es significativo, todas las demás respuestas, hasta ahora, fallan en el sentido de que coincidirán con cualquier número de ceros, mayor que 1, después del dígito de prueba. Y a excepción de la respuesta de FloHimself, coincidirán con cualquier cosa en la segunda columna que comience .000 o .500, incluyendo .0003y .500T, y la de FloHimself coincidirá con cualquier cosa que sea matemáticamente equivalente a .0y.5, no importa cuántos ceros haya. El último, aunque no coincide con lo que el OP declaró, es probable que coincida con lo que el OP necesita de todos modos.

Finalmente, si se desea el poder y la velocidad de awk, a pesar de que el OP lo solicitó grep, entonces el comando sería:

Con un archivo awk '$1 ~ /[^.]\.[05]0{2}$/' the_file.txt

Con una pipa my_command | awk '$1 ~ /[^.]\.[05]0{2}$/'

usuario207673
fuente
1

Si insiste en usar grep, entonces esto puede funcionar para usted. Guarde el primer resultado que proporcione en un archivo de texto llamado "file.txt" y luego use el siguiente comando:

grep -e '2[^ ]*.000' file.txt & grep -e '2[^ ]*.500' file.txt

Lo que da una salida de:

200.000    1.353    0.086
200.500    1.359    0.091
201.500    1.373    0.093
201.000    1.365    0.093
202.500    1.392    0.087
202.000    1.383    0.091
203.500    1.412    0.073
203.000    1.402    0.081
204.500    1.432    0.055
204.000    1.423    0.065
205.000    1.441    0.045

No tendrá que guardar la salida en un archivo de texto si ya está en un archivo. Pero en caso de que no se guarde en un archivo, también puede canalizar los datos en el comando grep que proporcioné y debería funcionar al menos hasta que el primer número 2, en la primera columna ya no sea a 2. En ese punto, deberá actualizar el comando grep con el carácter apropiado para imprimir correctamente.

Lo que sucede con este grepcomando dual es que el primero grepse envía al fondo con el &operador. A medida que se envía a segundo plano, el siguiente grepcomando se ejecuta inmediatamente después y le proporciona una salida uniforme. Para que la tarea que necesita completar se realice con mayor facilidad, debe seguir el ejemplo que otros han dado y usar awko incluso sed.

(editar)

De ninguna manera es el mejor o más efectivo uso de grep para sus necesidades, pero debería ser suficiente para que juegue un poco y tenga una mejor sensación de grep.

Yokai
fuente
El primer proceso se ejecuta en segundo plano, pero no está demonizado, lo que incluye la ejecución en segundo plano, pero un poco más. Y es muy poco probable que produzca resultados en el mismo orden que los de entrada; Incluso en su ejemplo bastante pequeño, ya salió mal en la tercera línea.
dave_thompson_085 01 de
No menciona que la salida debe estar en un orden específico. Solo que debe ser específico para .500y .000de la primera columna. Si necesita estar en un orden específico, como de menor a mayor, eso se puede hacer fácilmente. Sin embargo, los primeros 3 dígitos de las primeras columnas que se imprimen están en orden de menor a mayor. Ese es el resultado de la 2[^ ]*.000y 2[^ ]*.500. Es bastante apropiado para lo que pidió el OP.
Yokai el
También tenga en cuenta mi edición para descargo de responsabilidad de eficiencia para el comando que proporcioné.
Yokai