行と列の転置


18

以下のような行を持つファイルがあります。

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


あなたにも、あなたが望む結果を得るために複数のパスをPerlやPythonや実際のプログラミング言語または使用TR /カットしてカスタムソリューションを展開かもしれない、awkには使用しないでくださいくださいください
ルドルフ・オラー

回答:



9

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

設置

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

$ 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

8

あなたは使用することができawk、データを処理するpastecolumn、それをフォーマットします。

ここで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")"

柔軟性と保守性を高めたい場合は、スクリプトとして作成できます。にbashラッパーを使用しawk、にパイプする例を次に示し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")"

1
いい答え!@JoelDavisと私はこれについてハッキングしてきましたが、あなたの答えは素晴らしいです!
slm

7

以下に、ファイルを目的の形式にする簡単な方法を示します。

$ grep -Ev "^$|title5" sample.txt | sed 's/title[0-9]://g' | paste - - - -
A1  A2  A3  A4
B1  B2  B3  B4
C1  C2  C3  C4
D1  D2  D3  D4

列ヘッダーが必要な場合:

$ grep -Ev "^$|title5" sample.txt | sed 's/:.*//' | sort -u | tr '\n' '\t'; \
    echo ""; \
    grep -Ev "^$|title5" a | sed 's/title[0-9]://g' | paste - - - -
title1  title2  title3  title4  
A1      A2      A3      A4
B1      B2      B3      B4
C1      C2      C3      C4
D1      D2      D3      D4

2番目のコマンドの仕組み

バナーを印刷する
grep -Ev "^$|title5" sample.txt | sed 's/:.*//' | sort -u | tr '\n' '\t';
バナーの後にリターンを入れる
echo
データの行を印刷する
grep -Ev "^$|title5" a | sed 's/title[0-9]://g' | paste - - - -

貼り付けコマンドは、単に私の仕事を完了させました。答えてくれてありがとう...
SKヴェンカト


3

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

[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も改行変換を行うことができると確信しています)ので、それはおそらく最も簡単な方法ではありません。また、これによりヘッダーが削除されますが、行/フィールドが適切にフォーマットされたら、ヘッダーを手動で生成できます。

より良い答えはおそらく、その効果をただ使用するsedawk、または一度に1つのことだけを行うように行うことまで減らすでしょう。しかし、私は疲れているので、これは私がまとめることができたものです。


ジョエル-私は同じ間違いを犯しましたが、気がついたのですが、彼は出力にtitle5カラムを必要としません。
slm

ああ、最後にawkをうまく実行することで修正できます。しかし、Sukminderが完全なソリューションを投稿したようです。
ブラッチリー

1

pasteおそらくあなたの最善策です。あなたはとの関連ビットを抽出することができcutgrepそしてawkこのように:

(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

0

転置部分についてのみ、最近同様の問題が発生し、使用しました:

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.