0

我想遍历许多制表符分隔的文件并以下列方式更改单个列:

原来的

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

4 回答 4

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 列中的原始值更改时递增。

于 2013-11-13T12:27:41.877 回答
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
于 2013-11-13T12:40:18.670 回答
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";
}
于 2013-11-13T15:56:02.533 回答
-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";
}
于 2013-11-13T13:57:47.947 回答