Удалить все строки, если столбец2 соответствует двум или более значениям столбца1

Пока ничто не заменило ShiftIT.

Я наткнулся на Shellshape , что немного приблизило меня к тому, чего я хотел достичь.

BlueTile -> Не удалось запустить его в Debian. Сетка -Плагин -> Невозможно скомпилировать.

Как заметил другой автор, возможно, лучше рассмотреть лучший оконный менеджер, -более ориентированный на клавиатуру.

1
10.06.2021, 17:59
4 ответа
awk 'BEGIN{ FS=OFS="\t" }
{ data[$2]= (data[$2]==""?"":(k[$2]==$1? data[$2] ORS: "@") ) $0; k[$2]=$1 }

END{ for(x in data) if(data[x] !~/^@/) print data[x] }' infile

примечание :Я использовал символ @для обозначения тех записей, которые не следует печатать при печати, поэтому этот символ не должен быть представлен во входном файле, иначе вам нужно будет изменить его на другой символ. символ (или вместо )выберите набор символов в виде строки.

  • data[$2]= (data[$2]==""?"":(XXX) ) $0, обновляет значение массива dataрезультатом части (XXX), если он не пуст, и добавляет к нему текущую строку. столбец $2используется как ключи массива.

  • часть (XXX), которая является (k[$2]==$1? data[$2] ORS: "@"), устанавливает в -символ знака, если значение того же ключа (мы использовали массив tв качестве помощника для хранения пар последних значений ключей )отличается, иначе добавьте его содержимое + новую строку (ORS )для этого ключа.

  • в конце все те строки, которые имеют один и тот же второй столбец, но разные > 1 кратный первый столбец, будут удалены, так как мы пометили те, которые имеют определенный символ @в коде.


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

awk 'BEGIN{ FS=OFS="\t" }
{ data[$2]= (data[$2]==""?"":(k[$2]==$1? data[$2] ORS: "***") ) $0; k[$2]=$1 }

END{ for(x in data) print "<" data[x] ">" }' infile

Я только что пометил удаленные записи знаком at -в исходной команде, показав здесь те, которые начинаются со значка ***.

2
28.07.2021, 11:25

Использование любого awk, поддерживающегоlength(array):

$ cat tst.awk
BEGIN { FS=OFS="\t" }
$2 != prev {
    if ( NR > 1 ) {
        prt()
    }
    prev = $2
}
{ cnt[$1]++ }
END { prt() }

function prt(   val,i) {
    if ( length(cnt) == 1 ) {
        for (val in cnt) {
            for (i=1; i<=cnt[val]; i++) {
                print val, prev
            }
        }
    }
    delete cnt
}

$ sort -t$'\t' -k2,2 -k1,1 file | awk -f tst.awk
GL89    AADAC
GL60    AC100
GL60    AC100
GL20    AC200
GL30    AC300
GL30    AC400
GL89    AFDAC
GL89    AFGAC
1
28.07.2021, 11:25

Подходящей структурой данных для этой задачи является setиdictionary

И в Python встроены -.

python3 -c 'import sys
ifile = sys.argv[1]
fs,ors = "\t","\n"

d = {}; L = {}
with open(ifile) as fh:
  for l in fh:
    c1,c2 = l.rstrip().split(fs)
    if c2 in d:
      d[c2].add(c1)
      L[c2].append(l.rstrip())
    else:
      d[c2] = { c1 }
      L[c2] = [ l.rstrip() ]

print(*[l
  for k,v in d.items()
  if len(v) == 1
  for l in L[k]
  ], sep=ors)
' file

Выход:

GL89    AADAC
GL89    AFGAC
GL89    AFDAC
GL60    AC100
GL60    AC100
GL20    AC200
GL30    AC300
GL30    AC400
1
28.07.2021, 11:25

Просто так 1 , используя Миллер

Основные шаги

  1. используйте nest --implode, чтобы собрать все значения $1, соответствующие каждому $2, в список с разделителями

  2. отфильтровать список для подсчета уникальных значений$1

  3. используйте nest --explode, чтобы развернуть отфильтрованные $1значения обратно в виде отдельных записей

Для шага (2 )мы можем де -дублировать элементы списка с разделителями, например GL50;GL50;GL79;GL99;GL99, превратив их в ключи в хэш-карте. К сожалению, встроенная строка DSL с -по -функции хэш-карты splitkvи splitkvxработают только с парами ключей -значение -, кажется, нет способа (путем передачи разделитель пустых пар, например ), чтобы они превратили строку ключей с разделителями в хэш-карту (с произвольными или пустыми значениями ). Итак, мы должны свернуть свою собственную, разбив строку на индексированную карту, а затем превратив значения в ключи новой карты.

Обратите внимание, что шаги (2 )и (3 )необходимы только потому, что мы не хотим отфильтровывать несколько значений одного и того же $1value -иначе мы могли бы просто проверить длину индексированной карты сжатой строки (и не было бы необходимости разбивать результат ).

Итак,

$ mlr --nidx --fs tab nest --ivar ';' -f 1 then filter '
  func splitkx(s,t):map {
    var m = {};
    for(k,v in splitnvx(s,t)){m[v] = 1};
    return m
  }
  length(splitkx($1,";")) == 1' then nest --evar ';' -f 1 file
GL89    AADAC
GL89    AFGAC
GL89    AFDAC
GL60    AC100
GL60    AC100
GL20    AC200
GL30    AC300
GL30    AC400

Примечания:

  1. на самом деле больше для справки, так как я сомневаюсь, что когда-нибудь снова смогу это понять
1
28.07.2021, 11:25

Теги

Похожие вопросы