提问人:JVGen 提问时间:8/25/2023 更新时间:8/26/2023 访问量:53
Linux中大型TSV的条件编辑
Conditional Editing of Large TSV in Linux
问:
我有许多大型 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 | - |
答:
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 = $3
else { 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
就是你真正需要的。
评论
awk
awk 'BEGIN {FS = OFS = "\t"} {if ($6 == "+") {$3 = ...} else {...}; print}' file.tsv