构建表 - 根据文件名添加列

构建表 - 根据文件名添加列

我有大量具有以下命名风格的文件:

WBM_MIROC_rcp8p5_mississippi.txt
WBM_GFDL_rcp8p5_nosoc_mississippi.txt
DBH_HADGEM_rcp4p5_co2_mississippi.txt
HMH_IPSL_rcp4p5_mississippi.txt

这些文件表示具有以下格式的表(其中一些具有制表符分隔符,其他具有一个空格分隔符):

YEAR MONTH DAY RES
1971 1 1 1988
1971 1 2 3829
...

我想将rcp8p5名称中包含的所有文件分组到一张大表中;并对rcp4p5名称中包含的文件执行相同的操作。但是,我只想粘贴每个文件的 4 列,以避免前三列始终相同的冗余。我目前正在使用以下脚本:

ls |
awk -F_ '{ i=$1; m=$2; s=$3; u=$4;
          if(f[s]=="")add = $0;
          else add = sprintf("<(cut -f4 %s)",$0);
          f[s] = f[s] " " add }
          END{ for(insc in f)
                 printf "paste%s > out_%s.txt\n",f[insc],insc
          }' |bash

目前还不清楚为什么,但输出不符合预期。我有以下输出:

YEAR MONTH DAY RES YEAR MONTH DAY RES YEAR MONTH DAY RES
1971 1     1   187 1971 1     1   143 1971 1     1   234
1971 1     2   321 1971 1     2   398 1971 1     1   754
...

相反,我希望得到以下输出:

YEAR MONTH DAY RES RES RES
1971 1     1   187 143 234
1971 1     2   321 398 754

如果有人能给我提示,那就太好了!

答案1

最可能的答案是您的数据文件列不是用制表符分隔的,而是用空格分隔的。您可以通过运行其中一个来验证这一点,cat -vet其中真实选项卡显示为^I

要更改cut命令以使用空格作为分隔符,您需要添加 arg -d' ',但由于您已经在单引号和 awk 脚本内,因此您需要将其更改sprintf(...)

sprintf("<(cut -d\" \"  -f4 %s)",$0)

答案2

对于不太大的文件:

while read -r f_part
do
    awk '
        BEGIN{
            SUBSEP=" "
        }
        NR==1{
            for(i=2;i<ARGC;i++)
                $(NF+1)=$NF
            print
        }
        FNR==1{
             next
        }
        {
             RES[$1,$2,$3]=RES[$1,$2,$3] $4 " "
        }
        END{
             for(i in RES)
                 print i, RES[i]
        }' *_${f_part}_* > big_table_${f_part}
done < <(printf '%s\n' *_*_*_*txt | cut -d_ -f3 | sort -u)

或者,如果您确定文件中的顺序正确:

while read -r f_part
do 
    set -- *_${f_part}_*
    sed -i 's/\s+/:/3;s/\s\+/\t/g;s/\s*$//' "$@"
    while [ $# -gt 1 ]
    do
        join -t: $1 $2 > tmp
        mv tmp big_table_${f_part}
        shift 2
        set -- big_table_${f_part} "$@"
    done
    sed 's/:/\t/g' big_table_${f_part}
done < <(printf '%s\n' *_*_*_*txt | cut -d_ -f3 | sort -u)

答案3

for f in rcp8p5 rcp4p5
do  : >"$f.txt"
    find . ! -name . -prune ! -type d -name "*_${f}_*txt" -exec \
        sh -c '
            printf "%s\t" YEAR MONTH DAY
            printf "%.0sRES\t" "$@"; echo
            sed -n "
                /^[0-9]/!d;p;:n
                n
                /^[0-9]/s/.*[[:blank:]]//p
                bn
            "  "$@" | paste
    ' --    {} + >>"$f.txt"
done

...我想我可能对你之前所做的事情有错误的认识,所以也许这可以弥补它。我不太确定这会起作用,但是,如果有效的话,它应该是方式比你正在做的事情更快。

基本上获取名称与或find匹配的文件列表,并将它们交给shell。...8......4...{} +

shell 打印一个标题行,以YEAR MONTH DAY每个开头,后跟一个\tab,然后打印RES与参数一样多的列。

然后sed将所有文件参数连接到一个流中,并完整打印以数字开头的第一行,并且后面的所有以数字开头的行仅打印最后一个字段。

sed的所有输出都传递给 ,它将输入中的paste所有 ewlines 替换为输出上的 abs 。\n\t

希望这个版本将要工作,因为调用一个新的子 shell 并为列表中的每个文件打开一个新管道几乎是一个糟糕的主意。

如果是的话,这将要但是,请在表格中为每组添加一个新行ARGMAX文件 - 这可能不是一件坏事,但之后很容易处理。

答案4

你也可以做类似的事情

arr=( *_rcp8p5_*.txt )
paste "${arr[@]}" | cut -f-4,$(seq -s, 8 4 $((4*${#arr[@]}))) >out_rcp8p5.txt

这将提取paste所有*_rcp8p5_*.txt文件的字段 1-4 以及之后的每四个字段。

相关内容