usuń duplikaty na podstawie wartości innej kolumny

9

Mam następujący plik:

AA,true
AA,false
BB,false
CC,false
BB,true
DD,true

Próbuję poszukać duplikatów i usunąć wiersz o wartości kolumny równej true.

jako wynik powinien być:

AA,false
BB,false
CC,false
DD,true
Hani Gotc
źródło
2
Więc .. zachowaj tylko truejeśli jest to pierwsza instancja pierwszej kolumny?
DopeGhoti
1
@RomanPerekhrest Prawdopodobnie dlatego, że jest to wpis unikalny i jest drukowany „tak jak jest”
George Vasiliou,
@RomanPerekhrest, ponieważ DD, prawda nie jest duplikatem, nie mamy innej linii z DD, fałsz.
Hani Gotc,
AA,true AA,false AA,false AA,falseJaka powinna być wydajność w tym przypadku? Rozumiem, że ten wiersz powinien zostać usunięty tylko wtedy, gdy jest zduplikowany i zawiera truejednocześnie. W falsekażdym razie wszystkie wiersze powinny pozostać nietknięte. Oznacza to, że w tym przypadku tylko AA, truezostaną usunięte. Ale wszystkie odpowiedzi pozostawiają tylko jedną linię - AA,false. Po prostu ciekawe :)
MiniMax

Odpowiedzi:

9
awk -F, '$2 == "false" {data[$1]=$2 } $2=="true" { if ( data[$1]!="false" ) { data[$1]=$2 } } END { OFS=","; for (item in data) { print item,data[item] }}' input

Aby rozwinąć skrypt pionowo w celu wyjaśnienia:

BEGIN {
   FS=","         # Set the input separator; this is what -F, does.
}
$2 == "false" {    # For any line whose second field is "false", we
   data[$1]=$2     # will use that value no matter what.
}
$2=="true" {                    # For lines whose second field is "true",
   if ( data[$1]!="false" ) {   # only keep if if we haven't yet seen a
      data[$1]=$2               # "false"
   }
}
END {                           # Now that we have tabulated our data, we
   OFS=","                      # can print it out by iterating through 
   for (item in data) {         # the array we created.
      print item,data[item]
   }
}
DopeGhoti
źródło
@DopeGhoti dobrze wyjaśnione! Masz na to moje +1.
Valentin Bajrami,
14

Prosta wersja:

sort input.txt | awk -F, '!a[$1]++'

„fałsz” sortuje alfabetycznie przed „prawda”, a tutaj polecenie Awk zachowuje pierwszy wiersz tylko dla każdej odrębnej pierwszej wartości pola.

Jeśli chcesz zachować wartość „prawda” zamiast „fałsz”, posortuj ją odwrotnie, przekaż to samo polecenie Awk, a następnie posortuj ponownie w odwrotnej kolejności.

Dzika karta
źródło
1
również, jeśli -uopcja jest dostępna,sort input.txt | sort -t, -u -k1,1
Sundeep
2
@ Sundeep, po co używać dwóch sortpołączeń? Dlaczego nie tylko sort -ut, -k1,1 input.txt ?
terdon
2
@terdon, ponieważ -uzachowa pierwszą znalezioną linię z pliku wejściowego wśród duplikatów ... w danym przypadku dane wejściowe muszą zostać posortowane, zanim -ubędzie można je zastosować ... na przykład: AA,truezostanie wydrukowane zamiast, AA,falseponieważ pojawia się jako pierwsze w danej próbce. z tego samego powodu, dla którego awk -F, '!a[$1]++'sam nie rozwiąże tego problemu
Sundeep
5
perl -F, -lane '
   exists $h{$F[0]} or $h[$h{$F[0]}=@h]=$_;
   $h=$_; /,false$/ or $_=$h for $h[$h{$F[0]}];
   END{ print for @h; }
' duplicates.file

Struktury danych:

  • Hash, %hktórego klucze są pierwszymi polami (AAA, BBB, CCC itp.), A odpowiadającymi im wartościami są liczby określające kolejność napotkania kluczy. Zatem np. Klucz AAA => 0, klucz BBB => 1, klucz CCC => 2.
  • Tablica, @hktórej elementami są linie zawarte w kolejności drukowania. Więc jeśli zarówno prawda, jak i fałsz zostaną znalezione w danych, wówczas wartość fałszu przejdzie do tablicy. OTW, jeśli istnieje jeden typ danych, to byłby obecny.

Innym sposobem jest użycie GNU sed:

sed -Ee '
   G
   /^([^,]*),(false|true)\n(.*\n)?\1,\2(\n|$)/ba
   /^([^,]*)(,true)\n(.*\n)?\1,false(\n|$)/ba
   /^([^,]*)(,false)\n((.*\n)?)\1,true(\n|$)/{
      s//\3\1\2\5/;h;ba
   }
   s/([^\n]*)\n(.*)$/\2\n\1/;s/^\n*//
   h;:a;$!d;g
' duplicates.file

FWIW, kod równoważny POSIX dla powyższego kodu GNU-sed jest wymieniony poniżej:

sed -e '
   G

   /^\([^,]*\),\(false\)\n\(.*\n\)\{0,1\}\1,\2$/ba
   /^\([^,]*\),\(false\)\n\(.*\n\)\{0,1\}\1,\2\n/ba

   /^\([^,]*\),\(true\)\n\(.*\n\)\{0,1\}\1,\2$/ba
   /^\([^,]*\),\(true\)\n\(.*\n\)\{0,1\}\1,\2\n/ba

   /^\([^,]*\),true\n\(.*\n\)\{0,1\}\1,false$/ba
   /^\([^,]*\),true\n\(.*\n\)\{0,1\}\1,false\n/ba

   /^\([^,]*\)\(,false\)\n\(\(.*\n\)\{0,1\}\)\1,true$/{
      s//\3\1\2/
      h
      ba
   }
   /^\([^,]*\)\(,false\)\n\(\(.*\n\)\{0,1\}\)\1,true\n/{
      s//\3\1\2\n/
      h
      ba
   }

   y/\n_/_\n/
   s/\([^_]*\)_\(.*\)$/\2_\1/;s/^_*//
   y/\n_/_\n/

   h;:a;$!d;g
' duplicates.file

Wyjaśnienie

  • W tej metodzie przechowujemy wynik do ostatecznego wydrukowania w przestrzeni wstrzymania.
  • Do każdego odczytanego wiersza dołączamy przestrzeń wstrzymania do obszaru wzoru w celu zbadania bieżącej linii względem istniejącego stanu przestrzeni wstrzymania.
  • Teraz podczas tego porównania może się zdarzyć 5 rzeczy:
    • a) Bieżąca linia pasuje gdzieś w linii wstrzymania i false: false.
      • [AKCJA] Ponieważ znaleziono ten sam stan fałszywy, nic nie rób.
    • b) Bieżąca linia odpowiada gdzieś w linii wstrzymania i true: true.
      • [AKCJA] Ponieważ znaleziono ten sam stan prawdziwy, nic nie rób.
    • c) Bieżąca linia odpowiada gdzieś w linii wstrzymania i true: false.
      • [AKCJA] Ponieważ stan fałszywy już istnieje, nie rób nic.
    • d) Bieżąca linia odpowiada gdzieś w linii wstrzymania i false: true.
      • [DZIAŁANIE] Wymaga to pewnej pracy, ponieważ musimy zastąpić fałszywą linię dokładnie w tej samej pozycji, w której znajduje się prawda.
    • e) Bieżąca linia NIE pasuje nigdzie w linii wstrzymania.
      • [AKCJA] Przenieś obecną linię na koniec.

Wyniki

AA,false
BB,false
CC,false
DD,true

źródło
3

Dla każdego wiersza wejściowego przechowuj wartość drugiego pola w tablicy asocjacyjnej a(używając pierwszego pola jako klucza tablicy) TYLKO, jeśli nie zapisaliśmy jeszcze wartości falsetego klucza. Użyj ,dla separatora pola wejściowego i wyjściowego. Wydrukuj tablicę po przeczytaniu wszystkich linii wejściowych.

$ awk -F, -v OFS=, 'a[$1] != "false" { a[$1] = $2 };
                    END { for (i in a) {print i,a[i]} }' truefalse.txt
AA,false
BB,false
CC,false
DD,true

Istotna różnica między tą wersją a wersją DopeGhoti polega na tym, że ta wersja w ogóle nie dba o wartość $2, tylko dba o wartość, jeśli w ogóle a[$1].

cas
źródło
1

sortRozwiązanie dwuprzebiegowe

sort -k1,1 -k2,2 -t, file | sort -k1,1 -t, -u

Przekazywanie w pierwszej sortkolejności klastrów według pola 1z falserekordami poprzedzającymi truedla każdego bloku rekordów o wspólnej 1wartości pola . Drugie sortprzejście jest ustawione tak, aby uzyskać jeden rekord dla każdej odrębnej wartości w ramach 1uprzejmości terenowej -u. Ponieważ -uimplikuje to sortowanie stabilne, uzyskany w ten sposób jeden rekord jest pierwszym napotkanym rekordem dla każdej odrębnej wartości w polu 1- który jest rekordem falsew drugim polu ze względu na pracę wykonaną przy pierwszym sortprzejściu

iruvar
źródło