【问题标题】:awk extract columns from file based on header from second file 3awk 根据第二个文件的标题从文件中提取列 3
【发布时间】:2017-09-20 07:34:52
【问题描述】:

我正在尝试从有关此主题的先前线程中运行一些修改过的代码。 我有一个文件 data.txt,其中第一行是标题。我想创建一个新文件,其中仅包含与第二个文件(list.txt)中的条目匹配的那些列。

数据.txt

1,2,3,4,5,6,7,8,9,10
1.000000,0,0,0,0,0,0,0,0,0
0,1.000000,0.031250,0,0,0,0.031250,0,0,0
0,0.031250,1.000000,0,0,0,0.062500,0,0,0
0,0,0,1.000000,0,0,0,0,0,0
0,0,0,0,1.000000,0,0,0,0,0
0,0,0,0,0,1.000000,0,0.062500,0,0
0,0.031250,0.062500,0,0,0,1.000000,0,0,0
0,0,0,0,0,0.062500,0,1.000000,0,0
0,0,0,0,0,0,0,0,1.000000,0

list.txt

3
5
7
9

想要的输出是

3,5,7,9
0,0,0,0
0.031250,0,0.031250,0
1.000000,0,0.062500,0
0,0,0,0
0,1.000000,0,0
0,0,0,0
0,1.000000,0
0,0,0,0
0,0,0,1.000000

我已经使用了下面的代码

echo "${DATAFILE:-data.txt}"
echo "${COLUMNFILE:-list.txt}"

awk {
     j=1
     while ((getline < COLUMNFILE) > 0) {
        col[j++] = $1
     }
     n=j-1;
     close(COLUMNFILE)
     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

我得到下面的结果,它复制了 data.txt 中的行而不做任何选择。 list.txt 中的条目打印在文件末尾

1,2,3,4,5,6,7,8,9,10
1,2,3,4,5,6,7,8,9,10
1.000000,0,0,0,0,0,0,0,0,0
1.000000,0,0,0,0,0,0,0,0,0

0,1.000000,0.031250,0,0,0,0.031250,0,0,0
0,1.000000,0.031250,0,0,0,0.031250,0,0,0

0,0.031250,1.000000,0,0,0,0.062500,0,0,0
0,0.031250,1.000000,0,0,0,0.062500,0,0,0

0,0,0,1.000000,0,0,0,0,0,0
0,0,0,1.000000,0,0,0,0,0,0

0,0,0,0,1.000000,0,0,0,0,0
0,0,0,0,1.000000,0,0,0,0,0

0,0,0,0,0,1.000000,0,0.062500,0,0
0,0,0,0,0,1.000000,0,0.062500,0,0

0,0.031250,0.062500,0,0,0,1.000000,0,0,0
0,0.031250,0.062500,0,0,0,1.000000,0,0,0

0,0,0,0,0,0.062500,0,1.000000,0,0
0,0,0,0,0,0.062500,0,1.000000,0,0

0,0,0,0,0,0,0,0,1.000000,0
0,0,0,0,0,0,0,0,1.000000,0

3
3

5
5

7
7

9
9

非常感谢任何帮助。

【问题讨论】:

  • awk 无法“看到”外部变量、数据/列文件。你需要把它们传进去,即。 awk -v COLF="$COLF" '{ awk program}' "$DATAFILE" 。我没有仔细查看您的其余代码,但您所拥有的似乎是一个很好的尝试。祝你好运。
  • 使用getline 很少是正确的方法,在这种情况下肯定不是。请参阅 awk.freeshell.org/AllAboutGetline 了解何时应考虑使用 getline 以及所有注意事项。

标签: bash awk


【解决方案1】:
$ awk '
    BEGIN { FS=OFS="," }
    NR==FNR { f[++nf]=$0; next }
    { for (i=1; i<=nf; i++) printf "%s%s", $(f[i]), (i<nf?OFS:ORS) }
' list.txt data.txt
3,5,7,9
0,0,0,0
0.031250,0,0.031250,0
1.000000,0,0.062500,0
0,0,0,0
0,1.000000,0,0
0,0,0,0
0.062500,0,1.000000,0
0,0,0,0
0,0,0,1.000000

【讨论】:

    【解决方案2】:

    您可以将您的位置文件和数据文件都传递到 awk 并在内部执行您的逻辑:

     awk -F"," 'FILENAME=="list.txt"{a[NR]=$1}FILENAME=="data.txt"{for(i=1; i<=length(a); i++){printf (i==length(a)?"%s\n":"%s,"),$a[i]}}' list.txt data.txt
    

    我们在这里:

    1. 使用逗号分隔符分割传入文件 (-F",")
    2. 如果 FILENAME awk 变量是“list.txt” (FILENAME=="list.txt")
    3. ++然后将该行中的值添加到以行号为索引的数组中(a[NR]=$1
    4. 如果 FILENAME awk 变量是“data.txt”(FILENAME=="data.txt")
    5. ++然后循环遍历数组for(i=1; i&lt;=length(a); i++)中的每个元素
    6. ++++并打印出该位置的项目值($a[i])。如果该位置是找到的最后一个位置 (i==length(a)),则使用换行符 ("%s\n") 将其打印出来,否则使用逗号 ($a[i]) 将其打印出来。

    另一种选择是通过 -v(变量)标志传递您的职位,但这并不能很好地考虑可变数量的职位:

    awk -F"," -v f1=$(awk 'NR==1' list.txt) -v f2=$(awk 'NR==2' list.txt) -v f3=$(awk 'NR==3' list.txt) -v f4=$(awk 'NR==4' list.txt) '{print $f1, $f2, $f3, $f4}' data.txt
    

    【讨论】:

      【解决方案3】:

      awk解决方案:

      awk -F, 'function pr(a){ r=""; for(i=1;i<=NF;i++) if(i in a) r=(r!="")? r","$i:$i; print r }
               NR==FNR{ a[$0]; next }{ pr(a) }' list.txt data.txt
      

      输出:

      3,5,7,9
      0,0,0,0
      0.031250,0,0.031250,0
      1.000000,0,0.062500,0
      0,0,0,0
      0,1.000000,0,0
      0,0,0,0
      0.062500,0,1.000000,0
      0,0,0,0
      0,0,0,1.000000
      

      【讨论】:

        【解决方案4】:

        比较和对比的非awk 解决方案...

        $ join -t, <(sort list) <(<file tr ',' '\n' | pr -10ts, | sort) | 
          sort -n | 
          tr ',' '\n' | 
          pr -4ts,
        
        
        3,5,7,9
        0,0,0,0
        0.031250,0,0.031250,0
        1.000000,0,0.062500,0
        0,0,0,0
        0,1.000000,0,0
        0,0,0,0
        0.062500,0,1.000000,0
        0,0,0,0
        0,0,0,1.000000
        

        您需要魔术数字104,它们是原始文件和提取文件的列号(这些也可以自动化)。将数字排序转换为字典顺序并返回所需的多种排序(join 需要)。

        算法本质是transpose-join-transpose

        【讨论】:

          【解决方案5】:

          这是一个将list.txt 处理为字段列表并使用该列表调用另一个awk 来处理data.txt 的awk:

          $ awk '
          BEGIN { FS=OFS="," }          # set the delimiters for the list file
          NR==FNR {                     # process the list file
              p=p (p==""?"":OFS) "$" $1 # make a field list ($3,$5,$7,$9)
              next
          }
          {                             # process the data or call the processor
              RS=""                     # for getline to return multilined output
              cmd="awk \047BEGIN{FS=OFS=\",\"}{print "p"}\047 " FILENAME   # build awk call
              cmd | getline res         # actual awk call and output to res
              print res                 # output res
              exit                      # exit after first record
          }
          ' list data
          3,5,7,9
          0,0,0,0
          0.031250,0,0.031250,0
          1.000000,0,0.062500,0
          0,0,0,0
          0,1.000000,0,0
          0,0,0,0
          0.062500,0,1.000000,0
          0,0,0,0
          0,0,0,1.000000
          

          【讨论】:

          • 有创意,但这更像是 awk 精神awk -f &lt;(awk ...list.txt) file.txt
          • 我使用了 awk 代码 awk -F"," 'FILENAME=="list.txt"{a[NR]=$1}FILENAME=="data.txt"{for(i=1 ; i
          • 这在具有 100,000 行和 5,000 的列表文件的 data.txt 上运行良好。感谢所有建议
          【解决方案6】:

          awk 解决方案,考虑到列名可以是任何东西,而不仅仅是列的索引。

          BEGIN { FS=OFS="," }
          NR==FNR { l[$0]++; next }                      # save headers from list
          FNR==1{ for (i=1; i<=NF; i++) 
                      if ($i in l){ max=i; c[i]++ }}         # save column index in c;
                                                             # max index in max 
          { for(j=1; j<=NF; j++)                             # loop over column indices
                if(j in c)                                   # if index in c
                    printf "%s%s", $j, (j==max ? ORS : OFS)  # print column
          }
          

          有输入:

          $ cat list.txt
          C
          E
          G
          I
          

          $ cat data.txt
          A,B,C,D,E,F,G,H,I,J
          1.000000,0,0,0,0,0,0,0,0,0
          0,1.000000,0.031250,0,0,0,0.031250,0,0,0
          0,0.031250,1.000000,0,0,0,0.062500,0,0,0
          0,0,0,1.000000,0,0,0,0,0,0
          0,0,0,0,1.000000,0,0,0,0,0
          0,0,0,0,0,1.000000,0,0.062500,0,0
          0,0.031250,0.062500,0,0,0,1.000000,0,0,0
          0,0,0,0,0,0.062500,0,1.000000,0,0
          0,0,0,0,0,0,0,0,1.000000,0
          

          给出结果:

          $ awk 'BEGIN {FS=OFS=","} NR==FNR{l[$0]++;next} FNR==1{ for (i=1; i<=NF; i++) if ($i in l){max=i; c[i]++}}{for (j=1;j<=NF;j++) if(j in c) printf "%s%s",$j,(j==max ?ORS:OFS) }' list.txt data.txt
          C,E,G,I
          0,0,0,0
          0.031250,0,0.031250,0
          1.000000,0,0.062500,0
          0,0,0,0
          0,1.000000,0,0
          0,0,0,0
          0.062500,0,1.000000,0
          0,0,0,0
          0,0,0,1.000000
          

          【讨论】:

          • for (j in c) 将以随机(通常是哈希)顺序输出列,不一定是它们在任一输入文件中出现的顺序。这可能是可取的,也可能不是可取的……
          • 这也适用于标题。
          • ...可以通过{ for (j=1; j&lt;=NF; j++) if(j in c) printf "%s%s", $j, (j==max ? ORS : OFS) }预防
          猜你喜欢
          • 1970-01-01
          • 2018-09-15
          • 1970-01-01
          • 2018-08-09
          • 1970-01-01
          • 2012-06-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多