转置行和列


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


请请不要使用awk,您不妨使用perl或python或真正的编程语言来推出自定义解决方案,或者使用tr / cut进行多次传递来获得所需的内容
Rudolf Olah

Answers:



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 " ")。

你的例子

由于您的样本数据格式稍微复杂一些,因此需要分两个阶段进行处理。首先,我们需要将其转换为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等的次要出现:

$ 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

第二个命令的工作方式

打印横幅
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 Venkat


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执行此操作,这样您一次只能执行一件事。但是我很累,所以这是我能够做到的。


乔尔-我犯了同样的错误,只是注意到了,他不希望输出中的title5列。
slm

嗯,最后通过awk运行应该可以解决该问题。但是看来Sukminder的发布了完整的解决方案。
布莱奇利2013年

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字符串连接是隐式的:当您编写两件事而没有任何运算符时,它将发生。

样本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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.