перестановка таблицы с помощью bash / awk - PullRequest
2 голосов
/ 13 февраля 2020

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

column1  column2 column3 column4....
abc      def     ghi     jki
lmn      opq     rst     uvw
xyz              abc     def
ghi              jkl

, что мне нужно сделать, это эффективно объединить столбцы, где column1 - это имя исходного столбца, а column2 - значения в строках, что-то вроде следующего:

column1  abc
column1  lmn
column1  xyz
column1  ghi
column2  def
column2  opq
column3  ghi
column3  rst
column3  abc
column3  jkl
column4  ...

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

awk -F"\t" '{ for (i=1;i<=NF;i++) { l=length($i) ; if ( l > linesize[i] ) linesize[i]=l ; }} END \
{ for (l=1;l <= NF; l++) for (j=2;j<=NR;j++) printf "%d/t%d\n",NR==1($l),!(NR==$j&&NF==$l)="" ;; }'\
 file_in.txt > file_out.txt

Я провел обширный поиск и не смог найти ничего полезного (я точно знаю, что некоторые настройки NR и NF неверны, но я не уверен, как это исправить), поэтому любая помощь будет высоко ценится. Спасибо

Ответы [ 2 ]

1 голос
/ 13 февраля 2020

Этого достаточно в чистом awk, чтобы проще было все бросить в отдельный файл скрипта, а не в виде однострочного:

#!/usr/bin/awk -f
BEGIN { FS = "\t" }
NR == 1 {
    for (c = 1; c <= NF; c++)
        headers[c] = $c
    maxcol = NF
    next
}
{
    for (c = 1; c <= NF; c++)
        if ($c != "")
            results[c] = results[c] headers[c] "\t" $c "\n"
}
END {
    for (c = 1; c <= maxcol; c++)
        printf "%s", results[c]
}

Использование:

$ ./rowify.awk < input.tsv
column1 abc
column1 lmn
column1 xyz
column1 ghi
column2 def
column2 opq
column3 ghi
column3 rst
column3 abc
column3 jkl
column4 jki
column4 uvw
column4 def

Это просто добавляет имя каждого столбца + значение столбца в качестве новой строки в строке к соответствующей записи в массиве, а затем печатает их все в конце.

Альтернативный один вкладыш с awk в сочетании с GNU datama sh:

$ datamash --no-strict transpose < input.tsv | awk '{ for (c = 2; c <= NF; c++) if ($c != "N/A") print $1 "\t" $c }'
column1 abc
column1 lmn
column1 xyz
column1 ghi
column2 def
column2 opq
column3 ghi
column3 rst
column3 abc
column3 jkl
column4 jki
column4 uvw
column4 def

Команда transpose переключает строки и столбцы, поэтому строки выглядят как column1 abc lnm ..., а затем awk просто разбивает это на отдельные строки.

0 голосов
/ 13 февраля 2020

Я сделал это. Тестовый файл Gen

for i in {1..10}; { echo "column1 1_$RANDOM" >> file; }
for i in {1..5} ; { echo "column2 2_$RANDOM" >> file; }
for i in {1..7} ; { echo "column3 3_$RANDOM" >> file; }
for i in {1..4} ; { echo "column4 4_$RANDOM" >> file; }

Для этого тестового файла

#!/bin/bash

declare -A col
mapfile raw < file
for item in "${raw[@]}"; {
    sub=($item)
    col[$sub]+="${sub[@]:1} "
}

N=0
for name in ${!col[@]}; {
    sub=(${col[$name]})
    sub_lenght=${#sub[@]}
    (( $sub_lenght > $N )) && { N=$sub_lenght; longest=$name; }
}

I=0
echo "${!col[@]}"
for i in ${col[$longest]}; {
    line=
    for name in ${!col[@]}; { 
        sub=(${col[$name]})
        line+="${sub[$I]}\t"
    }
    ((I++))
    printf "$line\n"
}

Выход

$ ./test 
column2 column3 column1 column4
2_17726 3_28940 1_1605  4_16951 
2_4722  3_22401 1_22079 4_15812 
2_8031  3_4652  1_20680 4_11615 
2_9130  3_12552 1_7640  4_22234 
2_28572 3_9818  1_26144     
        3_13568 1_28112     
        1_30364     
        1_2937      
        1_16697 
...