Как взять только уникальные строки на основе столбца с помощью Linux cmd?

Как взять только уникальные строки на основе столбца с помощью Linux cmd?

Вот мой набор данных:

col1,col2,col3
a,b,c
a,d,f
d,u,v
f,g,h
d,u,g
x,t,k

Ожидаемый результат:

f,g,h
x,t,k

Критерий выбора:

Если что-либо повторяется col1несколько раз, то все связанные строки будут удалены.

Могу ли я решить эту проблему с помощью Linux sortили uniqчего-то еще?

решение1

Вот «небуферизованный» (1) двухпроходный подход awk(будет работать только с обычными файлами).

awk -F',' 'NR==FNR{cnt[$1]++;next} FNR>1&&cnt[$1]==1' input.csv input.csv 

Это приведет к обработке файла дважды, поэтому он дважды указывается в качестве аргумента в командной строке.

  • Аргумент -F','устанавливает разделитель полей ,.
  • На первом проходе, когда NRглобальный счетчик строк равен FNRсчетчику строк для каждого файла, мы регистрируем, как часто каждое значение в столбце 1 встречается в массиве cnt(который принимает значение как «индекс массива»), но сразу же пропускаем обработку до следующей строки.
  • Во втором проходе мы проверяем, равен ли счетчик вхождений для текущего значения первого столбца точно 1, а номер строки в файле больше 1 (чтобы пропустить заголовок). Только если это так, текущая строка будет напечатана. Это использует синтаксис, awkкоторый выражение вне правил блокирует, что вычисляется как trueинструкция awkдля печати текущей строки.

(1) В ответ на комментарий, который я разместилнебуферизованныйв кавычках, потому что, поскольку решение будет временно хранить некоторые данные из файла в оперативной памяти, оноделаетидут с использованием оперативной памяти. Однако он не будет хранить содержимое файла дословнокроме тогок любым другим данным, сохраняющим прокрутку в оперативной памяти (которыеябудет рассматривать «буферизацию» в прямом смысле).

решение2

Предполагая, что файл есть, /tmp/dataвы можете сделать это с помощью однострочника Perl:

perl -e 'while(<STDIN>) { /(^\S+?),/; $show->{$1}=$_; $count->{$1}++;}; foreach(keys %$show) {print $show->{$_} if($count->{$_} == 1);}' < /tmp/data

Или более читабельно... :

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
}

решение3

awkединственное решение

  1. не соблюдает порядок

    awk -F, 'NR>1 { count[$1]++ ; line[$1]=$0 ;} 
       END { for ( c in count) if (count[c] ==1) print line[c]}' data
    
  2. поддержание порядка

    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
    

где

  • -F,указать awk использовать ,в качестве разделителя

  • NR>1 после первой строки

  • count[$1]++количество элементов первого столбца

  • line[$1]=$0 линия магазина

  • ENDпосле конца файла

  • for ( c in count)цикл через элемент

  • if (count[c] ==1)если только один

  • print line[c]линия печати

  • aи col[]используются для хранения порядка строк в варианте, сохраняющем порядок.

это можно сделать в одну строку, я сворачиваю для удобства чтения

решение4

декорировать/сортировать/использовать/отменять декорирование, используя любую версию обязательных инструментов POSIX и любые символы во входных данных (если только ваши входные данные не являются CSV-файлом с полями в кавычках, которые могут содержать запятые и/или символы новой строки, но тогда все остальные ответы также будут неверными) и сохраняя порядок входных строк для выходных данных и открывая входные данные только один раз, чтобы все работало, если входные данные поступают из канала или файла, и не сохраняя все входные данные в памяти:

$ 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

Связанный контент