如何使用Perl,Python或bash更改列中的值的运行

时间:2013-11-13 12:22:53

标签: python perl bash

我想迭代许多制表符分隔的文件,并按以下方式更改单个列:

ORIGINAL

Col1    Col2    ....
afawer  1
asdgf   1
aser    1
qwerq   10
a3awer  10
1sdgf   11
a55er   11
2wu9    12
asxwer  12
a2dgf   13
a1er    13
qperq   13
...

期望的更换

Col1    Col2    ....
afawer  1
asdgf   1
aser    1
qwerq   2
a3awer  2
1sdgf   3
a55er   3
2wu9    4
asxwer  4
a2dgf   5
a1er    5
qperq   5
...

请注意,Col2中的运行长度对于每个文件都会有很大差异,因此长度不能进行硬编码。基本上,我想要替换的模式是:

  

aaabbbbbccccdddd

其中a,b,c和d可以是任何整数。所需的替代品是:

  

1112222233334444

等等(即整数的自然排序)。

我想用Python或Perl来做这件事,或者如果可能的话只使用像sed这样的bash命令。

4 个答案:

答案 0 :(得分:1)

使用Python,使用itertools.groupby()对第二列上的行进行分组,并使用enumerate()提供的计数器:

import csv
from itertools import groupby
from operator import itemgetter

with open(inputfile, 'rb') as ifh, open(outputfile, 'wb') as ofh:
    reader = csv.reader(ifh, delimiter='\t')
    writer = csv.writer(ofh, delimiter='\t')
    writer.writerow(next(reader))  # copy across header

    for counter, (key, group) in enumerate(groupby(reader, itemgetter(1)), 1):
        for row in group:
            row[1] = counter
            writer.writerow(row)

这会写入一个包含相同数据的新CSV文件,但第二列被计数器(从1开始)替换,每当第2列中的原始值发生变化时,该计数器就会递增。

答案 1 :(得分:1)

使用awk你可以说:

awk 'NR>1{if ($2 == prev) { $2 = a; } else { prev=$2; a=a+1; $2=a; }}1' OFS='\t' filename

对于您的输入,它会产生以下输出:

Col1    Col2
afawer  1
asdgf   1
aser    1
qwerq   2
a3awer  2
1sdgf   3
a55er   3
2wu9    4
asxwer  4
a2dgf   5
a1er    5
qperq   5

答案 2 :(得分:0)

这是一个非常简单的Perl解决方案。它只是跟踪第二列的先前值,如果它发生变化,则会突然显示$n的值。

程序期望输入文件的路径作为命令行上的参数,并将修改后的数据发送到STDOUT

use strict;
use warnings;

print scalar <>; # Copy header line

my ($prev, $n) = (0, 0);
while (<>) {
  chomp;
  my @fields = split /\t/;
  ($prev, $fields[1]) = ($fields[1], $fields[1] == $prev ? $n : ++$n);
  print join("\t", @fields), "\n";
}

答案 3 :(得分:-1)

要完成您要求的三种语言:这是Perl中的一个版本。 如果Col2值稍后重复,也可以使用。 编辑:我使用空格作为分隔符,替换为选项卡。

use strict;
use warnings;

open (INFILE, "<", "infile.csv");
my @lines = <INFILE>;
close INFILE;

open (OUTFILE, ">", "outfile.csv");
my %hash = ("Col2", "Col2"); #  To handle the Header
my $counter=0;

foreach my $currentLine (@lines)
{
    chomp $currentLine;
    my @fields = split (/\W+/, $currentLine);
    if (!exists $hash{$fields[1]})  # Have I not seen the Col2 value before?
    {
        $counter++;                 # If not, determine what to replace the value with. 
        $hash{$fields[1]} = $counter;
    }
    $fields[1] = $hash{$fields[1]}; # Replace with known replacement.

    print OUTFILE $fields[0],"\t",$fields[1],"\n";
}