转置行和列

Den*_*ens 19 text-processing columns

我有一个像下面这样的行的文件。

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
Run Code Online (Sandbox Code Playgroud)

我怎样才能做到这一点?

title1    title2     title3    title4
A1         A2         A3         A4
B1         B2         B3         B4
C1         C2         C3         C4
D1         D2         D3         D4
Run Code Online (Sandbox Code Playgroud)

Pád*_*ady 16

看看GNU datamash可以像使用datamash transpose。未来版本还将支持交叉制表(数据透视表)


slm*_*slm 9

除了滚动自定义解决方案以从命令行转置行与列之外,我见过的唯一可以执行此操作的工具是具有讽刺意味的工具transpose

安装

不幸的是,它不在任何 repo 中,因此您需要下载并编译它。这非常简单,因为它没有依赖的其他库。它可以像这样完成:

$ gcc transpose.c -o transpose
Run Code Online (Sandbox Code Playgroud)

用法

它可以轻松处理简单的文本文件。例如:

$ cat simple.txt 
X column1 column2 column3
row1 0 1 2
row2 3 4 5
row3 6 7 8
row4 9 10 11
Run Code Online (Sandbox Code Playgroud)

可以使用以下命令转置:

$ 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
Run Code Online (Sandbox Code Playgroud)

此命令transpose用于转置 ( -t),要使用的字段分隔符是空格 ( --fsep " ")。

你的榜样

由于您的样本数据格式稍复杂,因此需要分两个阶段进行处理。首先我们需要把它翻译成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
Run Code Online (Sandbox Code Playgroud)

现在我们只需要删除 title1、title2 等的次要出现:

$ 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
Run Code Online (Sandbox Code Playgroud)

它现在是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
Run Code Online (Sandbox Code Playgroud)


Run*_*ium 8

您可以使用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")"
Run Code Online (Sandbox Code Playgroud)

如果您想让它更加灵活和易于维护,您可以将其编写为脚本。这是一个使用 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
Run Code Online (Sandbox Code Playgroud)

如果标题总是比数据短,您还可以保存标题宽度,然后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")"
Run Code Online (Sandbox Code Playgroud)


slm*_*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
Run Code Online (Sandbox Code Playgroud)

如果你想要列标题:

$ 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
Run Code Online (Sandbox Code Playgroud)

第二个命令的工作原理

印刷横幅
grep -Ev "^$|title5" sample.txt | sed 's/:.*//' | sort -u | tr '\n' '\t';
Run Code Online (Sandbox Code Playgroud) 在横幅后放置一个返回
echo
Run Code Online (Sandbox Code Playgroud) 打印数据行
grep -Ev "^$|title5" a | sed 's/title[0-9]://g' | paste - - - -
Run Code Online (Sandbox Code Playgroud)


Bra*_*ley 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 ~]$ 
Run Code Online (Sandbox Code Playgroud)

多次sed调用感觉不太对(而且我很确定 sed 也可以进行新行转换),因此它可能不是最直接的方法。此外,这会删除可能的标题,但是一旦您正确设置了行/字段的格式,您就可以手动生成这些标题。

更好的答案可能会将这种效果简化为仅使用sedawk执行此操作,以便一次只进行一件事。但我累了,所以这就是我能够整理的。