web-dev-qa-db-ja.com

行と列の入れ替え

次のような行を含むファイルがあります。

title1:A1
title2:A2
title3:A3
title4:A4
title5:A5

title1:B1
title2:B2
title3:B3
title4:B4
title5:B5

title1:C1
title2:C2
title3:C3
title4:C4
title5:C5

title1:D1
title2:D2
title3:D3
title4:D4
title5:D5

どうすればこれを達成できますか?

title1    title2     title3    title4
A1         A2         A3         A4
B1         B2         B3         B4
C1         C2         C3         C4
D1         D2         D3         D4
18
Dens

GNU datamash を見てください。これはdatamash transposeのように使用できます。将来のバージョンでは、クロス集計(ピボットテーブル)もサポートされる予定です。

15
Pádraig Brady

コマンドラインから列を含む行を転置するカスタムソリューションをローリングする以外に、これを実行できる唯一のツールは皮肉なことに transpose と呼ばれるツールです。

Installation

残念ながらそれはどのリポジトリにもないので、ダウンロードしてコンパイルする必要があります。依存している追加のライブラリがないため、これは非常に簡単です。それはそのように達成することができます:

$ gcc transpose.c -o transpose

使用法

簡単なテキストファイルを簡単に処理できます。例えば:

$ cat simple.txt 
X column1 column2 column3
row1 0 1 2
row2 3 4 5
row3 6 7 8
row4 9 10 11

次のコマンドを使用して転置できます。

$ transpose -t --fsep " " simple.txt 
X row1 row2 row3 row4
column1 0 3 6 9
column2 1 4 7 10
column3 2 5 8 11

このコマンドは、転置するtransposeです(-t)と使用するフィールド区切り文字はスペース(--fsep " ")。

あなたの例

サンプルデータは少し複雑な形式なので、2つのフェーズで処理する必要があります。まず、transposeが処理できる形式に変換する必要があります。

このコマンドを実行すると、データはより水平に適した形式になります。

$ sed 's/:/ /; /^$/d' sample.txt \
    | sort | paste - - - - -
title1 A1   title1 B1   title1 C1   title1 D1   title2 A2
title2 B2   title2 C2   title2 D2   title3 A3   title3 B3
title3 C3   title3 D3   title4 A4   title4 B4   title4 C4
title4 D4   title5 A5   title5 B5   title5 C5   title5 D5

ここで、title1、title2などの2番目のオカレンスを削除する必要があります。

$ sed 's/:/ /; /^$/d' sample.txt \
    | sort | paste - - - - - | sed 's/\ttitle[0-9] / /g'
title1 A1 B1 C1 D1 A2
title2 B2 C2 D2 A3 B3
title3 C3 D3 A4 B4 C4
title4 D4 A5 B5 C5 D5

現在はtransposeが処理できる形式になっています。次のコマンドは、転置全体を実行します。

$ sed 's/:/ /; /^$/d' sample.txt \
    | sort | paste - - - - - | sed 's/\ttitle[0-9] / /g' \
    | transpose -t --fsep " "
title1 title2 title3 title4
A1 B2 C3 D4
B1 C2 D3 A5
C1 D2 A4 B5
D1 A3 B4 C5
A2 B3 C4 D5
9
slm

awkを使用してデータを処理し、次にpasteおよびcolumnを使用してデータをフォーマットできます。

ここでは、title1は投稿の単なる例であり、そのデータにはヘッダー+データ間の区切り文字を除いて:が含まれていないと想定しています。

nは、印刷する列の数を示します(pasteのダッシュと一致する必要があります)。

awk -F":" -v n=4 \
'BEGIN { x=1; c=0;} 
 ++c <= n && x == 1 {print $1; buf = buf $2 "\n";
     if(c == n) {x = 2; printf buf} next;}
 !/./{c=0;next}
 c <=n {printf "%s\n", $2}' datafile | \
 paste - - - - | \
 column -t -s "$(printf "\t")"

より柔軟で保守しやすくしたい場合は、スクリプトとして作成できます。以下は、awkにbashラッパーを使用し、columnにパイプ接続した例です。このようにして、たとえば、すべての行でヘッダーが正しいことを確認するなど.

通常は次のように使用されます:

$ ./trans -f data -c 4
title one  title two  title three  title four
A1         A2         A3           A4
B1         B2         B3           B4
C1         C2         C3           C4
D1         D2         D3           D4

ヘッダーが常に短い場合は、データをヘッダーの幅として保存し、printf%-*sと一緒に保存して、columnをすべてスキップすることもできます。

#!/bin/bash

trans()
{
    awk -F":" -v ncol="$1" '
    BEGIN {
        level = 1 # Run-level.
        col   = 1 # Current column.
        short = 0 # If requested to many columns.
    }
    # Save headers and data for row one.
    level == 1 {
        head[col] = $1
        data[col] = $2
        if (++col > ncol) { # We have number of requested columns.
            level = 2
        } else if ($0 == "") { # If request for more columns then available.
            level = 2
            ncol  = col - 2
            short = 1
        } else {
            next
        }
    }
    # Print headers and row one.
    level == 2 {
        for (i = 1; i <= ncol; ++i)
            printf("%s\t", head[i])
        print ""
        for (i = 1; i <= ncol; ++i)
            printf("%s\t", data[i])
        level = 3
        col = ncol + 1
        if (!short)
            next
    }
    # Empty line, new row.
    ! /./ { print ""; col = 1; next }
    # Next cell.
    col > ncol {next}
    {
        printf "%s%s", $2, (col <= ncol) ? "\t" : ""
        ++col
    }
    END {print ""}
    ' "$2"
}

declare -i ncol=4  # Columns defaults to four.
file=""            # Data file (or pipe).

while [[ -n "$1" ]]; do
    case "$1" in
    "-c") ncol="$2"; shift;;
    "-f") file="$2"; shift;;
    *) printf "Usage: %s [-c <columns>] [-f <file> | pipe]\n" \
        "$(basename $0)" >&2;
        exit;;
    esac
    shift
done

trans "$ncol" "$file" | column -t -s "$(printf "\t")"
8
Runium

GNU datamashユーティリティ

apt install datamash  

datamash transpose < yourfile

このサイトから取得 https://www.gnu.org/software/datamash/ および http://www.thelinuxrain.com/articles/transposing-rows-and-columns -3-メソッド

4
nelaaro

これを定式化するより簡潔な方法がおそらくあるでしょうが、これは一般的な効果を達成するようです:

[jadavis84@localhost ~]$ sed 's/^title[2-9]://g' file.txt | tr '\n' '\t' | sed 's/title1:/\n/g' ; echo

A1  A2  A3  A4  A5      
B1  B2  B3  B4  B5      
C1  C2  C3  C4  C5      
D1  D2  D3  D4  D5  
[jadavis84@localhost ~]$ 

複数のsed呼び出しは正しく感じられない(そして、sedが改行変換も実行できると確信しています)ので、それはおそらく最も簡単な方法ではありません。また、これによりヘッダーが削除されますが、行/フィールドが適切にフォーマットされたら、ヘッダーを手動で生成できます。

より良い答えは、おそらくsedまたはawkを使用してこれを行うことでその効果を抽出し、一度に1つの処理のみを実行することです。でも疲れたのでこれでまとめました。

3
Bratchley

pasteがおそらく最善の策です。次のように、cutgrepawkを使用して関連ビットを抽出できます。

(awk 'NR==1' RS= infile | cut -d: -f1; cut -sd: -f2 infile)

5番目の列を削除する必要がある場合は、次のようにawk 'NR%5'を追加します。

(awk 'NR==1' RS= infile | cut -d: -f1; cut -sd: -f2 infile) | awk 'NR%5'

pasteを使用して列化します。

(awk 'NR==1' RS= infile | cut -d: -f1; cut -sd: -f2 infile) | awk 'NR%5' | paste - - - -

出力:

title1  title2  title3  title4
A1  A2  A3  A4
B1  B2  B3  B4
C1  C2  C3  C4
D1  D2  D3  D4
1
Thor

転置部分だけについて、私は最近同様の問題があり、使用しました:

awk -v fmt='\t%4s'  '{ for(i=1;i<=NF;i++){ a[i]=a[i] sprintf(fmt, $i); } } END { for (i in a) print a[i]; }'

必要に応じてfmtを調整します。入力行ごとに、各フィールドを配列要素に連結します。 awk文字列の連結は暗黙的であることに注意してください。これは、演算子なしで2つのことを記述したときに発生します。

サンプルI/O:

i       mark    accep   igna    utaal   bta
-22     -10     -10     -20     -10     -10
-21     -10     -10     -20     -10     -10
-20     -10     -10     -20     -10     -10
-19     -10     0       -10     -10     -10
-18     0       0       -10     0       0
-12     0       0       -10     0       0
-11     0       0       -10     0       0
-10     0       0       -10     0       0

出力:

       i     -22     -21     -20     -19     -18     -12     -11     -10
    mark     -10     -10     -10     -10       0       0       0       0
    accep    -10     -10     -10       0       0       0       0       0
    igna     -20     -20     -20     -10     -10     -10     -10     -10
    utaal    -10     -10     -10     -10       0       0       0       0
     bta     -10     -10     -10     -10       0       0       0       0
0
Peter Cordes

3つのawkの回答、2つのdatamash transposeの回答、および1つのtransposeの回答のバランスをとるには、次の Miller を実行します。

mlr --ixtab --ips:--opprint cat << END 
 title1:A1 
 title2:A2 
 title3:A3 
 title4:A4 
 title5:A5 
 
 title1:B1 
 title2:B2 
 title3:B3 
 title4 :B4 
 title5:B5 
 
 title1:C1 
 title2:C2 
 title3:C3 
 title4:C4 
 title5:C5 
 
 title1:D1 
 title2:D2 
 title3:D3 
 title4:D4 
 title5:D5 
 
 END
 title1 title2 title3 title4 title5 
 A1 A2 A3 A4 A5 
 B1 B2 B3 B4 B5 
 C1 C2 C3 C4 C5 
 D1 D2 D3 D4 D5 
%

XTAB形式(-ixtab)からPPRINT形式(-opprint)への変換を行っています。

実際に行うtitle5を削除したい場合(つまりnotは質問から明らかです)、単純にgrep -v '^title5:'を介してMillerへの入力を前処理します。

0
JdeBP