如何使用 linux 命令交换重复格式数据块内的数字?

How can I swap numbers inside data block of repeating format using linux commands?

我有一个巨大的数据文件,我希望只交换一些第2列的数字,在下面的格式文件中。该文件有 25,000,000 个数据集,每个数据集有 8768 行。

%% 已编辑:更短的 10 行示例。带来不便敬请谅解。这是典型的一个数据块。

# Dataset 1  
# 
# Number of lines 10 
# 
# header lines
 5 11 3 10 120 90 0         0.952         0.881         0.898         2.744         0.034         0.030
 10 12 3 5 125 112 0         0.952         0.897         0.905         2.775         0.026         0.030
 50 10 3 48 129 120 0         1.061         0.977         0.965         3.063         0.001         0.026
 120 2 4 5 50 186 193 0         0.881         0.965         0.899         0.917         3.669         0.000        -0.005
 125 3 4 10 43 186 183 0         0.897         0.945         0.910         0.883         3.641         0.000         0.003
 186 5 4 120 125 249 280 0         0.899         0.910         0.931         0.961         3.727         0.000        -0.001
 193 6 4 120 275 118 268 0         0.917         0.895         0.897         0.937         3.799         0.000         0.023
 201 8 4 278 129 131 280 0         0.921         0.837         0.870         0.934         3.572         0.000         0.008
 249 9 4 186 355 179 317 0         0.931         0.844         0.907         0.928         3.615         0.000         0.008
 280 10 4 186 201 340 359 0         0.961         0.934         0.904         0.898         3.700         0.000         0.033
#
# Dataset 1  
# 
# Number of lines 10 
...

如您所见,数据集的头部有 7 条重复的 header 行,数据集末尾有 1 条尾随行。那些 header 和结尾的行都是从 # 开始的。因此,数据将有 7 header 行,8768 条数据行和 1 条尾随行,每个数据块总共有 8776 行。那一行尾部只包含一个“#”。

我只想交换第 2 列中的一些数字。首先,我想替换

1, 9, 10, 11 => 666
2, 6, 7, 8 => 333
3, 4, 5 => 222 

第 2 列,然后

666 => 6
333 => 3
222 => 2

第 2 列。我希望对所有重复数据集进行此替换。

我用python试过这个,但是数据太大,所以会出现内存错误。如何使用 sed 或 awk 或 cat 命令等 linux 命令执行此交换?

谢谢

最佳,

这可能对你有用,但你必须使用 GNU awk,因为它使用 gensub 命令和 [=15=] 重新分配。

将以下内容放入可执行的 awk 文件中(如 script.awk ):

#!/usr/bin/awk -f

BEGIN {
    a[1] = a[9] = a[10] = a[11] = 6
    a[2] = a[6] = a[7]  = a[8]  = 3
    a[3] = a[4] = a[5]          = 2
}

function swap( c2,            val ) {
    val = a[c2]
    return( val=="" ? c2 : val )
}

/^( [0-9]+ )/ { [=10=] = gensub( /^( [0-9]+)( [0-9]+)/, "\1 " swap(), 1 ) }

47 # print the line

细目如下:

  • BEGIN - 使用新值的映射设置数组 a
  • 创建一个用户定义的函数 swap 来为 a 数组的第二列或值本身提供值。 c2 元素被传入,而 val 元素是局部变量(因为没有传入第二个参数)。
  • 当一行以 space 开头,后跟数字和 space(模式)时,然后使用 gensub 将第一个数字模式的第一次出现替换为本身与 space 和 swap(操作)中的 return 连接。在这种情况下,我使用 gensub 的替换文本来保留第一列数据。第二列使用 </code> 的字段数据标识符传递给 <code>swap。使用 gensub 应保留数据行的格式。
  • 47 - 计算结果为 true 的表达式提供打印 [=15=] 的默认操作,对于数据行可能已被修改。任何不是 "data" 的行都将在此处打印出来 w/o 修改。

提供的数据并没有显示所有情况,所以我自己制作了测试文件:

# 2 skip me
9 2 not going to process me
 1 1 don't              change the for  matting
 2 2    4       23242.223       data
 3 3 data       that's  formatted
 4 4 7  that's  formatted
 5 5 data       that's  formatted
 6 6 data       that's  formatted
 7 7 data       that's  formatted
 8 8 data       that's  formatted
 9 9 data       that's  formatted
 10 10 data     that's  formatted
 11 11 data     that's  formatted
 12 12 data     that's  formatted
 13 13 data     that's  formatted
 14 s data      that's  formatted
# some other data

运行 可执行文件 awk(如 ./script.awk data)给出以下输出:

# 2 skip me
9 2 not going to process me
 1 6 don't              change the for  matting
 2 3    4       23242.223       data
 3 2 data       that's  formatted
 4 2 7  that's  formatted
 5 2 data       that's  formatted
 6 3 data       that's  formatted
 7 3 data       that's  formatted
 8 3 data       that's  formatted
 9 6 data       that's  formatted
 10 6 data      that's  formatted
 11 6 data      that's  formatted
 12 12 data     that's  formatted
 13 13 data     that's  formatted
 14 s data      that's  formatted
# some other data

我觉得还不错,但我不是那个拥有 2500 万个数据集的人。

您肯定也想首先在较小的数据样本(前几个数据集?)上尝试此操作,然后将 stdout 重定向到临时文件,例如:

head -n 26328 data | ./script.awk - > tempfile

您可以在此处了解有关此脚本中使用的元素的更多信息:

当然,您应该花一些时间在 Stack Overflow 上查看与 awk 相关的问题和答案;)