Linux中大型TSV的条件编辑

Conditional Editing of Large TSV in Linux

提问人:JVGen 提问时间:8/25/2023 更新时间:8/26/2023 访问量:53

问:

我有许多大型 TSV 文件,它们有 6 个未命名的列和大约 1000 万行。我需要逐行应用更改并另存为新的 TSV。我在 Linux 上工作,可以访问 awk、gawk、grep。理想情况下,代码将在其运算符中引用列号,因此无论行内容如何,它都能正常工作。泰亚!

唯一需要做的更改是第 2 列和第 3 列,但更改取决于第 6 列中的值:

If the value of column 6 is "+", then:  
New Column 2: [Column 2 Value]  
New Column 3: [Column 2 Value] + 1  
  
If the value of column 6 is "-", then:  
New Column 2: [Column 3 Value] - 1  
New Column 3: [Column 3 Value]  

示例 TSV 数据结构(实际数据中不存在 | )

| AI     | 828     | 878     | ABC4807:78485:5:79215       | 42 | - |
| AI     | 971     | 1021    | ABC248:78485:5:79215:46065  | 42 | + |
| AI     | 1104    | 1153    | X7481:78485:5:79215:40174   | 35 | - |
| XVDIII | 56939   | 56988   | 9478:78485:5:79215:30872    | 42 | - |
| XVDIII | 56971   | 57020   | 7841S:78485:5:79215:34301   | 42 | - |
| UTXV   | 1043196 | 1043246 | T885189:78485:5:79215:10036 | 42 | + |
| UTXV   | 1043198 | 1043248 | C74581:78485:5:79215:10792  | 42 | - |
| UTXV   | 1043201 | 1043250 | T75S17:78485:5:79215:30204  | 42 | - |
| UTXV   | 1043201 | 1043251 | B784W7:78485:5:79215:42548  | 42 | - |

所需的 TSV 输出

| AI     | 877     | 878     | ABC4807:78485:5:79215       | 42 | - |
| AI     | 971     | 972     | ABC248:78485:5:79215:46065  | 42 | + |
| AI     | 1152    | 1153    | X7481:78485:5:79215:40174   | 35 | - |
| XVDIII | 56987   | 56988   | 9478:78485:5:79215:30872    | 42 | - |
| XVDIII | 57019   | 57020   | 7841S:78485:5:79215:34301   | 42 | - |
| UTXV   | 1043196 | 1043197 | T885189:78485:5:79215:10036 | 42 | + |
| UTXV   | 1043247 | 1043248 | C74581:78485:5:79215:10792  | 42 | - |
| UTXV   | 1043249 | 1043250 | T75S17:78485:5:79215:30204  | 42 | - |
| UTXV   | 1043250 | 1043251 | B784W7:78485:5:79215:42548  | 42 | - |
Linux AWK 终端 grep

评论

0赞 Fravadona 8/25/2023
这应该相当简单;你不想自己试一试吗?awk
0赞 JVGen 8/25/2023
这里的生物学家,对 Linux 编码的了解很少。我不知道如何引用未命名的列或如何设置条件语句。有时我会发布一些我认为对其他人来说很容易的东西,否则我会花费数小时/数天。
3赞 Fravadona 8/25/2023
下面是一个框架代码:awk 'BEGIN {FS = OFS = "\t"} {if ($6 == "+") {$3 = ...} else {...}; print}' file.tsv
2赞 dodrg 8/26/2023
我很欣赏@Fravadona努力支持提问者尝试自己写剧本。但除了基本算法之外,细节会让 AWK 的绝对新手不知所措。所以我敢于通过我的答案来缩短游戏;-)。
1赞 Ed Morton 8/26/2023
@dodrg 我认为 OP 发布的例子导致您误解了他们的需求。他们的文件只是 TSV,他们应该可以毫不费力地填写 Fravadonas 骨架。

答:

2赞 dodrg 8/26/2023 #1

替换值的算法不是问题。 有趣的一点是保持固定宽度的列。

文件:dataconvert.awk

#!/usr/bin/gawk -f

BEGIN {
    OFS = FS = "|"
}
{
    if ($7 ~ /+/) {
        len = length($4)
        $4 = $3
        gsub(/\s+/, "", $4)
        $4 = substr((" " ($4 + 1) "                       "), 0, len)
    } else {
        if ($7 ~ /-/) {
            len = length($3)
            $3 = $4
            gsub(/\s+/, "", $3)
            $3 = substr((" " ($3 - 1) "                       "), 0, len)
        }
    } 
    print
}

将标记为可执行文件,您可以直接启动它 调用chmod +x dataconvert.awk./dataconvert.awk

在此解决方案中,存储列内容的宽度,结果填充节省的空格量,最后裁剪为存储的长度。

如果数据集在提供值时或在第 6 列中确实没有例外,则可以省略第二个子句。在大型数据集中,这可以节省一些时间。+-if

由于数据集以字段分隔符开头,因此第一列始终为空但存在。因此,列计数必须以直观的方式进行计数。+1

评论

2赞 Ed Morton 8/26/2023
关于“有趣的一点是保持固定宽度的列”——是的,但 OP 没有固定宽度的列,它们有制表符分隔值 (TSV)。他们提供的示例输入/输出包括 s 并且似乎是固定宽度字段,即使他们说“否 |存在于实际数据中”。|
0赞 JVGen 8/26/2023
Ed,我以为这会造成混乱,但我找不到一种方法将我的数据复制粘贴为代码,同时保持列间距。我不得不使用一个 Markdown 表格转换器并从那里复制粘贴,但它包含“|”。
0赞 dodrg 8/26/2023
啊哈,好吧,如果你用“''”(三个重音符号)将代码括起来作为 markdown 代码赋值,则在代码中打标签是有效的。代码会更容易。但像这样,你真的有你的练习来转换它以满足你的需求。有点像脚本小子一样开始是进入编码的好点。——希望各方都满意。
1赞 JVGen 8/26/2023 #2

我调整了 dodrg 的响应以处理我的数据结构。我已经接受了他们的回答,但想为将来可能遇到这个问题的任何人发布这个回复。

awk ' BEGIN { FS = OFS = "\t" }
{
  if ($6 == "+") {
    $2 = $2
    $3 = $2 + 1
  } else {
    if ($6 == "-") {
      $2 = $3 - 1
      $3 = $3
    }
  } 
  print
}'  filein.tsv > fileout.tsv

评论

0赞 Ed Morton 8/26/2023
1)和没有做任何有用的事情,只是删除这些语句。2)可以只是.3)除非可以有一些你没有告诉我们的价值,否则既不是也不是,可以只是。$2 = $2$3 = $3else { if ($6 == "-") { $2 = $3 - 1} }else if ($6 == "-") { $2 = $3 - 1 }$6+-else if ($6 == "-") { $2 = $3 - 1 }else { $2 = $3 - 1 }
2赞 Ed Morton 8/26/2023
awk -v OFS='\t' '{if ($6=="+") $3=$2+1; else $2=$3-1} 1' file就是你真正需要的。