Uniq в awk; удаление повторяющихся значений в столбце с помощью awk - PullRequest
4 голосов
/ 05 июня 2010

У меня большой файл данных в следующем формате:

ENST00000371026 WDR78,WDR78,WDR78,  WD repeat domain 78 isoform 1,WD repeat domain 78 isoform 1,WD repeat domain 78 isoform 2,
ENST00000371023 WDR32   WD repeat domain 32 isoform 2
ENST00000400908 RERE,KIAA0458,  atrophin-1 like protein isoform a,Homo sapiens mRNA for KIAA0458 protein, partial cds.,

Столбцы разделены табуляцией. Несколько значений в столбцах разделяются запятыми. Я хотел бы удалить повторяющиеся значения во втором столбце, чтобы получить что-то вроде этого:

ENST00000371026 WDR78   WD repeat domain 78 isoform 1,WD repeat domain 78 isoform 1,WD repeat domain 78 isoform 2,
ENST00000371023 WDR32   WD repeat domain 32 isoform 2
ENST00000400908 RERE,KIAA0458   atrophin-1 like protein isoform a,Homo sapiens mRNA for KIAA0458 protein, partial cds.,

Я попробовал следующий код ниже, но он не удаляет повторяющиеся значения.

awk ' 
BEGIN { FS="\t" } ;
{
  split($2, valueArray,",");
  j=0;
  for (i in valueArray) 
  { 
    if (!( valueArray[i] in duplicateArray))
    {
      duplicateArray[j] = valueArray[i];
      j++;
    }
  };
  printf $1 "\t";
  for (j in duplicateArray) 
  {
    if (duplicateArray[j]) {
      printf duplicateArray[j] ",";
    }
  }
  printf "\t";
  print $3

}' knownGeneFromUCSC.txt

Как правильно удалить дубликаты в столбце 2?

Ответы [ 4 ]

6 голосов
/ 05 июня 2010

Ваш скрипт действует только на вторую запись (строку) в файле из-за NR==2. Я взял это, но это может быть то, что вы намерены. Если это так, вы должны положить его обратно.

Оператор in проверяет наличие индекса , а не значения, поэтому я сделал duplicateArray ассоциативный массив *, который использует значения из valueArray как его показатели. Это избавляет от необходимости перебирать оба массива в цикле внутри цикла.

Оператор split видит "WDR78, WDR78, WDR78" как четыре поля, а не как три, поэтому я добавил if, чтобы он не печатал нулевое значение, что приведет к печати ", WDR78", если if там не было.

* В действительности все массивы в AWK являются ассоциативными.

awk '
BEGIN { FS="\t" } ;
{
  split($2, valueArray,",");
  j=0;
  for (i in valueArray)
  { 
    if (!(valueArray[i] in duplicateArray))
    { 
      duplicateArray[valueArray[i]] = 1
    }
  };
  printf $1 "\t";
  for (j in duplicateArray)
  {
    if (j)    # prevents printing an extra comma
    {
      printf j ",";
    }
  }
  printf "\t";
  print $3
  delete duplicateArray    # for non-gawk, use split("", duplicateArray)
}'
3 голосов
/ 05 июня 2010

Perl:

perl -F'\t' -lane'
  $F[1] = join ",", grep !$_{$_}++, split ",", $F[1]; 
  print join "\t", @F; %_ = ();
  ' infile  

AWK:

awk -F'\t' '{
  n = split($2, t, ","); _2 = x
  split(x, _) # use delete _ if supported
  for (i = 0; ++i <= n;)
    _[t[i]]++ || _2 = _2 ? _2 "," t[i] : t[i]
  $2 = _2 
  }-3' OFS='\t' infile

Строка 4 в скрипте awk используется для сохранения исходного порядка значений во втором поле после фильтрации уникальных значений.

3 голосов
/ 05 июня 2010

Извините, я знаю, что вы спрашивали о awk ... но Perl делает это намного проще:

$ perl -n -e ' @t = split(/\t/);
  %t2 = map { $_ => 1 } split(/,/,$t[1]);
  $t[1] = join(",",keys %t2);
  print join("\t",@t); ' knownGeneFromUCSC.txt
2 голосов
/ 05 июня 2010

Pure Bash 4.0 (один ассоциативный массив):

declare -a part                            # parts of a line
declare -a part2                           # parts 2. column
declare -A check                           # used to remember items in part2

while read  line ; do
  part=( $line )                           # split line using whitespaces
  IFS=','                                  # separator is comma
  part2=( ${part[1]} )                     # split 2. column using comma
  if [ ${#part2[@]} -gt 1 ] ; then         # more than 1 field in 2. column?
    check=()                               # empty check array
    new2=''                                # empty new 2. column
    for item in ${part2[@]} ; do 
      (( check[$item]++ ))                 # remember items in 2. column
      if [ ${check[$item]} -eq 1 ] ; then  # not yet seen?
        new2=$new2,$item                   # add to new 2. column
      fi 
    done
    part[1]=${new2#,}                      # remove leading comma
  fi 
  IFS=$'\t'                                # separator for the output
  echo "${part[*]}"                        # rebuild line
done < "$infile"
...