Jak grepować wiersze o określonej wartości w określonej kolumnie?

9

Mam następujący plik

  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  

Chciałbym grepować tylko wiersze, które mają w pierwszej kolumnie tylko dziesiętne .000 i .500, więc wynik będzie taki:

  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
źródło
2
Wygląda dość łatwo. Czego spróbowałeś do tej pory? Jakie problemy miał twój kod?
John1024
może jest ci łatwo, ale próbowałem z grep '.000' | grep '.005', ale sortuje również wiersze o tej samej wartości w innych kolumnach
Mohsen El-Tahawy
3
Bardzo dobre. Ludzie tutaj są o wiele bardziej życzliwi, jeśli samemu podejmujesz uczciwą próbę rozwiązania problemu. Kod w twoim komentarzu to pokazuje. W przyszłości, jeśli w swoim pytaniu uwzględnisz takie próby, prawdopodobnie szybciej uzyskasz lepsze odpowiedzi.
John1024

Odpowiedzi:

14

Nie używasz grep. Zastosowanie awk.

"your data" | awk '$1 ~ /\.[05]00/'
azzid
źródło
Bardzo dobre. Jak napisano, kod zależy od dokładności trzech cyfr po przecinku. Byłby bardziej niezawodny w użyciu awk '$1 ~ /\.[05]0*$/'.
John1024,
1
@ John1024, tak jak napisano, kod zależy od tego, że po przecinku są co najmniej trzy cyfry. Skłoniłbym się ku awk '$1 ~ /\.[05]00$/'sobie (wymagam dokładnie trzech cyfr), chyba że miałbym powód, by sądzić, że na wejściu spodziewane są zmienne miejsca dziesiętne.
Wildcard
2
@Wildcard Jeśli jest ich więcej niż trzy, kod może się nie powieść. Na przykład: echo 0.5001 | awk '$1 ~ /\.[05]00/'. Działa niezawodnie tylko wtedy, gdy są dokładnie trzy.
John1024
4
awk '$1 ~ /\.[50]00/ { print $0 }' myFile.txt

Pierwsza kolumna $1zostanie dopasowana /\.500|\.000/do kropek, które są znakami ucieczki, aby były dosłowne kropki, nie regexuj żadnego znaku, który ~jest częściowo dopasowany, i wypisz całą linię$0

Dalvenjia
źródło
2
Bez powodu, aby dołączyć { print $0 }; jest to domyślna akcja Awk.
Wildcard
4

Chciałbym grepować tylko wiersze, które w pierwszej kolumnie mają dziesiętne .000 i .500

Moja pierwsza myśl

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

Szybki test przy użyciu 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

Można to wyrazić w bardziej zwięzły sposób.

$ 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

Jeśli pierwsza kolumna może mieć inną niż 3-cyfrową część całkowitą

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

W niektórych okolicznościach może być konieczne użycie [:digit:]zamiast [0-9].

I tak dalej.

man grep jest twoim przyjacielem.

RedGrittyBrick
źródło
To użycie grepjest łatwiejsze w użyciu niż moje. Nie opublikowałbym odpowiedzi, gdybym to pierwszy raz zobaczył. Dobra robota!
Yokai,
2

W zależności od przypadku użycia możesz również użyć rzeczywistych operacji numerycznych:

$ 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

Testowane z BSD awk (OSX El Capitan, 20070501) i GNU awk 4.1.4.

muru
źródło
1
Ostrzeżenie: testowanie dokładnej równości zmiennoprzecinkowej (której używa awk) często daje „złe” wyniki, chyba że wartości nie mają części ułamkowej (i nie są zbyt duże), lub część ułamkowa jest „binarna” (dokładnie połowa, a kwartał itp.), co jest prawdą w przypadku danych w tym Q, ale niewiele innych, które wydają się podobne do niewtajemniczonych.
dave_thompson_085
1
@ dave_thompson_085 wprawdzie, ale z gawk możesz użyć dowolnej arytmetyki precyzji , co prawda nie używam ich tutaj.
mur
2
 grep -e '2[^ ]*.000' -e '2[^ ]*.500' file.txt
książę 987
źródło
2

Z 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

Z 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
FloHimself
źródło
2

Ok, trochę późno dodając swój wkład, ale myślę, że warto.

Wymóg spełnienia w odniesieniu do PO jest pierwszą kolumną mającą wartość dziesiętną .000lub .500tylko. Nie ma ograniczeń co do wiodącej wartości, ani według zakresu, ani długości. Aby zapewnić niezawodność, nie należy zakładać, że jest ograniczany przez cokolwiek poza tym, że przed pierwszą kolumną (lub nie jest to już pierwsza kolumna) nie ma niepustych znaków, a zawartość pierwszej kolumny będzie miała kropkę dziesiętną ., gdzieś w tym.

OP chce użyć grep, który wypisze całą linię po znalezieniu dopasowania, więc jedyne, co można zrobić, to stworzyć wzór, który pasuje do wszystkich i tylko tego, co jest wymagane.

Prostota sama w sobie, i nie ma powodu, aby używać sedlub awkjako `grep, może obsłużyć źródło jako plik lub potok.

Do greppliku użyjgrep '^[^.]*\.[05]0\{2\}\s' the_file.txt

Do grepz potoku użyjmy_command | grep '^[^.]*\.[05]0\{2\}\s'

Wzór jest następujący:, ^zacznij od początku linii; [^.], dopasuj dowolny znak nie dziesiętny; *, tyle razy, ile to możliwe (w tym żaden); \., dopasuj przecinek dziesiętny; [05], dopasuj pięć lub zero; 0\{2\}, dopasuj jeszcze 2 zera (odwrotne ukośniki przed otwieraniem i zamykaniem klamry uniemożliwiają powłoce próby rozszerzenia nawiasu klamrowego); \s, dopasuj biały znak (oznaczający koniec kolumny - aby użyć w innym przypadku użycia, zamień na separator kolumny, zwykle na przecinek, średnik lub tabulator \t).

Zauważ, że będzie to dokładnie zgodne z żądaniem OP. To będzie nie pasuje .5000lub .0000chociaż numerycznie równoważne, ponieważ wygląda wzór dla pięciu lub zero, a następnie dokładnie 2 więcej zer następnie spacji. Jeśli jest to znaczące, wówczas wszystkie inne odpowiedzi, jak dotąd, zawodzą, ponieważ będą pasować do dowolnej liczby zer, większej niż 1, za cyfrą testową. I z wyjątkiem odpowiedzi FloHimself, dopasują wszystko w drugiej kolumnie, która się zaczyna .000 lub .500, włączając .0003i .500T, a ta przez FloHimself dopasuje wszystko, co jest matematycznie równoważne .0i.5, bez względu na liczbę zer. Ten ostatni, choć niezgodny z tym, co stwierdził PO, prawdopodobnie i tak odpowiada temu, czego potrzebuje PO.

Wreszcie, jeśli awkpożądana jest moc i prędkość, nawet jeśli poprosił o to OP grep, wówczas polecenie brzmiałoby:

Z plikiem awk '$1 ~ /[^.]\.[05]0{2}$/' the_file.txt

Z rurką my_command | awk '$1 ~ /[^.]\.[05]0{2}$/'

użytkownik207673
źródło
1

Jeśli nalegasz na użycie grep, może to działać dla ciebie. Zapisałem pierwsze dane wyjściowe w pliku tekstowym o nazwie „file.txt”, a następnie użyłem następującego polecenia:

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

Co daje wynik:

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

Nie musisz zapisywać wyniku w pliku tekstowym, jeśli jest już w pliku. Ale w przypadku, gdy nie jest on zapisywany w pliku, możesz również potokować dane do podanego polecenia grep i powinno ono działać przynajmniej do pierwszej liczby 2, w pierwszej kolumnie nie jest już a 2. W tym momencie musisz zaktualizować polecenie grep odpowiednim znakiem, aby drukować poprawnie.

To podwójne greppolecenie polega na tym, że pierwsze grepjest wysyłane do &operatora w tle . Gdy jest wysyłane do tła, następne greppolecenie jest wykonywane natychmiast po tym, zapewniając jednolity wynik. Aby zadanie, które musisz wykonać, było łatwiejsze do wykonania, powinieneś podążać za przykładem podanym przez innych, a awknawet z niego skorzystać sed.

(edytować)

To nie jest najlepsze lub najskuteczniejsze użycie grepa dla twoich potrzeb, ale powinno wystarczyć, abyś trochę się pobawił i lepiej poczuł grep.

Jokai
źródło
Pierwszy proces działa w tle, ale nie jest demonizowany, co obejmuje działanie w tle, ale o wiele więcej. I jest bardzo mało prawdopodobne, aby wytwarzać dane wyjściowe w tej samej kolejności co dane wejściowe; nawet w twoim dość małym przykładzie już poszło nie tak na trzeciej linii.
dave_thompson_085
Nie wspomina, że ​​dane wyjściowe muszą być w określonej kolejności. Tylko, że muszą być specyficzne do .500i .000od pierwszej kolumnie. Jeśli to konieczne, w określonej kolejności, od najmniejszej do największej, można to łatwo zrobić. Jednak pierwsze 3 cyfry pierwszych drukowanych kolumn są co najmniej w największej kolejności. To wynik 2[^ ]*.000i 2[^ ]*.500. To całkiem pasuje do tego, o co poprosił PO.
Yokai,
Zwróć także uwagę na moją edycję zastrzeżenia dotyczącego wydajności dla podanego polecenia.
Yokai,