【问题标题】:Extract columns from a file based on header selected from another file根据从另一个文件中选择的标题从文件中提取列
【发布时间】:2012-06-21 07:52:52
【问题描述】:

我想在 awk 中解决以下问题。我有一个大文本表,逗号分隔,由 100k 行和 5k 列组成。第一行是标题,第一列是记录 ID。然后我有第二个文本文件,其中包含第一个文件中标题的子集。我想提取第一个文件的所有列,其标题包含在第二个文件中给出的列表中。这是输入和所需输出的示例:

数据.TXT

   ID, head1, head2, head3, head4  
    1, 25.5, 1364.0, 22.5, 13.2  
    2, 10.1, 215.56, 1.15, 22.2  

LIST.TXT

head1  
head4  

期望的输出:

ID, head1, head4  
1, 25.5, 13.2  
2, 10.1, 22.2

任何人都可以给我一些关于如何在 awk 或通过 unix 脚本解决这个问题的建议?提前感谢您的帮助!

【问题讨论】:

  • 您想要 LIST.TXT 文件中的 ID 吗? (如果您可以包含它,它将简化解决方案)。祝你好运。 (如果这是真的,请编辑您的问题)。
  • @shellter 在我的情况下,解决方案比现在更简单:)
  • 不,LIST.TXT 没有任何 ID 字段,按原样提供。输出中的 ID 是 DATA.TXT 中的 ID。 @Lev 您的解决方案确实(显然)很简单。一旦我明白为什么每个符号都在它所在的位置! :)

标签: unix awk


【解决方案1】:

我有一个想法,但由于我没有 shell 编程经验(也不知道 awk),这看起来像是以一种荒谬的方式重新发明了一些轮子:

$ cat DATA.TXT 
ID, head1, head2, head3, head4
1, 25.5, 1364.0, 22.5, 13.2
2, 10.1, 215.56, 1.15, 22.2

$ cat LIST.TXT 
head1
head4

$ cols=($(sed '1!d;s/, /\n/g' DATA.TXT | grep -nf LIST.TXT | sed 's/:.*$//'))

$ cut -d ',' -f 1$(printf ",%s" "${cols[@]}") DATA.TXT 
ID, head1, head4
1, 25.5, 13.2
2, 10.1, 22.2

附:我从thisthis 的答案中使用了一些关于bash 数组的非常基本的想法。

【讨论】:

  • 这完美!谢谢。而且我也有一些材料可以思考并理解你做了什么!
  • @lud 很高兴为您提供帮助。随意询问您是否无法理解我在那里所做的事情。 (提示:拆开看看会发生什么,以sed '1!d;s/, /\n/g' DATA.TXT等开头)
  • 此脚本唯一匹配最大前三个数字,例如,如果 Data.txt 文件中的标题是这样的: ID,10020,10022,10023,10024 list.txt 10020 10022 10023 所以而不是打印直到10023,它也会打印10024,请检查这个问题
  • 当 LIST.TXT 中的列名包含数字和特殊字符(例如 34-0.0)时,这似乎不起作用。它可以适应这些吗?
  • @Powege 我怀疑您需要在 grep 命令中的 -nf 之前添加一个 -F 来声明字符串是固定的,而不是解释为正则表达式(“。”有一个特殊的含义,匹配正则表达式中的所有字符)
【解决方案2】:

有一个有用的awk 脚本here 可用于从 csv 文件中提取特定的列名。

我稍微修改了它,以便它可以从另一个文件中读取列名。将下面的脚本另存为dataExtractor.sh

#!/bin/bash

DATAFILE=${1:-data.txt}
COLUMNFILE=${2:-list.txt}

awk -F, -v colsFile="$COLUMNFILE" '
   BEGIN {
     j=1
     while ((getline < colsFile) > 0) {
        col[j++] = $1
     }
     n=j-1;
     close(colsFile)
     for (i=1; i<=n; i++) s[col[i]]=i
   }
   NR==1 {
     for (f=1; f<=NF; f++)
       if ($f in s) c[s[$f]]=f
     next
   }
   { sep=""
     for (f=1; f<=n; f++) {
       printf("%c%s",sep,$c[f])
       sep=FS
     }
     print ""
   }
' "$DATAFILE"

运行它:

$ cat data.txt
ID,head1,head2,head3,head4
1,25.5,1364.0,22.5,13.2
2,10.1,215.56,1.15,22.2

$ cat list.txt
ID
head1
head4

$ dataExtractor.sh data.txt list.txt
1,25.5,13.2
2,10.1,22.2

【讨论】:

  • 感谢您的回答。我使用了下面更紧凑的那个,但现在我知道我在 awk 尝试中哪里出错了!很有用!!
  • 如果我们也想在输出中看到 ID、head1、head4 怎么样? ID、head1、head4 似乎在输出部分中消失了。
猜你喜欢
  • 2018-09-15
  • 1970-01-01
  • 1970-01-01
  • 2017-11-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-28
  • 2020-03-02
相关资源
最近更新 更多