我想迭代许多制表符分隔的文件,并按以下方式更改单个列:
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命令。
答案 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";
}