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

时间:2015-03-31 20:39:38

标签: linux replace awk sed

我有一个庞大的数据文件,我希望只在以下格式文件中交换一些第二列的数字。该文件有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条重复的标题行,数据集末尾有1条尾随行。那些标题和尾随行都是从#开始的。结果,数据将具有7个标题行,8768个数据行和1个尾随行,每个数据块总共8776行。那一条尾随行只包含sinlge'#'。

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

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

的第二列,然后,

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

的第二栏。我希望对所有重复数据集进行替换。

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

谢谢

最好,

1 个答案:

答案 0 :(得分:1)

这可能适合您,但您必须使用GNU awk,因为它使用gensub命令和$0重新分配。

将以下内容放入可执行的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]+ )/ { $0 = gensub( /^( [0-9]+)( [0-9]+)/, "\\1 " swap($2), 1 ) }

47 # print the line

以下是细分:

  • BEGIN - 设置一个数组a,其中包含新值的映射。
  • 创建用户定义的函数swap,以便为a数组中的第二列或值本身提供值。传入c2元素,而val元素是局部变量(因为没有传入第二个参数)。
  • 当一行以空格开头后跟一个数字和一个空格(模式)时,然后使用gensub替换第一个数字模式的第一个出现,其自身与空格连接并从{返回{1}}(行动)。在这种情况下,我使用gensub的替换文本来保留第一列数据。使用swap的字段数据标识符将第二列传递给swap。使用$2应保留数据行的格式。
  • gensub - 计算结果为true的表达式提供打印47的默认操作,该操作可能已修改数据行。任何不是&#t;#34;数据"将在没有修改的情况下打印出来。

提供的数据并未显示所有情况,因此我编写了自己的测试文件:

$0

运行可执行文件awk(如# 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 )会给出以下输出:

./script.awk data

对我而言看起来不错,但我不是拥有2500万个数据集的人。

您还绝对希望首先在较小的数据样本(前几个数据集?)上尝试此操作,并将stdout重定向到临时文件,可能是这样的:

# 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

您可以在此处详细了解此脚本中使用的元素:

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