Подсчет уникальных значений в одном столбце на основе дубликатов в другом столбце

У меня есть файл с идентификатором и значением:

ABC123 111111
ABC123 111111
ABCDEF 333333
ABCDEF 111111
CCCCCC 333333
ABC123 222222
DEF123 444444
DEF123 444444

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

ABCDEF 2
ABC123 2
DEF123 1
CCCCCC 1

...где первый столбец — это идентификатор, а второй столбец — количество уникальных значений во втором столбце. Другими словами, мне нужно узнать, сколько уникальных значений существует для данного идентификатора.

Ближе всего я подошел к этому, но все, что он делает, это подсчитывает уникальные значения первого столбца:

cut -d " " -f1 "file.txt" | uniq -cd | sort -nr | head

Как бы мне сделать что-то подобное в Bash?

Начнем с сортировки: sort PHA-DC.txt | cut -d " " -f1 | uniq …

pmf 20.08.2024 21:42

сколько уникальных значений существует для данного идентификатора, также будет напечатано 2 для «ABC123». Вы действительно хотите считать идентификаторы только там, где дубликаты вообще не встречаются?

Andre Wildberg 20.08.2024 22:01

@AndreWildberg Хорошая уловка, вы правы, что ABC123 из примеров также должен выдавать 2.

Hashim Aziz 20.08.2024 22:09

Поскольку мы очищаем тестовые данные, DEF123 не имеет двух разных значений, оно встречается с 444444 дважды.

Costi Ciudatu 20.08.2024 22:12

Почему DEF123 2 сейчас не выводится?

anubhava 20.08.2024 22:16

Благодаря вам обоим оказалось, что писать хорошие исчерпывающие примеры сложно, или я просто очень устал.

Hashim Aziz 20.08.2024 22:18

Может ли серийный противник моих вопросов объяснить, почему?

Hashim Aziz 20.08.2024 23:44
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
7
70
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Это awk должно сработать для вас:

awk '{
   ++c1[$1]               # frequency of 1st column
   uq[$0]                 # counts of full record
}
END {
   for (i in uq) {        # store frequency of uniques in fq
      sub(/ .*/, "", i)
      ++fq[i]
   }
   for (i in fq)          # print output from fq
      if (c1[i] > 1)
         print i, fq[i]
}' file

ABCDEF 2
DEF123 1
ABC123 2
Ответ принят как подходящий

Это достаточно близко?

$ sort -u file.txt | cut -d' ' -f1 | uniq -c
   2 ABC123
   2 ABCDEF
   1 CCCCCC
   1 DEF123

Вы можете дополнительно отфильтровать его с помощью | grep -vw '1', чтобы имитировать семантику HAVING COUNT(DISTINCT value) > 1 и исключить последние две строки из вывода в этом примере (при условии, что 1 не является допустимым значением идентификатора!).

И вы, конечно, можете изменить порядок столбцов несколькими способами. Например.

$ sort -u file.txt  |        # sort and eliminate multiple occurrences of the same '<identifier> <value>' pair
    cut -d' ' -f1   |        # keep only the identifier
    uniq -c         |        # collapse and count occurrences of the same identifier
    grep -vw '1'    |        # eliminate rows containing the word '1', assuming this can only be a count value, never an identifier!
    awk '{print $2 " " $1}'  # reverse column order to show '<identifier> <count>'
ABC123 2
ABCDEF 2

Только что протестировал и отлично работает, спасибо, особенно полезно иметь возможность связывать grep -v, когда я хочу просмотреть только строки с более чем 1 или даже 2 дубликатами, хотя стоит отметить, что мне также нужен sort -rn для эмуляции сортировки.

Hashim Aziz 20.08.2024 23:40

С GNU awk (для многомерных массивов):

awk '
    !seen[$1][$2]++ {++uniqs[$1]}
    END {for(id in uniqs) print id, uniqs[id]}
' file.txt
DEF123 1
ABC123 2
ABCDEF 2
CCCCCC 1

С помощью стандартного awk вы можете использовать seen[$1,$2] для эмуляции двумерного массива.

Barmar 20.08.2024 22:55

Ты прав; теперь, когда вы об этом упомянули, можно использовать даже seen[$0]

Fravadona 20.08.2024 23:58

Предположения:

  • каждая строка имеет две строки, разделенные пробелами
  • повторяющиеся строки действительно являются дубликатами (например, они имеют одинаковое количество начальных, встроенных и конечных пробелов)

Еще один awk подход:

awk '
    { lines[$0] }                            # capture unique lines
END { for (line in lines) {                  # loop through list of unique lines
          split(line,a)                      # split line on white space
          counts[a[1]]++                     # count number of times we see the first field (aka "id")
      }
      for (id in counts)                     # loop through list of id
          print id, counts[id]               # print id and count
    }
' file.txt

Это генерирует:

ABC123 2
DEF123 1
ABCDEF 2
CCCCCC 1

Если выходные данные необходимо упорядочить, передайте результаты соответствующей команде sort, например:

$ awk '<see script from above>' file.txt | sort -k2,2nr -k1,1r
ABCDEF 2
ABC123 2
CCCCCC 1
DEF123 1

Вот Ruby для этого:

ruby -lane 'BEGIN{ cnt=Hash.new{|h,k| h[k]=[]} }
cnt[$F[0]]<<$F[1]
END{
    cnt.select{|k,v| v.length>1 }.
        each{|k,v| puts "#{k} #{v.uniq.length}"} 
}
' file.txt

Распечатки:

ABC123 2
ABCDEF 2
DEF123 1

Неясно, должен ли CCCCCC 1 присутствовать в выводе. Если да, то фильтровать не нужно:

ruby -lane 'BEGIN{ cnt=Hash.new{|h,k| h[k]=[]} }
cnt[$F[0]]<<$F[1]
END{ cnt.each{|k,v| puts "#{k} #{v.uniq.length}"} }
' file.txt

Распечатки:

ABC123 2
ABCDEF 2
CCCCCC 1
DEF123 1

Вы также можете сделать этот канал POSIX:

uniq file.txt | awk '{cnt[$1]++} END{for (e in cnt) print e, cnt[e]}'

Распечатки:

CCCCCC 1
ABCDEF 2
DEF123 1
ABC123 2

Другие вопросы по теме