【问题标题】:Making every other row into a new column使每隔一行变成一个新列
【发布时间】:2018-01-01 02:56:36
【问题描述】:

所以,我有一个如下所示的输出:

samples pops    condition 1     condition 2 condition 3

A10051  15  1   3   4   
A10051  15  2   4   4   
A10052  15  2   1   4   
A10052  15  2   1   4

但是,对于下一次分析,我需要输入看起来像这样

samples pops    condition 1     condition 1     condition 2     condition 2 condition 3 condition 3

A10051  15  1   2   3   4   4   4       
A10052  15  2   2   1   1   4   4   

因此,它不仅仅是让每一行都是一个新列,给定列中的每一行都将位于分配给相同条件的新列中,这样每个样本都有两列用于相同的条件,而不是同一样本的两行。例如,我放了 2 个样本和 3 个条件,但是 IRL 我有超过 100 个样本和超过 1000 个条件...... 有什么想法吗?我相信它可以用 awk 完成,但我就是想不通。

【问题讨论】:

  • 我们是否允许假设数据是完美的,总是有两条线的倍数,相邻的对总是有相同的样本和弹出值(或者,如果它们不同, 区别无所谓)?
  • 是的。数据很完美。
  • 好的;它包括标题行和标题后的空白行?列标签是否由制表符分隔?如果不是,则“条件 1”等中的空白是令人讨厌的。
  • 所有内容都是制表符分隔的,条件名称将使用 _ 而不是空格。很抱歉,将在问题中解决它

标签: awk


【解决方案1】:

3 个条件列

从表面上断言“数据是完美的”,而忽略多年的经验表明数据很少是完美的,那么:

awk 'NR == 1 { printf "%s  %s  %s  %s  %s  %s  %s  %s\n",
                      $1, $2, $3, $3, $4, $4, $5, $5; next }
     NR == 2 { next }
     NR % 2 == 1 { c[1] = $3; c[2] = $4; c[3] = $5 }
     NR % 2 == 0 { printf "%s  %d  %d  %d  %d  %d  %d  %d\n",
                          $1, $2, c[1], $3, c[2], $4, c[3], $5 }' "$@"

给定输入文件:

samples pops    condition_1     condition_2 condition_3

A10051  15  1   3   4
A10051  15  2   4   4
A10052  15  2   1   4
A10052  15  2   1   4

脚本产生输出:

samples  pops  condition_1  condition_1  condition_2  condition_2  condition_3  condition_3
A10051  15  1  2  3  4  4  4
A10052  15  2  2  1  1  4  4

这段代码更机械而非有趣。如果每行有 10 列,你会以不同的方式处理它。您可能会使用循环来保存和打印数据。如果您希望标题和数据之间有一个空行,您可以轻松添加一个(NR == 2 { print; next } 或在第一个printf 函数中使用\n\n 代替\n)。如果您愿意,您可以安排输出字段由制表符分隔(在此代码中它们由双空格分隔)。

代码不依赖于分隔数据字段的制表符;它只取决于字段中没有空格。

许多条件列

当条件列很多时,需要使用数组和循环来捕获和打印数据,像这样:

awk 'NR == 1 { printf "%s  %s", $1, $2
               for (i = 3; i <= NF; i++) printf "  %s  %s", $i, $i
               print ""
               next
             }
     NR == 2 { next }
     NR % 2 == 1 { for (i = 3; i <= NF; i++) c[i] = $i }
     NR % 2 == 0 { printf "%s  %d", $1, $2;
                   for (i = 3; i <= NF; i++) printf "  %d  %d", c[i], $i
                   print ""
                 }' "$@"

当在与以前相同的数据上运行时,它会产生与以前相同的输出,但循环将允许它在每个输入行读取 1000 个条件并在每个输出行生成 2000 个条件。唯一可能的问题是您的 Awk 版本是否首先处理如此长的输入行。如果需要,升级到 GNU Awk。

【讨论】:

  • 其实我有10多列。我刚刚编辑了这个问题。谢谢。
  • 100 个样本不是问题。 1000 个条件几乎是一个问题;如果你不走运,你可能会在你的 Awk 变体中遇到限制。如果你使用 GNU Awk,你可能会没事,也许我在诽谤系统。但要小心一点。我会更新以处理更多列。
【解决方案2】:

带有GNU datamash 的简单解决方案(无输出标题)(这是文本文件上“命令行statistical operations”的好工具):

$ grep -v ^$ file | datamash -W -g1 --header-in first 2 collapse 3-5 | tr ',' ' ' | column -t
A10051  15  1  2  3  4  4  4
A10052  15  2  2  1  1  4  4

首先,使用grep 跳过所有空白行,然后根据第一个字段(-g1)使用datamash 分组行,使用空格作为字段分隔符(-W),折叠多行字段 3、4 和 5 的组。折叠的值用逗号分隔,这就是为什么我们必须用 tr 来分隔它们。

对于不同数量的列,只需调整collapse 操作的范围(例如collapse 3-1000)。并且由于分组操作,已经支持每组任意数量的样本。

【讨论】:

  • 从来没有使用过datamash,所以我真的不能让它工作......“ datamash:第2行字段2中的无效数值:'Locus_1_35'”
  • 看起来您在字段 2 中有带有字母的 2 行标题。max 运算符仅适用于数字,我在这里使用它只是为了传播值(因为它应该是唯一的组)。
  • 尝试使用first 而不是max(请参阅我的编辑)。此外,您可以使用 tail -n+3 file 而不是 grep 来删除 2 行标题。在这种情况下,您不需要--header-in,即:tail -n+3 file | datamash -W -g1 first 2 collapse 3-5 | tr ',' ' ' | column -t
  • 顺便说一句,你会发现datamash 对于文本文件的简单统计分析非常有用。这一切都可以在 awkpython 中完成 - 但它通常是与 datamash 的单行...
【解决方案3】:

awk 来救援!

awk     '{k=$1 FS $2} 
   NR==1 {p0=$0; pk=k}
   pk==k {split(p0,a); for(i=3;i<=NF;i++) $i=a[i] FS $i; print}
   pk!=k {p0=$0; pk=$1 FS $2}' file

samples pops condition_1 condition_1 condition_2 condition_2 condition_3 condition_3
A10051 15 1 2 3 4 4 4
A10052 15 2 2 1 1 4 4

将适用于未指定数量的列和记录,只要它们都是格式正确(相同数量的列)和分组(相同的键按顺序排列)。

【讨论】:

  • 这很完美!命名条件很容易!
猜你喜欢
  • 1970-01-01
  • 2021-04-16
  • 2021-09-12
  • 1970-01-01
  • 1970-01-01
  • 2014-02-10
  • 2013-12-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多