次のような行を含むファイルがあります。
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
GNU datamash を見てください。これはdatamash transpose
のように使用できます。将来のバージョンでは、クロス集計(ピボットテーブル)もサポートされる予定です。
コマンドラインから列を含む行を転置するカスタムソリューションをローリングする以外に、これを実行できる唯一のツールは皮肉なことに 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
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")"
GNU datamashユーティリティ
apt install datamash
datamash transpose < yourfile
このサイトから取得 https://www.gnu.org/software/datamash/ および http://www.thelinuxrain.com/articles/transposing-rows-and-columns -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が改行変換も実行できると確信しています)ので、それはおそらく最も簡単な方法ではありません。また、これによりヘッダーが削除されますが、行/フィールドが適切にフォーマットされたら、ヘッダーを手動で生成できます。
より良い答えは、おそらくsed
またはawk
を使用してこれを行うことでその効果を抽出し、一度に1つの処理のみを実行することです。でも疲れたのでこれでまとめました。
paste
がおそらく最善の策です。次のように、cut
、grep
、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
転置部分だけについて、私は最近同様の問題があり、使用しました:
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
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への入力を前処理します。