Объединить несколько строк в одном файле на основе столбца 1

Объединить несколько строк в одном файле на основе столбца 1

Я все еще учусь программированию и перепробовал много вещей, но так и не могу получить правильный формат. У меня естьс разделителем табуляциифайл с 17 столбцами и множеством (около 50.000) строк. Файл уже отсортирован по первому столбцу. Я хочу объединить строки, которые имеют одинаковый первый столбец (A), но все остальные 16 столбцов отличаются, и я хочу сохранить всю информацию в одной строке, желательно в том же столбце сточка с запятой ;как разделитель между ними. Я хочу сохранить табуляцию как разделитель в выходном файле. Большое спасибо за ответы, и если бы вы также могли объяснить ответ, где я ошибся, это было бы еще лучше :).

Я уже пробовал:

awk -F'\t' 'NF>1{a[$1] = a[$1]";"$2}END{for(i in a){print i""a[i]}}' filename.txt

perl -F',' -anle 'next if /^$/;$h{$F[0]} = $h{$F[0]}.", ".$F[1];
END{print $_,$h{$_},"\n" for sort keys %h}' filename.txt

ФОРМАТ ФАЙЛА (остальные 15 столбцов имеют тот же формат, что и столбец B)

A     B     C    
123   fvv   ggg
123   kjf   ggg
123   ccd   att
567   abc   gst
567   abc   hgt
879   ttt   tyt

Нужный мне вывод (мне нужны все 17 столбцов, а для столбцов 2-16 мне нужен тот же вывод, что и в столбцах B и C). Все случаи B должны быть под B, все случаи C должны быть под C, все случаи D должны быть под D и т. д. Таким образом, вывод содержит 17 столбцов, как и ввод, и вместо 50 000 строк теперь должно быть около 20 000, поскольку для столбца 1 (для этого конкретного файла) есть много повторений:

A     B                C
123   fvv;kjf;ccd      ggg;ggg;att
567   abc;abc          gst;hgt
879   ttt              lll

решение1

awk '{
      if(NR!=1){a[$1]=$2";"a[$1]}
      else print $0}
    END{
      n = asorti(a, b);
      for (n in b) {
      print b[n],a[b[n]]
      }
    }'

решение2

Решение на Perl:

$ perl -F"\t" -anle 'if($.==1){print; next} push @{$k{$F[0]}},@F[1..$#F]; 
  END{print "$_\t" . join(";",@{$k{$_}}) for sort keys(%k)}' file 
A   B   
123 fvv;kjf;ccd
567 abc;abc
879 ttt

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


Что касается того, где вы ошиблись, мы не сможем вам сказать, пока вы не объясните, что на самом деле произошло, но, навскидку, ваша попытка с помощью Perl потерпит неудачу, потому что:

  • Вы используете -F,, который устанавливает разделитель полей на запятую, когда ваши входные данные содержат символы табуляции.
  • Вы используете -lи print "foo\n". Он -lуже добавляет новую строку к каждому вызову print, поэтому у вас будет несколько пустых строк.
  • Вы используете $h{$F[0]}.", ".$F[1];для добавления, поэтому при первом запуске, когда $h{$F[0]}он не определен, вы добавите дополнительный символ ,в начало сохраненного значения.
  • Вы смотрите только на второе поле, игнорируя все остальные.

Аналогично, вы awkпотерпите неудачу, потому что:

  • Вы печатаете foo""bar, что объединит вывод без пробелов между каждым полем. Вы хотите print foo,barи вы также хотите OFS="\t"для вывода с разделителями табуляции.
  • Вы смотрите только на второе поле, игнорируя все остальные.

решение3

извините за эту однострочную фразу, но вот она —

awk 'BEGIN{FS="\t"} {for(i=2; i<=NF; i++) { if (!a[$1]) a[$1]=$1FS$i ;else a[$1]=a[$1]";"$i};if ($1 != old) b[j++] = a[old];old=$1 } END{for (i=0; i<j; i++) print b[i] }' 1

123 fvv ;kjf;ccd
567 abc;abc
879 ttt

решение4

awk '
    function p(n,A){
        s = n
        for(i=2;i<=NF;i++){
            s = s "\t" A[i]
            A[i] = $i
        }
        if(n)
            print s
    }
    NR==1{
        print
        next
    }
    $1==n{
        for(i=2;i<=NR;i++)
            A[i] = A[i] ";" $i
        next
    }
    {
        p(n,A)
        n = $1
    }
    END{
        p(n,A)
    }
    ' file

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